32 KiB
阶段4设计:钱包管理(账户列表 + 个人开户 + 企业开户 + 账户主页/交易/提现/保证金/回单对账单)
- 状态: 用户已授权自主推进,批量决策已确认,自查通过后直接进入 writing-plans
- 上级文档:
2026-07-08-fryl-frontend-overall-design.md(阶段4=钱包管理);阶段3设计文档2026-07-09-phase3-customer-management-design.md作为格式与既有约定参照 - 支撑材料:
凡荣e链前端业务需求说明书.md2.6.1–2.6.10(约1235–1814行)、swagger_project_scfs_2026-07-09_14-15-23.json(/api/wallet-management/personal-opening/*、/api/wallet-management/enterprise-open-application/*、/api/wallet-management/enterprise-bank-card/*、/api/wallet-account/*) - 已确认的跨阶段批量决策(与本阶段相关部分):企业开户采用"完整申请单流程"(
enterprise-open-application/create+list+detail+update,不用一步直提/api/enterprise-account-open/create);移动端确认页(personal-opening/mobile/*、enterprise-open-application/confirm)不属于本项目范围;渠道编号来源=表单内"所属渠道"选择框(复用阶段3ChannelSelect.vue)
1. 范围
包含(对应需求文档 2.6.1–2.6.10):
- 2.6.1 账户列表页(查询+开户入口+开分户/销户/变更/手机号变更/绑卡/查看/余额同步,单选)
- 2.6.2 个人账户开立(选择个人客户→录入补充信息→提交草稿→展示申请编号,轮询开户进度)
- 2.6.3 企业账户开立(基础信息+法人代表+实际控制人+受益人信息+影像资料,提交为"开户申请",支持列表查询/编辑/查看)
- 2.6.4 账户主页面(选中账户的多子账户余额卡片+交易明细列表)
- 2.6.5 交易明细查询(时间范围+分页)
- 2.6.6 主账户提现(金额+短信验证码)
- 2.6.7 回单下载(单笔/批量)
- 2.6.8 对账单下载(时间范围)
- 2.6.9 保证金缴纳
- 2.6.10 保证金释放
- 绑卡管理(个人
wallet-account/bind-card*、企业enterprise-bank-card/*,含发送验证码环节)
不包含(超出后端能力或明确排除,不问用户,列入第7节能力缺口):
- 移动端确认相关页面(
personal-opening/mobile/draft-detail、mobile/submit、enterprise-open-application/confirm)——批量决策3已定,客户手机端流程范围外,本阶段前端只做到"提交草稿/申请后展示申请编号+轮询状态" - 企业开户"负责人信息""经办人信息"(
leaderName/leaderIdType/leaderIdNo/leaderMobile/leaderOpto、operatorName/operatorIdType/operatorIdNo/operatorMobile/operatorOpto)——需求文档 2.6.3.4 字段表未列出这两组信息,但后端CreateEnterpriseAccountOpenBto/UpdateEnterpriseAccountOpenApplicationBto中存在对应字段;按"需求文档未要求即不做 UI 采集"原则不在表单展示,提交时不传(见第7节缺口1) - 企业开户受益人信息的完整反洗钱字段集(
EnterpriseAccountOpenApplicationBeneficiaryBto35个字段,如受益所有权形成/终止日期、25%以上收益权/表决权、实际控制形式、高级管理人员职位等)——需求文档 2.6.3.4 仅要求8个字段(客户名称/证件号/证件地址/证件到期日/性别/国籍/是否股东/持股比例),按需求文档裁剪,不采集剩余27个字段(见第7节缺口2) - 账户列表页"渠道""机构""冻结账户余额"三列——后端
WalletAccountVo/WalletAccountDetailVo均无对应字段(见第7节缺口3),不展示,不编造数据 - 账户列表查询条件"所属机构""渠道""客户类型"——
wallet-account/page请求体仅支持accountName/accountNo/accountType(账户类型A1-A7非客户类型个人/企业),与需求文档索引要素不匹配(见第7节缺口3),查询表单按后端实际能力裁剪 - 个人开户"绑定银行账号校验(卡信息查询接口223405)""职业三级码值菜单"——swagger 中未检索到对应查询接口,按纯文本输入实现,不做后端联动校验(见第7节缺口4、5)
- 保证金缴纳/释放的短信验证码步骤——
MarginOperationRequestEo请求体不含channelNo/verifyCode字段,与需求文档描述的"发送验证码→校验"流程不符,按接口实际字段实现(不采集验证码,仅金额+摘要),见第7节缺口6
2. 接口契约速查
2.1 账户列表与账户主页(/api/wallet-account/*)
| 接口 | Body | 说明 |
|---|---|---|
POST .../page |
{page,pageSize,accountName,accountNo,accountType} |
响应 {total,page,pageSize,records: WalletAccountVo[]};WalletAccountVo 字段:id,customerId,accountNo,accountType(A1/A2/A3/A6/A7),accountName,accountRelation('0'主/'1'从),customerNo,accountStatus(NORMAL/FROZEN/CLOSED/LOCKED),mainAccountNo,openDate |
POST .../detail |
{id} |
响应 WalletAccountDetailVo{mainAccountNo,mainBalance,a2AccountNo,a2Balance,a6AccountNo,a6Balance,a7AccountNo,a7Balance} |
POST .../close |
{id} |
销户,响应 data 无固定类型,按"成功即刷新列表"处理 |
POST .../open-sub-account |
{accountNo}(主账户账号) |
开分户(A7),同上处理响应 |
POST .../update |
{accountNo,mobile,bankCardNumber,bankNo,bankName}(仅 accountNo 必填) |
账户信息变更(与"手机号变更"分属两个不同接口,change-mobile 专用于变更手机号) |
POST .../change-mobile |
{accountNo,newMobile}(均必填) |
手机号变更 |
POST .../sync-balance |
{id} |
余额同步,响应 data 无固定类型,成功后前端重新调用一次 detail 刷新展示 |
POST .../transaction-detail |
{accountNo,channelNo,startDate,endDate,page(默认0),rows(默认10000)}(accountNo/channelNo/startDate/endDate 必填) |
响应 TransactionDetailVo{accountNo,accountName,curBalance,availBalance,withdrawBalance,preBalance,detailList: TradeDetailItemVo[]};TradeDetailItemVo{id,accountDate,tradeTime,detailType,transType,transAmount,balance,oppAccountNo,oppAccountName,remark} |
POST .../withdraw |
{channelNo, withdrawRequestEo: WithdrawRequestEo} |
WithdrawRequestEo{accountNo,accountName,primaryAccount(银行卡号),amount,verifyCode(前端调用发送验证码接口获取),transSummary,receiveUrl,transFee,feeAccountNo,feeAccountName},响应 WithdrawResultVo{success,recode,recodeInfo,accountNo,accountName,serialNo,transAmount} |
POST .../margin-pay |
MarginOperationRequestEo{accountNo,accountName,tradeAmount,remark}(无 channelNo/verifyCode,见缺口6) |
保证金缴纳,响应 MarginResultVo{success,recode,recodeInfo,accountNoBzj,accountNameBzj} |
POST .../margin-release |
同上 MarginOperationRequestEo |
保证金释放,响应同 MarginResultVo |
POST .../download-statement |
{accountNo,channelNo,startDate,endDate}(均必填) |
响应 StatementDownloadVo{fileData(base64 PDF)} |
POST .../download-receipt |
{accountNo,channelNo,originalSerialNo}(均必填,仅支持单笔,见缺口7) |
响应 ReceiptDownloadVo{fileData(base64 PDF)} |
POST .../bind-card-list |
{accountNo,channelNo}(均必填) |
响应 BindCardListVo{accountNo,accountName,idType,idNo,curBalance,availBalance,withdrawBalance,preBalance,detailList: BindCardDetailVo[]{mobile,primaryAccount}} |
POST .../bind-card |
BindCardRequestEo{accountNo,idNo,mobile,bankCardNumber,bankNo,bankName,setDefault,verifyCode,channelNo} |
响应 ActionResultVo{success,message} |
POST .../unbind-card |
UnbindCardRequestEo{accountNo,bankCardNumber,channelNo} |
响应 ActionResultVo;后端注明"最后一张绑卡不可删除" |
2.2 个人开户(/api/wallet-management/personal-opening/*)
| 接口 | Body | 说明 |
|---|---|---|
POST .../query-customer |
{organizationId,customerCode,customerName,certificateNumber,page,pageSize}(page/pageSize 必填) |
响应 records: CustomerListPageVo[](字段同 IndividualCustomerDetailVo:id,customerCode,customerName,certificateType,certificateNumber,mobilePhone,organizationId,gender,birthDate,ethnicity,certificateEffectiveDate,certificateExpiryDate,issuingAuthority,certificateAddress,occupation) |
POST .../create-draft |
必填 accountProperty,bankCardNumber,certificateAddress,certificateNumber,certificateType,channelNo,customerId,customerName,issuingAuthority,mobilePhone,occupation;选填 certificateEffectiveDate,certificateExpiryDate,gender,bankNo,bankName,accountClass,signNo,ethnic,jobNote,openLongitude,openLatitude,openIp,fileList: AccountOpenFileItemEo[]{fileNo,fileType,filePage} |
响应 CreateDraftResultVo{applicationNo} |
POST .../query-progress |
{applicationNo} |
响应 ApplicationProgressVo{applicationNo,applicationStatus(DRAFT/PENDING_ACCOUNT_OPEN/CONFIRMED/OPENED/APPLY_FAILED),failReason,mainAccountNo,customerNo,createdAt,updatedAt} |
accountProperty 固定传 "1"(个人);fileList 的 fileType 取值:01身份证正面、02身份证反面、04人脸照片(范围外,不采集)、13开户协议(范围外,不采集),本阶段仅上传 01/02。
2.3 企业开户申请(/api/wallet-management/enterprise-open-application/*、enterprise-bank-card/*)
| 接口 | Body | 说明 |
|---|---|---|
POST .../list |
FindEnterpriseOpenApplicationListQto{applicationNo,enterpriseNameLike,applicationStatus,startDate,endDate,page,pageSize} |
响应 records: EnterpriseAccountOpenApplicationListVo[]{applicationNo,enterpriseName,businessLicense,applicationStatus,channelNo,confirmTime,mainAccountNo,customerNo,failReason,createdAt,updatedAt} |
POST .../create |
CreateEnterpriseAccountOpenBto(见下) |
响应 CreateDraftResultVo{applicationNo} |
POST .../detail |
{applicationNo} |
响应 EnterpriseAccountOpenApplicationDetailVo(字段同 Create Bto,另加 frchainSerialNo,openSerialNo,fileList: EnterpriseAccountOpenApplicationFileVo[],beneficiaryList: EnterpriseAccountOpenApplicationBeneficiaryVo[]) |
POST .../update |
UpdateEnterpriseAccountOpenApplicationBto(同 Create Bto 字段,applicationNo→id) |
响应 data boolean。与阶段3"股东高管信息仅新增可写"不同,企业开户申请的编辑接口支持完整字段回写(含受益人列表),不存在该限制 |
POST enterprise-bank-card/list |
{accountNo,channelNo}(均必填) |
响应 BindCardListVo(结构同2.1个人绑卡列表) |
POST enterprise-bank-card/bind |
必填 accountNo,accountProperty,bankName,bankNo,channelNo,idNo,mobile,primaryAccount,setDefault,verifyCode |
企业账户绑卡 |
POST enterprise-bank-card/unbind |
必填 accountNo,channelNo,primaryAccount |
企业账户解绑 |
CreateEnterpriseAccountOpenBto 完整字段(本阶段实际采集/传递的子集见第4.3节,未采集字段不传):applicationNo(update时对应id),organizationId,enterpriseId,enterpriseName,businessLicense,certificateType(固定BUSINESS_LICENSE),certificateNumber,certificateEffectiveDate,certificateExpiryDate,certificateAddress,issuingAuthority,signNo,mobilePhone,businessScope,industry,orgcodes,ratcodes,bankCardNumber,bankNo,bankName,legalPersonName,legalPersonIdType,legalPersonIdNo,legalPersonOpto,controllerName,controllerIdType,controllerIdNo,controllerOpto,leaderName/leaderIdType/leaderIdNo/leaderMobile/leaderOpto(不采集),operatorName/operatorIdType/operatorIdNo/operatorMobile/operatorOpto(不采集),applicationStatus,channelNo,openLongitude,openDimensions,openIp,businessLicenseFileNo,creatorId,enterpriseAccountOpenApplicationFileBtoList: [{fileNo,fileType('12'营业执照/'13'开户协议/'01'法人身份证正面/'02'法人身份证反面),filePage}],enterpriseAccountOpenApplicationBeneficiaryBtoList: [{beneName,beneIdType,beneIdNo,beneOpto,beneSex('0'男/'1'女),beneNationality,actCtrl(是否股东,'是'/'否'),actHdRat(持股比例),...(27个反洗钱扩展字段不采集)}]。
删除接口:未检索到企业开户申请删除接口,列表页不提供删除入口。
2.4 枚举取值(均从 swagger 摘取)
AccountTypeEnum:A1主账户 /A2电商A2账户 /A3电商A3账户 /A6保证金账户 /A7分户账户AccountStatusEnum:NORMAL正常 /FROZEN冻结 /CLOSED销户 /LOCKED锁定OpenApplicationStatusEnum(个人开户进度):DRAFT待确认 /PENDING_ACCOUNT_OPEN待调用开户接口 /CONFIRMED已确认 /OPENED已开立 /APPLY_FAILED申请失败EnterpriseOpenStatusEnum(企业开户申请状态):DRAFT待确认 /CONFIRMED已确认 /SUBMITTED已开立 /APPLY_FAILED申请失败CertificateTypeEnum/GenderEnum:复用阶段3已确认取值(ID_CARD/PASSPORT/OTHER/BUSINESS_LICENSE、MALE/FEMALE/UNKNOWN)
3. 页面架构
src/views/wallet/
account/
AccountList.vue # 2.6.1 账户列表页(ProTable,单选,:row-selection="{type:'radio'}")
AccountDetail.vue # 2.6.4/2.6.5 账户主页(余额卡片+交易明细+提现/缴纳/释放弹窗入口)
personal-open/
PersonalOpenForm.vue # 2.6.2 个人开户表单(独立路由页)
enterprise-open/
EnterpriseOpenList.vue # 2.6.3 企业开户申请列表(ProTable,查询/新增/编辑/查看,无删除)
EnterpriseOpenForm.vue # 2.6.3 企业开户申请表单(独立路由页,create/edit/detail三态)
src/components/
WalletCustomerPicker.vue # 钱包开户专用客户选择弹窗(个人:query-customer;企业:复用阶段3 fetchEnterpriseCustomerListApi)
BindCardModal.vue # 绑卡管理弹窗(列表+新增绑定+解绑,个人/企业通过 props 区分调用的 API 组)
WithdrawModal.vue # 提现弹窗(金额+发送验证码+验证码输入)
MarginModal.vue # 保证金缴纳/释放弹窗(金额输入,无验证码步骤,见缺口6)
src/api/wallet.js # 本阶段全部接口封装
复用阶段3既有组件:ChannelSelect.vue(所属渠道选择)、PersonalCustomerPicker.vue(企业开户表单的"法人代表/实际控制人/受益人"客户名称选择,复用其选择个人客户的能力)、IdCardUpload.vue(企业开户"法人身份证正/反面"上传)、LicenseUpload.vue(企业开户"营业执照"上传)。
路由沿用阶段3例外方案:列表页(AccountList、EnterpriseOpenList)走菜单驱动路由;表单页(PersonalOpenForm、EnterpriseOpenForm)、账户主页(AccountDetail)作为 extraChildRoutes 非菜单子路由注册,通过 router.push + query 参数跳转:
'/wallet/account': 'wallet/account/AccountList' # 菜单
'/wallet/account/detail': 'wallet/account/AccountDetail' # extraChildRoutes,?accountNo=xxx
'/wallet/personal-open': 'wallet/personal-open/PersonalOpenForm' # extraChildRoutes(从账户列表"开户"弹窗跳入)
'/wallet/enterprise-open': 'wallet/enterprise-open/EnterpriseOpenList' # 菜单
'/wallet/enterprise-open/create': 'wallet/enterprise-open/EnterpriseOpenForm' # extraChildRoutes
'/wallet/enterprise-open/edit': 'wallet/enterprise-open/EnterpriseOpenForm' # extraChildRoutes
'/wallet/enterprise-open/detail': 'wallet/enterprise-open/EnterpriseOpenForm' # extraChildRoutes
菜单节点仅需配置"钱包管理 > 账户列表""钱包管理 > 企业开户申请"两项(后端权限管理侧配置),个人开户无独立菜单入口(需求文档 2.6.1.2:"点击开户,弹出'个人开户'、'企业开户'"选择框,由账户列表页内部弹窗触发跳转,不是独立菜单)。
4. 关键交互设计
4.1 账户列表页(AccountList.vue)
- 查询表单按后端实际能力裁剪为:账户名称(
accountName,模糊)、账号(accountNo,精确)——不提供需求文档描述的"所属机构""渠道""客户类型"下拉(见第7节缺口3) - 列表列裁剪为:序号、账户名称、账号、账户类型(
accountType用StatusTag展示 A1/A2/A3/A6/A7 中文名)、主/从标识(accountRelation)、状态(accountStatus)——不展示"活期账户余额""冻结账户余额""保证金账户余额""渠道""机构"列 - 余额展示折中方案:对当前页每一条
accountRelation==='0'(主账户)的记录,前端并发调用一次wallet-account/detail,将返回的mainBalance/a6Balance追加展示在该行"活期余额""保证金余额"两个补充列(从账户行留空,不重复查询);任一 detail 请求失败不阻塞整表渲染,失败单元格显示-- - 单选(
row-selection: {type:'radio'}),选中后启用:开分户(仅当选中行accountType==='A1')、销户、变更、手机号变更、绑卡、查看、余额同步;未选中全部禁用(除"开户"始终可用) - 点击"开户"→
<a-dropdown>或<a-modal>二选一("个人开户"跳/wallet/personal-open;"企业开户"跳/wallet/enterprise-open/create) - 点击账户名称/"查看"→
router.push('/wallet/account/detail?accountNo=xxx&id=xxx') - "变更"弹窗:复用
wallet-account/update字段(手机号/绑定银行卡号/开户行号/开户行名称);"手机号变更"弹窗单独走change-mobile(需求文档将两者列为并列按钮,后端也是两个独立接口,不合并) - "余额同步"点击后调用
sync-balance,成功后重新拉取当前行detail刷新余额补充列,不整表重新查询
4.2 个人开户表单(PersonalOpenForm.vue)
- 所属渠道:
ChannelSelect.vue - 账户名称:点击"选择客户"打开
WalletCustomerPicker(个人模式,调用query-customer),选中后回填customerId/customerName/certificateType/certificateNumber/certificateEffectiveDate/certificateExpiryDate/issuingAuthority/gender/certificateAddress/occupation(字段只读展示,与需求文档"选择个人客户后反显字段信息"一致) - 手机号码:文本输入,11位数字校验(复用
phoneValidatorRule) - 绑定银行账号:文本输入(缺口4:无卡号查询校验接口,仅做非空+纯数字长度校验)
- 职业:文本输入(缺口5:无职业三级码值菜单接口,退化为文本框,提交时按选中客户
occupation字段预填,可编辑) - 证件照片正/反面:复用
IdCardUpload.vue(side='front'/'back'),上传成功后取fileNo组装进fileList(fileType='01'/'02') - 提交:组装
create-draft请求体(accountProperty固定"1"),成功后展示applicationNo,弹出"申请已提交,等待客户手机扫码确认"提示框,提供"刷新状态"按钮调用query-progress轮询展示applicationStatus(不做自动定时轮询,避免无限请求,由用户手动点击刷新,与阶段3"移动端后续流程范围外"的既定处理方式一致) - 提交按钮 loading 态防重复点击(全局交互规则)
4.3 企业开户表单(EnterpriseOpenForm.vue)
四个信息区,均按需求文档 2.6.3.4 字段表实现(不使用后端 Bto 的全量字段):
- 基础信息:所属渠道(
ChannelSelect)、账户名称(点击"选择企业客户"→复用阶段3企业客户列表查询能力新建一个只读用途的企业客户选择弹窗,选中后回填enterpriseId/enterpriseName/businessLicense/certificateNumber/certificateEffectiveDate/certificateExpiryDate/certificateAddress)、经营范围(文本,可编辑)、职业(文本,缺口5同上)、手机号码(文本,11位校验)、绑定银行账号/开户行号/开户行全称(文本输入)。certificateType固定传BUSINESS_LICENSE,不展示选择框 - 法人代表信息:客户名称(点击"选择"打开
PersonalCustomerPicker,选中后回填legalPersonName,并需要额外一次个人客户detail查询补全certificateNumber/certificateExpiryDate映射为legalPersonIdType/legalPersonIdNo/legalPersonOpto——因列表接口字段不含证件到期日,与阶段3股东高管信息同样的字段补全问题) - 实际控制人信息:默认与"法人代表信息"相同(选中法人代表后自动回填,
controllerName/controllerIdType/controllerIdNo/controllerOpto= 法人代表对应字段),同时提供"重新选择"按钮可打开PersonalCustomerPicker单独指定 - 受益人信息(单条,非列表,与需求文档"受益人信息"字段表结构一致,虽然后端是数组
enterpriseAccountOpenApplicationBeneficiaryBtoList,前端仅收集一条,提交时包裹为长度1的数组):客户名称(默认同法人代表,可重新选择)、证件号(beneIdNo)、证件地址(beneAddress)、证件到期日(beneOpto)、性别(beneSex,由选中客户GenderEnum映射为'0'男/'1'女)、国籍(beneNationality,固定文本"中国",不可编辑)、是否股东(actCtrl,单选"是"/"否",默认"是")、持股比例(actHdRat,数字输入,单位%,0-100) - 影像信息:营业执照(
LicenseUpload→fileType='12')、法人身份证正/反面(IdCardUpload→fileType='01'/'02'),三者组装进enterpriseAccountOpenApplicationFileBtoList
提交:create 时不传 applicationNo;update 时用路由 query 的 applicationNo 映射到 id。编辑模式下四个信息区均可编辑(与阶段3"股东高管信息仅新增可写"不同,已在2.3节注明,UpdateEnterpriseAccountOpenApplicationBto 支持全量回写)。查看模式(detail)全部字段 disabled,并展示 applicationStatus/failReason/mainAccountNo/customerNo(开立成功后回填)。
4.4 企业开户申请列表(EnterpriseOpenList.vue)
- 查询条件:申请编号、企业名称(模糊)、申请状态(
EnterpriseOpenStatusEnum下拉)、创建时间范围(startDate/endDate) - 列表列:申请编号、企业名称、营业执照号、状态(
StatusTag)、渠道编号、主账户账号、失败原因、创建时间 - 操作列:查看(→ detail 路由)、编辑(仅
applicationStatus==='DRAFT'时可编辑,其余状态按钮禁用,理由:已确认/已开立的申请不应再改动基础信息,需求文档虽未明确限制但业务合理性决策,如实标注为前端侧约束,非后端强制) - 无删除入口(第2.3节已注明无删除接口)
4.5 账户主页(AccountDetail.vue)——2.6.4/2.6.5
- 页面载入:用路由 query 的
id调wallet-account/detail展示mainBalance/a2Balance/a6Balance/a7Balance四张余额卡片(无数据的子账户卡片不展示,如a2AccountNo为空则不渲染 A2 卡片) - 主账户卡片提供"提现"按钮(
WithdrawModal);保证金账户卡片提供"缴纳"/"释放"按钮(MarginModal,mode='pay'|'release') - 日期选择器:当月/上月/近三月快捷按钮 + 自定义起止日期,默认"今天"(
startDate=endDate=今天),变更后调用transaction-detail刷新列表(该接口一次性返回全部匹配记录,rows默认传大值如1000,前端本地做客户端分页展示,因为响应结构detailList是数组而非标准{total,records}分页结构,不复用ProTable的服务端分页模式,改用普通<a-table>+ 前端pagination配置) - 交易明细列:序号、系统流水号(
id)、记账日期(accountDate)、交易类型(transType)、对方户名(oppAccountName)、对方账号(oppAccountNo)、交易金额(transAmount,按detailType或金额符号用红/绿色区分借贷,若字段本身不含正负号标识则按detailType字符串包含"借/贷"关键字判断,具体规则以实测返回值为准,先按金额字符串是否带负号兜底)、账户余额(balance)、交易时间(tradeTime)、摘要(remark) - 排序:前端按
accountDate倒序、同日期按id升序本地排序(接口未声明排序保证,前端兜底排序,与需求文档规则2.6.4.5一致) - "回单"按钮:未勾选任何流水→按当前查询时间范围批量调用(受限于接口只支持单笔
originalSerialNo,批量场景前端循环逐笔调用download-receipt后打包为多个下载动作,不做后端不支持的"批量合并PDF/ZIP",见缺口7,提示文案调整为"将依次下载 N 笔回单");勾选一笔或多笔→逐笔调用 - "对账单"按钮:调用
download-statement,base64 解码为 Blob 触发浏览器下载,文件名对账单_${accountName}_${startDate}_${endDate}.pdf - 资金操作(提现/缴纳/释放)成功后:自动重新调用
detail+transaction-detail刷新余额与列表(需求文档2.6.4.5规则)
4.6 提现/保证金弹窗
WithdrawModal:金额输入(校验0 < amount <= mainBalance)、绑定银行卡号(从detail/bind-card-list取默认卡自动填充只读展示)、"发送验证码"按钮调用通用/api/auth/send-sms-code(businessType采用约定值WALLET_WITHDRAW,swagger 未定义该字段合法枚举,属工程假设,见缺口8)、验证码输入框、确定按钮组装WithdrawRequestEo提交MarginModal:金额输入(校验上限:pay模式不超过主账户可用余额,release模式不超过保证金账户余额,两个上限值均来自AccountDetail页面已加载的mainBalance/a6Balance)+ 摘要(可选文本),不提供验证码输入框(因MarginOperationRequestEo无verifyCode字段,提供了也无法真正传给后端校验,不做误导性 UI;已记入缺口6并同步登记缺失的后端接口.md)
4.7 绑卡管理弹窗(BindCardModal.vue)
- Props:
mode='personal'|'enterprise',内部按 mode 切换调用wallet-account/bind-card-list或enterprise-bank-card/list等对应三个接口 - 列表展示已绑卡片(
detailList: [{mobile,primaryAccount}]);"新增绑卡"表单:银行卡号/开户行号/开户行名称/手机号/证件号(企业模式另需accountProperty)+ "发送验证码"(businessType约定值WALLET_BIND_CARD,同缺口8)+验证码+是否默认;"解绑"按钮对每张已绑卡单独触发(最后一张不可解绑,提交后端会报错,前端不做客户端拦截,直接展示后端返回的 message)
5. 表单校验补充
不新增 validators.js 规则(手机号/金额校验复用现有 phoneValidatorRule/AntD 内置 number 类型规则),金额上限比较用组件内联 validator 函数(参考 MarginModal/WithdrawModal 各自的 amount 字段规则,依据当前已加载的余额上限动态生成,不适合提取为全局 validators)。
6. 业务规则映射
| 需求规则 | 落地方式 |
|---|---|
| 账户列表不支持多选,每次只能选中一条 | row-selection: {type: 'radio'} |
| 开分户仅主账户(A1)支持 | 选中行 accountType!=='A1' 时"开分户"按钮禁用 |
| 状态为"销户"禁用除查看外所有操作 | 选中行 accountStatus==='CLOSED' 时除"查看"外按钮全部禁用 |
| 状态为"冻结"禁用交易类操作 | 选中行 accountStatus==='FROZEN' 时"提现/缴纳/释放"入口禁用(这两个操作在账户主页而非列表页,列表页选中冻结账户后仍可"查看"进入主页,主页内按钮再次按状态禁用) |
| 提现/绑卡验证码有效期5分钟 | 前端不做本地倒计时强制失效校验(接口报错即视为失效,展示 message),"发送验证码"按钮点击后60秒内禁用防重复发送(常见反截流交互,需求未明确但属合理默认) |
| 操作频率限制(每分钟最多3次提现) | 前端不做本地计数拦截,依赖后端报错 message 展示,遵循"不做后端未明确的客户端强校验"原则 |
| 并发控制(同一账户同时只能一笔资金操作) | 前端每个弹窗提交时按钮 loading 禁用重复点击;跨弹窗并发依赖后端报错处理 |
| 对账单默认当前自然月 | 时间选择器初始值改为"当月"(1日至今日),而非需求2.6.4"默认今天"——2.6.4/2.6.8 两节对"默认时间范围"的描述不一致(前者说"今天",后者对账单场景说"默认当前自然月"),按场景区分:账户主页交易明细默认"今天"(遵循2.6.4.5),对账单下载弹窗单独的时间选择默认"当月"(遵循2.6.8.5),两处不共用同一个日期状态 |
7. 已识别的后端能力缺口(登记入 缺失的后端接口.md)
- 企业开户 Bto/Vo 含"负责人信息""经办人信息"两组字段,但需求文档字段表未要求——不采集,提交时不传;若后端将其中某字段标记为服务端必填,以实测报错为准调整(当前 swagger 未见
required数组包含这两组字段) - 企业开户"受益人信息"后端支持35个反洗钱合规字段,需求文档仅要求8个——按需求文档裁剪,只采集/回填
beneName/beneIdType/beneIdNo/beneOpto/beneSex/beneNationality/actCtrl/actHdRat,其余27个字段不提交(不传等同后端存空值,若后端有强制校验以实测调整) - 账户列表页字段/查询条件与需求文档不完全匹配——
WalletAccountVo/WalletAccountDetailVo均无"渠道""机构""冻结账户余额"字段,wallet-account/page查询体不支持按"所属机构""渠道""客户类型(个人/企业)"过滤(仅支持账户名称/账号/账户类型A1-A7);列表页按后端实际返回字段裁剪展示列与查询条件 - 个人/企业开户"绑定银行账号"无卡号查询校验接口——需求文档提及"卡信息查询接口223405"用于校验卡号正确性,swagger 中未检索到对应管理端接口(
wallet-account/wallet-management分组下均无query-bank-card/verify-card类路径),前端仅做格式校验,不做后端联动查卡校验 - "职业"字段无三级码值菜单查询接口——需求文档要求"职业表菜单选择,选择至三级码值名称",swagger 未检索到职业字典接口,退化为文本输入
- 保证金缴纳/释放请求体
MarginOperationRequestEo缺失channelNo/verifyCode字段——需求文档2.6.9/2.6.10均描述"发送验证码→校验"两步流程,且接口description也提及"验证码由前端自行调用发送验证码接口获取",但实际 schema 字段没有承载验证码的位置,前后端契约不一致;前端按 schema 实际字段实现(仅金额+摘要),不采集/不校验验证码,不做误导性 UI(与"不做后端未明确的强校验/不Mock接口"原则一致)。与之对比,提现接口WithdrawRequestEo正常含verifyCode字段,行为不对称,建议后端后续为MarginOperationRequestEo补齐这两个字段 - 回单下载接口仅支持单笔(
originalSerialNo单个字符串),不支持批量/流水号数组——需求文档2.6.7描述"批量下载:不勾选任何流水,点击回单按钮…返回一个PDF文件,内含多页"及"多笔/批量下载:返回一个zip",与实际接口签名(单笔单流水号,响应仅一个PDF)不符;前端改为"未勾选=按当前查询范围内所有可见流水逐笔循环调用并逐个触发下载"的兜底方案,不生成合并PDF/ZIP,已在4.5节标注 /api/auth/send-sms-code的businessType字段无枚举约束文档——该接口描述提到用于"手机号注册""找回密码(FIND_PASSWORD)"场景,钱包提现/绑卡场景复用该接口时businessType取值(WALLET_WITHDRAW/WALLET_BIND_CARD)属前端工程假设,非后端文档确认值,若后端有白名单限制会导致发送验证码报错,以实测报错信息为准调整取值- 无企业开户"开分户""销户"专属接口区分——企业主账户开立后从账户(A2/A3/A7)是否需要类似个人开户"主账户开立成功后从账户随之开立"的行为,swagger 未见企业专属"query-progress"等价接口,企业开户进度查询复用
enterprise-open-application/detail的applicationStatus字段代替(已在4.3节体现,未单独建 query-progress 接口调用) - 无独立"企业客户选择"轻量查询接口专供开户场景——个人开户有专属
personal-opening/query-customer,企业开户基础信息的"企业客户选择"沿用阶段3enterprise-customer/page接口(非钱包模块专属,行为应一致,只是接口分组不同,风险较低,仅记录以备后续核实)
8. 自查(占位符/矛盾/歧义/范围)
- 全文无 "TBD"/"待定" 类占位符;标注"以实测为准"的均为基于当前 swagger 文档信息不足的合理工程假设,已在第7节逐项列出
- 范围与总体设计文档阶段4定义(钱包管理)一致,未扩展至授信/商户/订单模块
- 第3节路由方案沿用阶段3"菜单驱动+extraChildRoutes非菜单子路由"的既定例外模式,无新增技术方案分歧
- 与批量决策结果核对:企业开户采用完整申请单流程(2.3/4.3节);移动端确认页面排除(第1节"不包含");渠道编号来源=
ChannelSelect表单内选择(4.2/4.3节),与批量决策结论一致,无冲突 - 4.6/7节已如实记录"保证金验证码"需求与接口的不一致,未强行编造字段或Mock校验逻辑,符合项目"不Mock接口"硬性约束
设计自查通过,直接进入 writing-plans 生成实施计划。