SFT/缺失的后端接口.md

1571 lines
144 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 缺失的后端接口 / 真实接口契约说明
> 更新说明:阶段1(工程基础设施+登录+系统管理)与阶段2(基础配置:核心企业/客户经理)原先按前端自
> 行设计的契约用 `mock-server/` 模拟开发。现后端已提供首版真实接口(`swagger_project_scfs_2026-07-
> 09_14-15-23.json`),前端已完成对接改造,不再使用 mock 契约。本文档改为:**Part 1** 速查已对接的
> 真实接口契约(便于前后端联调核对);**Part 2** 列出联调/开发中发现的、后端文档未覆盖或建议后续
> 补充的事项。`mock-server/` 保留作无后端环境下的应急本地演示兜底,契约与下文不一致,不作为参考。
>
> 通用约定(均已通过真实 swagger 核实):
> - 所有接口返回统一包裹结构 `{ code: number, message: string, data: any }`,`code === 200` 表示业务成功,
> 前端统一按 `code !== 200` 判断失败并展示 `message`,不再做具体错误码分支处理。
> - 除登录、刷新令牌、发送短信外,其余接口均需在请求头携带 `Authorization: Bearer {token}`。
> - 日期时间字段格式统一为 `yyyy-MM-dd HH:mm:ss`。
> - 分页查询接口(`.../page`)请求 Body 均含 `page`(从1开始)、`pageSize`;响应 `data` 统一为
> `{ total, page, pageSize, records: [...] }`(字段名为 `records`,不是 `list`)。
> - 除个别接口(见下文标注)外,增删改接口均为单条操作,不提供批量接口。
## Part 1:已提供接口契约速查
### 1.1 通用鉴权
| 接口 | 说明 |
| --- | --- |
| `POST /api/auth/login-password` | 账号密码登录,Body `{username, password}`,响应 `LoginVo` |
| `POST /api/auth/login-phone` | 手机验证码登录,Body `{phone, smsCode}`,响应 `LoginVo` |
| `POST /api/auth/send-sms-code` | 发送短信验证码,Body `{phone, businessType}`,登录场景前端暂传 `businessType: 'LOGIN'`(见 Part 2-1) |
| `POST /api/auth/refresh-token` | 刷新令牌,无 Body,依赖 httpOnly Cookie 中的 `refresh_token`(**不**携带 Authorization 头),响应 `LoginVo` |
| `POST /api/auth/logout` | 登出,无 Body |
| `POST /api/auth/user-info` | 获取当前用户信息,无 Body(基于当前 JWT),响应 `UserDetailVo` |
| `GET /api/auth/user/permission-detail?userId=` | 获取用户扁平权限列表 `UserPermissionDetailVo[]`,前端自行 `buildTreeFromFlat`/`extractMenuTree`/`extractButtonCodes` |
| `POST /api/auth/change-password` | 修改密码,Body `{oldPassword, newPassword}`(首次登录强制改密场景复用) |
| `POST /api/user/reset-password` | 管理员重置用户密码,Body `{userId}` |
| `POST /api/auth/reset-password` | 找回密码场景(未接入前端,登记备查),Body `{phone, smsCode, newPassword}` |
`LoginVo` 字段:`{ token, username, realName, userId, phone, userType, organizationId, forceChangePwd }`。
### 1.2 系统管理 —— 用户
| 接口 | Body/Query | 说明 |
| --- | --- | --- |
| `POST /api/auth/user/page` | `{organizationId, usernameLike, phone, status(ACTIVE\|LOCKED), roleId, page, pageSize}` | 响应 `records: UserPageVo[]`(含 `organizationName`/`roleNameList`,不含 `roleIds`/`createdAt`) |
| `POST /api/auth/user/create` | `{username, phone, realName, organizationId, status, email, userRoleBtoList:[{roleId}]}` | 响应 `CreateUserVo{id,status}`,不返回初始密码 |
| `POST /api/user/update` | `{id, username, phone, realName, organizationId, email, userRoleBtoList:[{userId,roleId}]}` | |
| `POST /api/user/delete` | `{userId}` | 单条 |
| `POST /api/user/lock` / `POST /api/user/unlock` | `{userId}` | |
| `GET /api/auth/user-role/list-by-user?userId=` | — | 响应 `UserRoleVo[]{id(即roleId), roleName, organizationId, isBuiltIn}`,用于编辑用户时回填已选角色 |
| `POST /api/auth/user-role/bind` / `unbind` | `{userId, roleIdList}` | 独立接口,当前未使用(create/update 已内嵌 `userRoleBtoList`) |
### 1.3 系统管理 —— 角色
| 接口 | Body | 说明 |
| --- | --- | --- |
| `POST /api/role/page` | `{roleName, page, pageSize}` | 不支持按机构/状态过滤,响应 `records: RoleVo[]` |
| `POST /api/role/detail` | `{roleId}` | 响应 `RoleDetailVo{roleName,id,organizationId,isBuiltIn,permissionIdList}` |
| `POST /api/role/create` | `{roleName, organizationId, isBuiltIn}` | 不含权限,不返回新建 id(见 Part 2-2) |
| `POST /api/role/update` | `{id, roleName, organizationId}` | 不含权限 |
| `POST /api/role/delete` | `{roleId}` | 单条 |
| `GET /api/auth/role-permission/list-by-role?roleId=` | — | 响应 `RolePermissionVo[]` |
| `POST /api/auth/role-permission/bind` | `{roleId, permissionIdList}` | 后端未提供 `unbind` 接口;前端不计算 diff,直接把页面上勾选的**全量**权限id列表一次性提交给 `bind`,由后端做全量覆盖 |
角色模型**不含**数据权限范围(dataScope)字段。
### 1.4 系统管理 —— 权限字典
| 接口 | Body | 说明 |
| --- | --- | --- |
| `GET /api/permission/tree` | — | 响应嵌套树 `PermissionTreeVo[]`,顶级 `level=0` |
| `POST /api/permission/create` | `{permissionType(PAGE\|BUTTON), permissionName, permissionCode, parentId, level, sortOrder, icon, routePath, pageResourcePaths[], apiResourcePaths[]}` | 响应 `{id, permissionCode}` |
| `POST /api/permission/update` | 同上,不含 `permissionType` | |
| `POST /api/permission/delete` | `{id}` | |
### 1.5 系统管理 —— 机构
| 接口 | Body/Query | 说明 |
| --- | --- | --- |
| `GET /api/organization/query-tree` | `{organizationName?, organizationId?}` | 响应嵌套树 `OrganizationTreeNodeVo[]`,不传筛选参数时返回全树 |
| `GET /api/organization/list-all` | `{organizationName?}` | 响应扁平 `OrganizationSimpleVo[]`,用于表单"所属机构"下拉 |
| `POST /api/organization/create` | `{organizationName, parentId, remark}` | `parentId` 为数字(引用上级机构的数字 `id`),响应完整 `OrganizationDetailVo` |
| `POST /api/organization/update` | `{organizationId(字符串), organizationName, parentId, remark}` | **注意**:定位字段是字符串业务码 `organizationId`,不是数字 `id` |
| `POST /api/organization/delete` | `{organizationId(字符串)}` | 同上,响应 `data``boolean` |
机构模型关键点:`id`(数字主键,供 `parentId`/用户/角色/核心企业/客户经理的 `organizationId` 外键引用)与
`organizationId`(字符串业务码,仅用于机构自身 update/delete 定位及 query-tree 筛选参数)是两个不同字段,
同时存在于同一条机构记录上。`/api/organization/tree`(POST,无筛选参数的全量树)与 `query-tree` 功能重叠,
前端统一只使用 `query-tree`
### 1.6 基础配置 —— 核心企业(渠道) / 客户经理
| 接口 | Body | 说明 |
| --- | --- | --- |
| `POST /api/core-enterprise/page` | `{page, pageSize, enterpriseName}` | 仅支持企业名称模糊搜索,响应 `records: CoreEnterpriseVo[]` |
| `POST /api/core-enterprise/create` / `update` / `delete` | 见 `CreateCoreEnterpriseBto`/`UpdateCoreEnterpriseBto`(字段:`enterpriseCode/enterpriseName/businessLicense/organizationId/contactPerson/contactPhone/status(ACTIVE\|INACTIVE)/remark`);`delete` Body `{id}` | |
| `POST /api/customer-manager/page` | `{page, pageSize, managerName}` | 仅支持姓名模糊搜索,响应 `records: CustomerManagerVo[]` |
| `POST /api/customer-manager/create` / `update` / `delete` | 字段:`managerCode/managerName/mobilePhone/organizationId/department/status(ACTIVE\|INACTIVE)/remark`;`delete` Body `{id}` | |
核心企业、客户经理模型均**不含** `organizationName`,前端本地通过 `organization/list-all` 建 id→name 映射
展示所属机构列。
> **重要说明(2026-07-17 补充)**:全局多处组件(`ChannelSelect.vue` 及其 8 个调用点:发票登记/匹配/
> 作废、绑卡、提现、身份证与营业执照上传、企业/个人开户等)的"所属渠道"下拉,复用的正是本接口
> `enterpriseName`/`enterpriseCode` 字段作为"渠道名称/渠道编号"。即产品侧"核心企业管理"页面本质上
> 就是全系统"渠道"的维护入口,`enterpriseName = 渠道名称`、`enterpriseCode = 渠道编号`,并非另一套
> 独立概念,原型图的字段命名(渠道/渠道编号)与现有接口字段语义完全一致,**无需**新增或改名这两个
> 字段。本次前端改造仅为"核心企业管理"页面新增/移除以下字段展示,详见 Part 2 第 67 条。
>
> **⚠️ 更正(2026-07-20)**:上述"复用 enterpriseName/enterpriseCode,无需改名"的结论已过期!
> 后端同事提供了最新 `CreateCoreEnterpriseBto` 源码截图,真实字段为 `channelName`(渠道名称)/
> `channelNo`(渠道编号,全局唯一)/`appNo`/`bankName`/`bankNo`/`organizationId`/`enterpriseName`
> (企业名称,与渠道名称是两个独立字段),`enterpriseCode` 字段已不存在。本次依据的 07-09 版 swagger
> 快照已明显滞后于后端真实实现。详见 Part 6 补充说明。
### 1.7 客户管理 —— 个人客户
| 接口 | 说明 |
| --- | --- |
| `POST /api/customer-info/personal/list` | `{organizationIdEq,customerCodeEq,customerNameLike,certificateNumberEq,page,pageSize}`,响应 `records: IndividualCustomerListVo[]`(仅 `id,organizationId,customerName,certificateType,certificateNumber,mobilePhone,ocrStatus,customerCode`,不含性别/创建人/创建时间;列表页最新处理方式见 Part 8 第77/78条) |
| `POST /api/customer-info/personal/detail` | `{id}``{customerCode}`,响应 `IndividualCustomerDetailVo` |
| `POST /api/customer-info/personal/create` | `CreateIndividualCustomerBto`,响应 `data`=新建id |
| `POST /api/customer-info/personal/update` | `UpdateIndividualCustomerBto` |
| `POST /api/customer-info/personal/ocr-idcard` | `{fileBase64,channelNo,fileType('01'\|'02')}`,响应 `OcrIdCardResultVo` |
| `POST /api/customer-info/personal/upload-file` | `{fileBase64,channelNo}`,响应 `{fileNo}`,个人身份证与企业营业执照图片均调用此通用接口 |
| `POST /api/customer-info/personal/face-recognize` | `{fileData(人脸照片base64),idNo,name,channelNo}`,响应含 `fileNo`+`recode`/`recodeInfo`(识别结果码/说明,枚举未文档化,详见 Part 10 第87条),个人开户"正面免冠照"专用,识别通过即完成文件存档,无需再调用 `upload-file` |
### 1.8 客户管理 —— 企业客户
| 接口 | 说明 |
| --- | --- |
| `POST /api/enterprise-customer/page` | `{page,pageSize,enterpriseName,businessLicense,mobilePhone}`,响应 `records: EnterpriseCustomerVo[]` |
| `POST /api/enterprise-customer/detail` | `{id}`,响应 `EnterpriseCustomerDetailVo` |
| `POST /api/enterprise-customer/create` | `{createEnterpriseCustomerBto,enableOcr}` |
| `POST /api/enterprise-customer/update` | `UpdateEnterpriseCustomerBto` |
### 1.9 钱包管理 —— 账户列表/账户主页
| 接口 | 说明 |
| --- | --- |
| `POST /api/wallet-account/page` | `{page,pageSize,accountName,accountNo,accountType(A1\|A2\|A3\|A6\|A7)}`,响应 `records: WalletAccountVo[]` |
| `POST /api/wallet-account/detail` | `{id}`,响应 `WalletAccountDetailVo{mainAccountNo,mainBalance,a2AccountNo,a2Balance,a6AccountNo,a6Balance,a7AccountNo,a7Balance}`(不含 accountName) |
| `POST /api/wallet-account/close` | `{id}`,销户 |
| `POST /api/wallet-account/open-sub-account` | `{accountNo}`(主账户账号),开分户 |
| `POST /api/wallet-account/update` | `{accountNo,mobile,bankCardNumber,bankNo,bankName}` |
| `POST /api/wallet-account/change-mobile` | `{accountNo,newMobile}` |
| `POST /api/wallet-account/sync-balance` | `{id}` |
| `POST /api/wallet-account/transaction-detail` | `{accountNo,channelNo,startDate,endDate,page,rows}`,响应 `TransactionDetailVo{...,detailList:TradeDetailItemVo[]}` |
| `POST /api/wallet-account/withdraw` | `{channelNo,withdrawRequestEo:WithdrawRequestEo}`,响应 `WithdrawResultVo` |
| `POST /api/wallet-account/margin-pay` / `margin-release` | `MarginOperationRequestEo{accountNo,accountName,tradeAmount,remark}`,响应 `MarginResultVo` |
| `POST /api/wallet-account/download-statement` / `download-receipt` | 均响应 `{fileData(base64 PDF)}` |
| `POST /api/wallet-account/bind-card-list` / `bind-card` / `unbind-card` | 个人绑卡管理三件套 |
### 1.10 钱包管理 —— 个人开户(`/api/wallet-management/personal-opening/*`)
| 接口 | 说明 |
| --- | --- |
| `POST .../query-customer` | 个人开户专属客户查询,响应字段同 `IndividualCustomerDetailVo`(2026-07-22 起"个人开户"页面改用 `/api/customer-info/personal/list`+`/detail` 组合,不再调用本接口;但 `WalletCustomerPicker.vue` 在个人贷款申请等场景仍在使用,详见 Part 10) |
| `POST .../create-draft` | 提交开户草稿,响应 `{applicationNo}` |
| `POST .../query-progress` | `{applicationNo}`,响应 `ApplicationProgressVo` |
| `POST .../list`(**后端未提供,前端假设契约**) | 2026-07-23 新增"个人开户申请"列表页,参照
`enterprise-open-application/list` 结构假设请求体含 `applicationNo`/`customerNameLike`/
`certificateNumber`/`applicationStatus`/`startDate`/`endDate`/`page`/`pageSize`,响应
`{total,page,pageSize,records}`,详见 Part 11 第91条 |
| `POST .../detail`(**后端未提供,前端假设契约**) | 请求 `{applicationNo}`,响应字段假设与
`create-draft` 请求体对齐(含 `applicationStatus`/`failReason`/`mainAccountNo`/`customerNo`
等开户结果字段),详见 Part 11 第91条 |
| `POST .../update`(**后端未提供,前端假设契约**) | 用 `applicationNo` 定位记录(而非数字 id),
仅允许 `DRAFT` 状态记录编辑,详见 Part 11 第91条 |
### 1.11 钱包管理 —— 企业开户申请与企业绑卡(`/api/wallet-management/enterprise-open-application/*`、`enterprise-bank-card/*`)
| 接口 | 说明 |
| --- | --- |
| `POST enterprise-open-application/list` | 响应 `records: EnterpriseAccountOpenApplicationListVo[]`(**无 id 字段**) |
| `POST enterprise-open-application/create` | `CreateEnterpriseAccountOpenBto`,响应 `{applicationNo}` |
| `POST enterprise-open-application/detail` | `{applicationNo}`,响应 `EnterpriseAccountOpenApplicationDetailVo`(**无 id 字段**) |
| `POST enterprise-open-application/update` | `UpdateEnterpriseAccountOpenApplicationBto`,**要求 `id`(int64)** |
| `POST enterprise-bank-card/list` / `bind` / `unbind` | 企业绑卡管理三件套,字段结构同个人绑卡但用 `primaryAccount`/`accountProperty` |
### 1.12 授信管理 —— 贷款申请(`/api/credit-apply/*`)
| 接口 | 说明 |
| --- | --- |
| `POST credit-apply/loan-application/list` | 柜员分页查询,入参仅支持 `loanApplicationId/applicationStatus/applicationType/customerName/createDateStart/createDateEnd`,响应 `records: LoanApplicationVo[]` |
| `POST credit-apply/loan-application/detail` | `{loanApplicationId}`,响应 `LoanApplicationDetailVo`(个人/企业字段混合于同一模型,约60余字段) |
| `POST credit-apply/personal-loan-application/create` / `update` | 个人贷款申请草稿创建/修改(仅 DRAFT 可改),响应 `data``loanApplicationId` 字符串 |
| `POST credit-apply/enterprise-loan-application/create` / `update` | 企业贷款申请草稿创建/修改,响应 `CreateEnterpriseLoanDraftResultVo` |
| `POST credit-apply/loan-application/confirm` | 申请人微信扫码确认(短验+人脸识别),移动端流程,不在本项目前端范围 |
### 1.13 商户中心 —— 商户管理(`/api/merchant/*`)
| 接口 | 说明 |
| --- | --- |
| `POST merchant/page` | 分页,入参仅支持 `merchantName`,响应 `records: MerchantVo[]`(13个字段,无 `loanPeriod/orderType/productType/guarantorAccountName`) |
| `POST merchant/create` | `CreateMerchantBto`,字段:`merchantNo/merchantName/coreEnterpriseId/customerManagerId/organizationId/a1AccountNo/a2AccountNo/guarantorAccountNo/guarantorAccountName/maxGuaranteeAmount/loanProductCode/creditRatio/loanPeriod/orderType/productType/status/remark` |
| `POST merchant/update` | `UpdateMerchantBto`,同上 + `id` |
| `POST merchant/delete` | `{id}` |
| — | 无 `/api/merchant/detail` 接口;无 approve/audit/review 审批相关接口(已全量检索 swagger 确认) |
### 1.14 商户中心 —— 发票管理(`/api/invoice/*`、`/api/invoice-management/*`)
| 接口 | 说明 |
| --- | --- |
| `POST invoice/create` | 发票登记(本地写操作),`CreateInvoiceRegistrationBto` |
| `POST invoice/batch-create` | 批量发票登记(本地写操作),入参为 `CreateInvoiceRegistrationBto[]`,响应 `BatchInvoiceCreateResultVo`(总数/成功数/失败数/失败详情JSON) |
| `POST invoice/match` | 发票匹配,**透传中台32007接口,无本地数据库操作**,需 `channelNo`+`matchType`(1自动/2人工)+`data:[{depositSerialNo,matchAmount}]` |
| `POST invoice/settle` | 发票清算并还款,**透传中台22310接口,无本地数据库操作**,仅需 `channelNo`+`invoiceNo` |
| `POST invoice/delete` | 发票作废,**透传中台31908接口,无本地数据库操作**,仅需 `channelNo`+`invoiceNo` |
| `POST invoice/unmatch` | 取消匹配(本地操作),仅需 `matchRecordId` |
| `POST invoice/page` | 发票分页,**透传中台31909查询接口**,需 `channelNo`,与 `invoice-management/invoice/list` 功能重叠,本项目未使用 |
| `POST invoice-management/invoice/list` | 本地业务列表查询,`FindInvoiceListQto`(merchantIdIs/invoiceNoLike/registerDateStart-End/invoiceStatusList/matchStatusList/settleStatusList/accountNoLike/oppAccountNoLike/amountMin-Max) |
| `POST invoice-management/invoice/detail` | `{id}`,响应含 `matchRecordList: InvoiceMatchVo[]` |
| `POST invoice-management/repayment/list` | 查询可匹配的本地回款流水,`FindRepaymentListQto`(merchantIdIs/accountNoLike/oppAccountNoLike/timeStart-End) |
| `POST invoice-management/offline-recharge-unmatched-detail` | 查询线下来账明细,**透传中台30908接口**,需 `channelNo`+交易日期范围,用于"自动匹配"高级场景,本次未在 UI 中实现对应入口 |
### 1.15 支付管理 —— 订单支付(`/api/payment/*`、`/api/payment-management/*`)
| 接口 | 说明 |
| --- | --- |
| `POST payment/query-credit-quota` | 查询授信额度,入参仅 `{accountNo}`(钱包账号字符串),响应 `data` **在 swagger 中完全未定义结构**(无 `$ref`/无 `properties`),真正的未文档化接口,前端只能做防御性字段展示 |
| `POST payment/order-pay` | 创建支付,`CreatePaymentBto`:`walletAccountId/loanApplyId(可空)/paymentMethod/payeeInfo/totalAmount/balanceAmount/financeAmount/status/remark/paymentDetailBtoList`,**无短信验证码字段** |
| `POST payment-management/query-payment-record` | 分页查询支付记录,`FindPaymentRecordListQto`,仅支持 `walletAccountIdIs/paymentMethodList/statusList/paidAtStart/paidAtEnd`,响应 `PaymentRecordListVo[]` |
| `POST payment-management/query-payment-detail` | `{id}`,响应 `PaymentRecordDetailVo`(列表字段 + `detailList: PaymentDetailVo[]`) |
| — | `PaymentMethodEnum`:`BALANCE`余额支付/`FINANCE`融资支付/`COMBINED`组合支付;`PaymentStatusEnum`:`PENDING_VERIFICATION/VERIFYING/PROCESSING/SUCCESS/FAILED/CANCELLED`;`FundSourceEnum`:`BALANCE/FINANCE` |
### 1.16 订单管理 —— 汇总订单/原始订单(`/api/summary-order/*`、`/api/original-order/*`)
| 接口 | 说明 |
| --- | --- |
| `POST summary-order/page` | 请求仅支持 `{page,pageSize,orderNo}`,响应 `SummaryOrderVo`(无关联原始订单明细列表) |
| `POST summary-order/detail` | `{id}`,响应即 `SummaryOrderVo` 本身,**不含关联原始订单明细** |
| `POST original-order/page` | 请求仅支持 `{page,pageSize,originalOrderNo}`,**不支持按 `summaryOrderId` 过滤** |
| `POST original-order/export` | 入参 `{summaryOrderId,originalOrderNo}` 均可选,响应 `data` **未定义结构**,前端沿用 `downloadStatement` 已建立的 `{fileData: base64}` 假设处理 |
| — | `SettlementStatusEnum`:`UNSCHEDULED/SETTLED_REPAID/FAILED`;`SummaryOrderVo` 含 `financeQuota/accumulatedLoanAmount` 等字段但**无剩余额度字段**,前端本地计算 `剩余额度 = financeQuota - accumulatedLoanAmount` |
---
## Part 2:待后端确认 / 建议补充事项
1. **`send-sms-code``businessType` 取值无 enum 文档**:swagger 仅标注为普通 `string`,登录场景前端暂定
`'LOGIN'`,需后端确认合法取值枚举(找回密码场景描述提到 `FIND_PASSWORD`,登录场景取值待确认)。
2. **`POST /api/role/create` 不返回新建记录 id**:前端已改为"创建成功后按 `roleName`+`organizationId` 反查
`role/page` 取得 id 再绑定权限"规避,建议后端补充直接返回新建 `id`(`core-enterprise/create`、
`customer-manager/create` 同样不返回新建 id,当前无需立即二次操作故不阻塞,但建议一并补充以保持接口
一致性)。
3. **批量删除接口缺失**:用户、角色、机构、核心企业、客户经理的删除接口均为单条,前端已改为循环调用
单条删除接口模拟批量交互(`src/utils/loopDelete.js`),建议后端评估是否补充原子性 batch 接口。
4. **管理员直接为其他用户指定新密码(无需旧密码)的接口不存在**:原型图中的"密码修改"入口暂保留但
置灰禁用,提示改用"密码重置"。
5. **`user/create`、`user/reset-password` 是否已实际发送短信通知用户,新密码规则(固定值/随机)未文档化**:
前端仅按约定展示"已通过短信通知用户"提示文案,不做实际校验。
6. **`role-permission/unbind` 接口后端未提供**:前端已改为不计算 diff,直接把页面上勾选的全量权限id
列表一次性调用 `bind` 提交给后端,由后端按全量覆盖处理,不再调用 `unbind`
7. **角色列表(`role/page`)不支持按所属机构过滤;核心企业、客户经理列表只支持单一名称模糊搜索**:
相比原前端自行设计的契约,过滤能力收窄,搜索表单已同步精简,若后续产品侧需要更多筛选维度,需后端
评估补充查询参数。
8. **角色数据权限范围(本机构/本机构及下级/全部)字段在真实模型中不存在**:若产品侧仍需要该能力,需
后端评估补充。
9. **用户列表/详情不返回 `roleIds`**:编辑用户角色时需额外调用 `user-role/list-by-user`,建议后端在
`UserPageVo`/`UserDetailVo` 中直接补充 `roleIdList` 以减少一次请求。
10. **权限字典种子数据需部署后手动初始化**:用户/角色/机构/权限/核心企业/客户经理各页面与按钮节点,
需部署后通过新的"系统管理 > 权限管理"页面手动创建,并授予内置管理员角色,否则登录后看不到任何
菜单。当前 `MainLayout.vue` 菜单渲染只支持两级,初始化菜单节点时层级需控制在 2 级以内。
11. **`/api/auth/reset-password`(找回密码)与登录页暂未接入**:当前登录页仅实现"账号密码"与"手机验证码"
两种登录方式,找回密码入口未在本次范围内开发,契约已在 Part 1-1 登记备查。
12. **个人客户/企业客户均无删除接口**:列表页暂不提供删除入口。
13. **营业执照无同步OCR识别接口**:`enterprise-customer/create` 的 `enableOcr` 参数用途/时序未文档化,
前端按"手动填写+提交时告知后端可异步识别"实现。
14. **企业客户股东高管信息(`enterpriseRelatedPersonBtoList`)只能在新增时一次性提交**:`update`/`detail`
接口均不支持读取或修改,编辑模式下该区块前端替换为提示文案,不可编辑。
15. **企业客户 `businessLicenseFileNo` 编辑/详情接口读取不到**:编辑模式无法回显已上传的营业执照图片。
16. **企业客户分页查询不支持按所属机构/企业编号过滤**:仅支持企业名称/营业执照号/手机号三项。
17. **个人客户 `customerCode`(客户编号)生成机制未文档化**:前端创建时不传该字段,交由后端生成。
18. **身份证/营业执照 OCR 结果字段命名未在 swagger 明确标注**(`OcrIdCardResultVo` 具体字段以实测为准),
前端 `mapOcrIdCardResult` 按常见字段名(`name/sex/nation/idcard/address/authority/birth/validDate`)
做防御性映射,识别失败或字段缺失时不阻断提交,允许手动填写。
19. **移动端确认页(个人开户 `mobile/*`、企业开户 confirm、贷款申请确认扫码后续)不属于本项目范围**:
相关接口已在后端提供但本次前端管理后台不实现对应移动端页面,登记备查。
20. **`WalletAccountDetailVo` 不含 `accountName`**:账户主页展示的账户名称改为由列表页通过路由 query
(`accountName`)传递,若用户直接刷新账户详情页地址,该字段会丢失显示为空,建议后端补充该字段。
21. **绑卡列表 `BindCardVo` 无法辨别"默认卡"**:提现/保证金默认取列表第一条作为默认卡,`accountProperty`
(借记卡/贷记卡)枚举取值未在 swagger 明确列出,前端按"01借记卡/02贷记卡"猜测渲染文案,建议后端
在文档中补充该字段真实枚举及是否存在"设为默认卡"能力。
22. **对账单/回单下载接口 `download-statement`/`download-receipt` 所需的 `originalSerialNo` 无法从交易
明细 `TradeDetailItemVo` 中直接获得独立流水号字段**:前端暂用该条记录的 `id` 字段兜底传递,若后端
实际需要的是独立业务流水号,批量下载回单功能将失败,需后端确认字段映射关系或补充响应字段。
23. **企业开户申请 `enterprise-open-application/list`、`detail` 接口返回的 Vo 均无 `id` 字段,但
`update` 接口的入参 `UpdateEnterpriseAccountOpenApplicationBto` 却要求 `id`(int64)**:前端编辑功能
已完整实现,提交更新时暂用 `applicationNo`(字符串)兜底传入 `id` 位置,该请求预计会被后端校验拒绝
或产生非预期行为,是本阶段最严重的接口契约缺口,需后端紧急评估:①在列表/详情 Vo 中补充 `id` 字段,
或②将 `update` 接口改为支持按 `applicationNo` 定位记录。
24. **`EnterpriseCustomerDetailVo``industry`(所属行业)字段**:企业开户申请表单选择企业客户后无法
自动回填所属行业,若产品侧需要联动回填,需后端在企业客户详情接口中补充。
**2026-07-31 更新**:`industry` 字段已改为使用动态字典(`dictTypeCode='industry_type'`,
`dictCategory=CASCADE`,门类->大类两级)驱动的多重下拉选择器(`DictSelect.vue`,自动根据
`dict-management/dict-type/page` 返回的 `dictCategory` 切换为 `a-cascader`)。提交给
`POST /api/wallet-management/enterprise-open-application/create``industry` 值为**最终选中
叶子字典项的 `itemValue` 字符串**(不是数字 `id`,也不是整条路径数组)。**已知局限**:回显
(编辑/查看已提交的申请)时,若该字典项此后被禁用,由于只读查询接口(`dict-items/tree`)只
返回已启用项,`DictSelect.vue` 会因在树里找不到匹配的 `itemValue` 而无法反查出完整路径展示
选中链路;后端目前没有提供按 `itemValue`/`id` 查询单个字典项详情(含已禁用项)的接口,若要
修复该局限需后端补充此类接口。
25. **企业开户申请受益人信息 `enterpriseAccountOpenApplicationBeneficiaryBtoList` 是否支持多条受益人
`update` 时的增删语义(全量替换 vs 增量)未在 swagger 说明**:前端当前只支持维护单条受益人信息,
提交时整体覆盖该数组,若产品侧需要多受益人管理,需后端明确该数组的更新语义。
26. **贷款申请存在两组平行接口 `/api/loan-application/*``/api/credit-apply/*`**:前者(`create-personal`
`create-enterprise`/`detail`/`page`)与后者(`personal-loan-application/create`等)字段结构不同、
用途描述均不完整,前端已选用后者(`credit-apply` 组,描述明确标注"银行柜员"场景且具备
DRAFT→CONFIRMED→SUBMITTED 状态机,与阶段4风格一致),前者暂未使用,建议后端确认是否为废弃接口或
说明二者分工,避免维护两套语义重叠的接口。
27. **个人贷款申请草稿 `CreatePersonalLoanApplicationDraftBto` 不含 `customerId`/账户号(钱包账号)字段**:
与企业贷款草稿 `CreateEnterpriseLoanDraftBto`(含 `customerId`+`accountNo`)不对称,个人贷款申请无法
与具体客户ID、具体钱包账号建立强关联,选择客户仅能回填 `customerName`/`phoneNo`/`permanentAddress`
三个文本字段,不产生外键关系,与需求文档"贷款钱包仅展示申请人已开立的钱包"的描述存在能力缺口,
建议后端为个人贷款草稿补充 `customerId`/`accountNo` 字段。
28. **贷款产品(`loanProductCode`)无独立的列表/字典查询接口**:前端改为柜员手动输入产品编号文本框,
无法做下拉选择与合法性校验,建议后端补充贷款产品字典查询接口。
29. **企业贷款草稿字段 `customerType` 无 enum 约束说明**:前端按项目既有命名习惯猜测传值 `'ENTERPRISE'`,
需后端确认合法取值(可能是 `'ENTERPRISE'`/`'BUSINESS'`/`'企业'` 等其他形式)。
30. **贷款申请没有"按企业客户查询其已开立钱包账号"的专用接口**:前端改为选定企业客户后,调用通用的
`wallet-account/page``accountName`(企业名称文本)模糊匹配钱包账户列表供人工选择,若同名不同企业
或企业名称与钱包账户名称不完全一致,可能匹配不到或匹配错误,建议后端补充按 `customerId` 精确查询
钱包账号的专用接口。
31. **法定代表人/配偶相关字段(`legalPersonEducation`/`legalPersonMarital`/`legalPersonNationality`/
`legalPersonGender`/`spouseGender` 等)均为无枚举约束的纯字符串**:前端为提升可用性复用个人贷款
对应枚举的取值域渲染下拉框,但后端未做强校验,实际入库是否要求严格匹配以实测为准。
32. **商户管理无 `/api/merchant/detail` 接口**:"查看/编辑"操作无法单独按 `id` 拉取最新详情,前端改为
由列表页把当前行(`MerchantVo` 全部字段)通过路由 query 带入表单页直接回填,若用户直接刷新表单页
地址或列表数据在打开表单期间发生变化,回显内容可能不是最新值,建议后端补充详情接口。
33. **商户管理无审批(approve/audit/review)相关接口**:已对 swagger 全量检索确认不存在该类路径,
原型图"审批"按钮及"审批中/通过"状态在后端无对应实现,前端改用 `MerchantStatusEnum`
(`ACTIVE`启用/`INACTIVE`停用)提供"启用/停用"操作代替,业务语义不完全等价,需与业务方确认是否
需要后端补充完整的审批工作流。
34. **`CreateMerchantBto`/`UpdateMerchantBto`/`MerchantVo` 均不含 docx/原型提及的
`goodsCategory`(商品类目)/`paymentCycle`(回款周期)/`singleOrderLimit`(单笔汇总订单限额)/
`financingRatio`(与 `creditRatio` 是否为同一字段存疑)/`estimatedReturnRate`(预计退货率)等
与贷款申请融资条件同源的字段**:前端表单只实现了接口实际支持的字段集合,原型列表列
"商户主体/主体钱包/回款商户/商品类型/回款周期"在 `MerchantVo` 中也无直接对应字段,已在列表中
省略或用现有字段(`a1AccountNo` 等)近似展示,建议后端评估是否需要扩展商户模型字段。
35. **商户分页查询(`merchant/page`)仅支持 `merchantName` 一项过滤**:原型图要求的"所属机构/商户编号/
商户类型/商品类型"四项查询条件后端不支持,已同步精简查询表单,若需要,建议后端补充对应查询参数。
36. **商户 `a1AccountNo`/`a2AccountNo`/`guarantorAccountNo` 均为纯字符串,不与客户/钱包账户建立外键
关联**:前端提供"选择钱包账户"辅助按钮(基于 `/api/wallet-account/page` 按户名模糊查询后手动选取
账号回填),但不保证选中账户与商户主体的真实归属关系,建议后端补充按客户/商户维度关联钱包账户的
校验或专用查询接口。
37. **发票登记/批量登记(`CreateInvoiceRegistrationBto`)无字段校验发票代码+发票号码组合唯一性**:
docx 要求"发票唯一性:发票代码+发票号码组合不可重复登记,重复提示'该发票已登记,请勿重复上传'",
前端未做提交前的唯一性预检(无对应查询接口支持"按代码+号码查重"),重复提交后的报错依赖后端
返回的 `message` 直接展示,若后端未实现该校验将导致重复数据入库,需后端确认已实现该规则。
38. **发票登记全部字段需手动录入,无 OCR 自动识别接口**:docx 提到发票号码/代码/金额/日期/购买方/
销售方名称"自动识别返回,可手动修改",后端未提供对应识别接口,已按纯手动录入实现。
39. **发票 `match`/`settle`/`delete` 均为"透传中台,无本地数据库操作"**:调用后本地 `InvoiceVo`
`matchStatus`/`settleStatus`/`invoiceStatus` 等字段更新依赖后端透传中台后自行同步本地记录(未在
swagger 中说明同步时序/是否为同步阻塞调用),前端调用成功后直接 `reload()` 列表,若后端存在异步
同步延迟,可能出现列表短暂未刷新到最新状态的情况。
40. **`InvoiceMatchVo`(匹配记录)无单条记录级别的"是否已进入清算流程"标记**:docx 规则"已进入清算
流程的回款记录禁止取消匹配"无法精确到每条匹配记录,前端简化为按发票整体 `settleStatus` 是否为
`UNSETTLED` 判断是否允许"取消匹配",可能与真实业务规则(可能存在部分匹配记录已清算、部分未清算
的混合场景)不完全一致,建议后端在 `InvoiceMatchVo` 中补充清算状态字段。
41. **发票列表原型底部"当前可用发票金额"统计无对应后端聚合接口/字段**:前端未实现该汇总展示,
建议后端评估是否需要补充按商户/渠道维度汇总"发票金额-已匹配/已结算金额"的统计接口。
42. **`offline-recharge-unmatched-detail`(线下来账明细,透传中台30908)与本地
`invoice-management/repayment/list` 功能存在重叠但数据源不同(前者查中台侧未匹配来账,后者查本地
回款流水表)**:二者关系未在 swagger 中说明(是否本地回款流水由中台来账同步生成、同步时序如何),
本次匹配交互统一采用本地 `repayment/list`,`offline-recharge-unmatched-detail` 对应的"自动匹配/
中台侧查验"高级交互未实现对应 UI 入口。
43. **`/api/invoice/page`(透传中台31909查询)与 `/api/invoice-management/invoice/list`(本地查询)为
功能重叠的两组平行接口**:前者需 `channelNo` 且字段命名与本地 `InvoiceVo` 不同,本项目统一采用
后者用于列表展示,前者未使用,建议后端确认二者分工或说明是否为废弃接口。
44. **`payment/query-credit-quota` 响应 `data` 字段完全未定义结构**:swagger 中该字段无 `$ref`、无
`properties`,前端在支付表单中改为原样展示返回的 JSON 供操作人员肉眼判断,无法做强类型的"剩余
额度"校验与展示,若融资金额超出实际额度,只能依赖后端 `order-pay` 接口调用失败后的错误提示兜底,
建议后端补充该接口的响应字段定义。
45. **`order-pay``CreatePaymentBto` 无短信验证码字段**:docx 描述的"发起支付前需短信验证"流程在
后端契约层面无法真正落地,前端仍按 docx 实现了"发送验证码(复用 `send-sms-code`)+ 输入验证码"的
交互与本地格式校验(非空+6位数字),但验证码本身不会被传给 `order-pay` 也不会被后端比对校验,
该环节目前形同虚设,需后端评估是否要在 `CreatePaymentBto` 中补充 `smsCode` 字段并在服务端真正
校验。
46. **`payment-management/query-payment-record`(及其 `PaymentRecordListVo`/`PaymentRecordDetailVo`)
均无机构/商户/订单编号/付款人姓名等字段**:原型图查询区与列表列包含"机构/商户/订单编号/付款人/
付款账号/服务费"等信息,后端当前的支付记录模型仅关联到 `walletAccountId`,不关联商户或订单,
前端已按接口实际返回字段精简查询表单与列表列,未臆造上述字段,建议后端评估是否需要在支付记录
落库时补充商户/订单关联字段。
47. **`CreatePaymentBto` 只有一个 `walletAccountId` 字段,无法同时指定"余额账户"与"融资账户"两个不同
账户 ID**:结合 `wallet-account/detail` 以一个账户 id 返回其所属客户整套 `mainBalance/a2Balance/
a6Balance/a7Balance` 余额的既有设计模式,前端推断该字段应始终填客户主账户(A1)的 id,融资额度
查询(`query-credit-quota`)则用该账户的 `accountNo` 反查,组合支付时的"融资部分"通过
`loanApplyId` 关联的贷款申请单据来承载客户与金额信息,不再单独指定 A3 账户 ID。此为前端基于现有
接口设计模式的**推断性假设**,未在 swagger 中被明确证实,建议后端确认该理解是否准确。
48. **`loanApplyId` 取自 `credit-apply/loan-application/list` 返回记录的数字主键 `id`**,且该模块使用的
`ApplicationStatusEnum`(`DRAFT/CONFIRMED/SUBMITTED/APPLY_FAILED`)**无"已批准/生效/放款"等更精确的
终态**,前端支付表单在选择"关联贷款申请"时只能按 `SUBMITTED`(已申请)状态筛选可选记录,无法准确
判断该笔贷款是否已实际放款、是否仍有可用余额支持本次融资支付,建议后端补充更精确的贷款状态枚举
或提供专门的"可用于支付的贷款额度"查询接口。
49. **`summary-order/page`/`original-order/page` 查询入参严重少于 docx/原型描述**:`summary-order/page`
仅支持 `orderNo`,`original-order/page` 仅支持 `originalOrderNo`,原型图要求的渠道/日期范围/订单
状态/结算状态/店铺 ID 等查询条件均无对应参数,前端已按接口实际支持字段精简查询表单,建议后端
评估是否需要补充查询参数。
50. **`original-order/export` 响应 `data` 字段未定义结构**:与 `download-statement`/`download-receipt`
等已文档化 `fileData` 字段的导出接口不同,该接口响应结构在 swagger 中完全空白,前端沿用既有
"假设为 `{fileData: base64}`"的处理模式实现下载,若实际结构不同将导致导出功能报错或下载内容
异常,建议后端补充响应字段定义。
51. **`summary-order/detail` 不返回关联原始订单明细,且 `original-order/page` 不支持按
`summaryOrderId` 过滤查询**:两个缺口叠加导致"查看汇总订单关联的原始订单明细列表"功能在当前
接口能力下无法实现,前端在汇总订单详情弹窗中用 `a-alert` 明确提示该缺口,未展示虚假数据,建议
后端为 `original-order/page` 补充 `summaryOrderId` 过滤参数,或在 `summary-order/detail` 响应中
直接内嵌关联的原始订单列表。
52. **`GET /api/organization/query-tree` 需按登录用户机构自动限定返回范围(机构数据隔离需求)**:
当前该接口返回全量机构树,不区分调用者身份。需要后端基于请求头 `Authorization` 中的 JWT 识别
当前登录用户,若该用户所属机构不是顶级机构,则只返回"该机构 + 其下属机构"组成的子树(顶级机构
用户仍返回全树);不新增请求参数,现有 `organizationName`/`organizationId` 筛选参数含义不变,
在裁剪后的范围内叠加过滤。前端 `OrgTreeSelect.vue` 组件已按"完全信任接口返回数据,不做二次裁剪"
的方式实现,后端完成本条改造后前端机构下拉框会自动生效为"仅本机构及下属机构可选",无需前端
再次改动。
53. **`GET /api/organization/list-all` 需要同样按登录用户机构自动限定返回范围**:规则同第 52 条,
返回"当前用户机构 + 下属机构"组成的扁平列表(顶级机构用户返回全部)。
54. **`POST /api/role/page` 需新增 `organizationId`(数字,机构主键)请求参数**:传入时按"该机构及
其下属机构"过滤角色列表结果(呼应本文档 Part 2 第 7 条已登记的缺口,现补充明确参数名与语义)。
前端 `RoleList.vue` 已新增"所属机构"筛选字段并会传该参数,后端未支持前该筛选暂不会真正生效。
55. **机构数据隔离的安全边界说明**:第 52/53/54 条只解决 UI 展示层"默认值 + 可选范围"的问题;真正
的数据安全隔离必须由后端在各业务查询接口(用户、角色、客户、商户等分页接口)内部,基于当前
登录用户可见的机构范围校验/过滤查询条件与结果,不能仅信任前端传入的 `organizationId` 参数,
否则用户可直接调用接口绕过前端限制越权查看其他机构数据。建议后端在通用鉴权层增加"按机构数据
权限"的统一拦截能力。
## Part 3:机构数据隔离改造 —— 各业务列表查询接口补充 `organizationId` 过滤参数
> 以下接口对应的数据实体(Vo)**已经含有** `organizationId` 字段,仅列表查询接口的请求参数缺少
> 对应的过滤入参。前端已在对应列表页新增"所属机构"筛选项并默认选中当前登录用户所属机构,会
> 随请求一并传递 `organizationId` 参数,后端补充该参数支持后前端无需任何改动即可自动生效。
56. **`POST /api/core-enterprise/page`(基础配置-核心企业管理)需新增 `organizationId`
(数字,机构主键)请求参数**:传入时按"该机构及其下属机构"过滤核心企业列表结果。
`CoreEnterpriseVo` 已含 `organizationId` 字段。
57. **`POST /api/customer-manager/page`(基础配置-客户经理管理)需新增 `organizationId`
请求参数**:规则同第 56 条。`CustomerManagerVo` 已含 `organizationId` 字段。
58. **`POST /api/enterprise-customer/page`(客户管理-企业客户管理)需新增 `organizationId`
请求参数**:规则同第 56 条。`EnterpriseCustomerVo` 已含 `organizationId` 字段。
59. **`POST /api/credit-apply/loan-application/list`(授信管理-贷款申请列表)需新增
`organizationId` 请求参数**:规则同第 56 条。`LoanApplicationVo` 已含 `organizationId`
字段("关联机构的id,指向所属机构"),补充成本较低,只差查询入参。
60. **`POST /api/merchant/page`(商户中心-商户管理)需新增 `organizationId` 请求参数**:
规则同第 56 条。`MerchantVo` 已含 `organizationId` 字段。
## Part 4:机构数据隔离改造 —— 无机构关联字段的业务实体需先补充关联关系
> 以下接口对应的数据实体(Vo)**完全不含** `organizationId` 字段,仅通过 `merchantId` /
> `customerId` / `walletAccountId` 等外键与其他实体间接关联,当前无法确定其"所属机构"。
> 前端已在对应列表页先接线"所属机构"筛选项(默认选中当前登录用户所属机构并随请求传递
> `organizationId` 参数),但在后端完成以下关联关系建设并支持该过滤参数前,筛选不会产生
> 实际效果。请后端评估技术方案(如建立间接关联查询,或在对应 Vo 中补充冗余 `organizationId`
> 字段)。
61. **`POST /api/wallet-account/page`(钱包管理-账户列表)**:`WalletAccountVo` 仅含
`customerId`,无机构关联,需后端建立"钱包账户 -> 客户 -> 所属机构"的关联并支持
`organizationId` 过滤参数。
62. **`POST /api/wallet-management/enterprise-open-application/list`(钱包管理-企业开户
申请)**:`EnterpriseAccountOpenApplicationListVo` 无机构关联字段(该接口连 `id` 字段
都缺失,已在本文档第 23 条登记),需后端先补充机构关联字段/`id` 字段,再支持
`organizationId` 过滤参数。
63. **`POST /api/invoice-management/invoice/list`(商户中心-发票管理)**:`InvoiceVo` 仅含
`merchantId`,无机构关联,需后端建立"发票 -> 商户 -> 所属机构"的关联并支持
`organizationId` 过滤参数。
64. **`POST /api/summary-order/page`(订单管理-汇总订单)**:`SummaryOrderVo` 仅含
`merchantId`,无机构关联,需后端建立"汇总订单 -> 商户 -> 所属机构"的关联并支持
`organizationId` 过滤参数。
65. **`POST /api/original-order/page`(订单管理-原始订单)**:`OriginalOrderVo` 仅含
`merchantId`/`summaryOrderId`,无机构关联,需后端建立"原始订单 -> 商户 -> 所属机构"的
关联并支持 `organizationId` 过滤参数。
66. **`POST /api/payment-management/query-payment-record`(支付管理-支付记录)**:
`PaymentRecordListVo` 仅含 `walletAccountId`,而钱包账户本身也无机构关联字段(见第 61
条),是本次改造中关联链最薄弱的一个,需后端优先评估补充方案(建立"支付记录 -> 钱包
账户 -> 客户 -> 所属机构"的完整关联链)并支持 `organizationId` 过滤参数。
## Part 5:核心企业管理(渠道)页面字段调整 —— 2026-07-17 补充
> 产品侧提供了"核心企业管理"页面的最新原型图,要求页面字段/搜索条件/按钮严格按图实现。经核对,
> 该页面的"渠道名称/渠道编号"与全局其它 8 处"所属渠道"下拉(`ChannelSelect.vue`)复用的
> `enterpriseName`/`enterpriseCode` 字段是同一概念(见 Part 1.6 补充说明),无需改动;但原型图新增
> 了以下字段,当前 `CoreEnterpriseVo`/`CreateCoreEnterpriseBto`/`UpdateCoreEnterpriseBto` 均未提供,
> 前端已按原型完成 UI 改造并在创建/更新请求中按最大努力一并传递这些字段,列表展示时若接口未返回则
> 显示占位符"-",待后端补充字段后前端无需再次改动即可自动生效。
67. **`CoreEnterpriseVo`/`CreateCoreEnterpriseBto`/`UpdateCoreEnterpriseBto` 需新增以下字段**:
- `appNo`(应用编号,字符串,新增/编辑必填)
- `bankName`(开户银行/开户行,字符串,选填)
- `bankNo`(银行行号,字符串,选填)
- `createdAt`(创建时间,`yyyy-MM-dd HH:mm:ss`,仅 `CoreEnterpriseVo` 需要,由后端自动生成)
另外,原型图暂未包含"签名密钥"字段,产品侧明确**本次新增/编辑表单不需要提供签名密钥**,前端
表单未接入该字段;但请后端注意后续版本大概率会补充该字段(用于渠道对接鉴权),建议提前预留
字段位置(如 `signKey`),避免后续需要数据库变更。
68. **`CoreEnterpriseVo` 原有字段 `businessLicense`/`contactPerson`/`contactPhone`/`status`/
`remark` 在本次原型图中均未展示**:前端"核心企业管理"页面的列表列与新增/编辑弹窗已移除这些
字段,新增/编辑请求不再传递。若数据库对这些列存在非空(`NOT NULL`)约束,请后端确认可调整为
允许空值,否则新增请求可能因缺少这些字段而报错。
69. **`POST /api/core-enterprise/page` 需新增 `enterpriseCode`(渠道编号,模糊匹配)过滤参数**:
原型图查询区包含"渠道编号"输入框,当前接口仅支持 `enterpriseName` 模糊搜索,`organizationId`
过滤已在 Part 3 第 56 条登记,此处补充 `enterpriseCode` 参数需求。
## Part 6:核心企业(渠道)创建接口字段更正 —— 2026-07-20 补充
> 前端新增核心企业时发现请求未能生效(渠道编号/渠道名称等核心字段没有正确传入)。经核实,后端已经
> 实现了 Part 5 第67条要求的 `appNo`/`bankName`/`bankNo` 字段(感谢及时补充),但**真实字段命名与
> 前端此前依据的 07-09 版 swagger 快照不一致**:后端同事提供了最新 `CreateCoreEnterpriseBto` 源码
> 截图,确认真实字段为:
> - `channelName`(渠道名称)
> - `channelNo`(渠道编号,注释标注"全局唯一")
> - `appNo`(应用编号)
> - `bankName`(开户银行)
> - `bankNo`(银行行号)
> - `enterpriseName`(企业名称,**与 `channelName` 是两个独立字段**,并非同一概念)
> - `organizationId`(所属机构ID)
>
> 即原 `enterpriseCode` 字段已不存在,已被 `channelNo` 取代;`enterpriseName` 仍然存在但不再等同于
> "渠道名称"。Part 1.6 及 Part 5 第67/69条基于旧版 swagger 快照得出的"渠道名称/渠道编号复用
> `enterpriseName`/`enterpriseCode`,无需改名"结论已过期,以本条为准。
70. **前端已同步修正**:`ChannelList.vue` 新增/编辑表单、列表列、搜索条件均已改为提交/读取
`channelName`/`channelNo`(而非 `enterpriseName`/`enterpriseCode`);全局下游读取核心企业列表
的组件(`ChannelSelect.vue` 及其 8 个"所属渠道"下拉调用点、`MerchantForm.vue`/`MerchantList.vue`
的"核心企业"下拉)均已改为优先读取 `channelName`/`channelNo`,并兼容旧字段名
`enterpriseName`/`enterpriseCode` 作为兜底,避免历史数据或后端环境未完全升级时选择器/列表显示
空白。
71. **`enterpriseName`(企业名称)字段语义待确认**:后端 Bto 中 `enterpriseName` 是独立于
`channelName`(渠道名称)的字段,但当前"核心企业管理"页面原型图未提供单独的"企业名称"输入项。
前端新增/编辑提交时暂时以"渠道名称"的值兜底填充 `enterpriseName`,避免该字段为空导致创建失败
(是否有非空校验尚未确认)。请产品/后端确认:①`enterpriseName` 是否确实需要与 `channelName`
区分(例如渠道对应的合作方公司全称 vs 渠道简称/编号别名);②若需要区分,请后端明确业务含义,
前端将在弹窗中补充独立的"企业名称"输入项。
72. **`POST /api/core-enterprise/page` 查询过滤参数命名需要后端确认**:Part 5 第69条要求新增
`enterpriseCode` 模糊过滤参数,若查询接口的参数命名已与创建接口同步改为 `channelNo`/
`channelName`,请后端明确告知。当前前端查询请求已**同时携带** `channelNo`/`channelName` 与
`enterpriseCode`/`enterpriseName` 两套参数名做兼容,待后端确认真实参数名后可移除多余的兼容传参。
73. **强烈建议后端更新/提供最新 swagger 快照或补充 `PROJECT_ID`**:本次问题的根源是前端依据的
`swagger_project_scfs_2026-07-09_14-15-23.json` 快照已滞后于后端真实实现,且项目
`.toco/CONFIG` 未配置 `PROJECT_ID`,无法通过 MCP 工具在线查询最新接口定义,只能依赖人工截图
核对,效率低且容易遗漏字段(例如本次的 `enterpriseName` 字段语义变化)。建议后端团队:
①在 `.toco/CONFIG` 补充 `PROJECT_ID`;②或在每次接口变更后重新导出并覆盖 swagger 快照文件。
## Part 7:个人客户新增表单"更多信息"字段补充 —— 2026-07-20 补充
> 产品侧提供了"个人客户新增"页面的最新原型截图,要求页面在现有基础上:①原有字段全部改为必填并去掉
> "职业"输入框;②新增一个可折叠的"更多信息"区块,补充证件类型(从主表单挪入)与19个新字段;③"证件
> 失效日期"旁增加"长期"勾选框。经核对
> `CreateIndividualCustomerBto`/`UpdateIndividualCustomerBto`/`IndividualCustomerDetailVo`,截图新增的
> 19个字段**全部不存在**于现有接口契约,《凡荣e链前端业务需求说明书.md》2.5.1节与已抓取的原型页面
> (`website_detail/客户管理/个人客户`)中也找不到任何字段/编码依据。前端已按截图完成 UI 改造,新增
> 字段随创建/更新请求一并提交(后端目前会忽略未声明字段,不会报错,但也不会持久化),待后端补充字段
> 定义后前端无需再次改动即可自动生效。
74. **`CreateIndividualCustomerBto`/`UpdateIndividualCustomerBto`/`IndividualCustomerDetailVo` 需扩展
以下19个字段**(前端字段名如下,均为新增,建议类型见括号):
- `nationality`(国籍,字符串,默认"中国")
- `maritalStatus`(婚姻状况,复用已有 `MaritalStatusEnum`)
- `residenceStatus`(居住状况,枚举,**取值为前端临时拟定的占位值** `SELF_OWNED`/`RENTED`/`OTHER`,
需后端/产品确认标准编码表后由前端替换)
- `residentType`(居民性质,枚举,前端占位值 `PERMANENT`/`NON_PERMANENT`,同上需确认)
- `educationLevel`(最高学历,复用已有 `EducationLevelEnum`)
- `degree`(最高学位,枚举,前端占位值 `NONE`/`BACHELOR`/`MASTER`/`DOCTOR`,需确认)
- `mainIncomeSource`(主要经济来源,枚举,前端占位值 `SALARY`/`BUSINESS`/`OTHER`,需确认)
- `annualPersonalIncome`(个人年收入,数字)
- `annualFamilyIncome`(家庭年收入,数字)
- `employmentStatus`(从业状况,枚举,前端占位值
`EMPLOYED`/`SELF_EMPLOYED`/`FREELANCE`/`RETIRED`/`UNEMPLOYED`/`OTHER`,需确认)
- `occupationType`(职业类型,枚举,前端占位值
`ENTERPRISE_MANAGER`/`TECHNICAL`/`SELF_EMPLOYED`/`FARMER`/`FREELANCER`/`STUDENT`/`RETIRED`/
`WHOLESALE_RETAIL_OTHER`/`OTHER`,截图中出现"其他批发与零售服务人员"选项已纳入,**该字段真实
业务上很可能需要对接标准职业分类码表(如 GB/T 6565),前端占位选项远不完整,需后端/产品明确**)
- `employerName`(工作单位名称,字符串)
- `employerIndustry`(工作单位所属行业,枚举,前端占位值
`AGRICULTURE`/`MANUFACTURING`/`WHOLESALE_RETAIL`/`CONSTRUCTION`/`FINANCE`/`IT`/`EDUCATION`/
`OTHER`,**同样可能需要对接标准行业分类码表,前端占位选项不完整**)
- `positionType`(职务类型,枚举,前端占位值 `LEADER`/`MIDDLE_MANAGER`/`STAFF`/`OTHER`,需确认)
- `professionalTitle`(职称,枚举,前端占位值 `SENIOR`/`INTERMEDIATE`/`JUNIOR`/`NONE`,需确认)
- `employmentStartDate`(参加本单位时间,日期,`yyyy-MM-dd HH:mm:ss`)
- `isAgriculturalCustomer`(是否涉农客户,布尔,默认 `false`)
- `residencyArea`(境内境外,枚举,前端占位值 `DOMESTIC`/`OVERSEAS`,默认 `DOMESTIC`,需确认)
- `customerStatus`(客户状态,枚举,前端占位值 `FORMAL`/`POTENTIAL`,默认 `FORMAL`;注意本文档 Part 2
第12条曾记录"客户实体本身也无状态属性,不做",本次因产品侧新截图明确要求而重新引入,以本条为准)
- `remark`(备注,字符串,多行文本)
以上枚举取值均为**前端临时拟定的极简占位选项**(定义于 `src/constants/customerEnums.js`),
非后端已有枚举,后端确认标准编码表后前端会同步替换,请勿直接按前端占位值建表/校验。
75. **"证件失效日期"新增"长期"勾选框,当前用哨兵日期权变**:`certificateExpiryDate` 字段目前仍是
具体日期字符串,没有专用布尔字段表示"长期有效"(常见于46周岁以上居民身份证)。前端勾选"长期"后,
提交时用固定哨兵值 `9999-12-31 00:00:00` 代替具体日期。建议后端后续补充专用布尔字段(如
`certificateLongTerm`)承载该语义,避免长期依赖这个约定值(尤其要注意该哨兵值不应被"证件已过期"
之类的日期比较逻辑误判)。
76. **个人客户相关4个接口的请求头新增了 `Channelno`,且全站请求头大小写格式已按最新约定统一修正**:
为配合本次"上传身份证正反面图片需带渠道信息"的需求,前端已将 `src/utils/traceHeaders.js` 修正为
输出 `Appno`/`Channelno`/`Serialno`/`Transdate`/`Transtradetime`(首字母大写、其余小写)5个 header
(此前只有 `serialNo`/`transDate`/`transTradeTime` 三个小写驼峰形式,不完整也不符合约定大小写)。
`Channelno` 目前只在个人客户新增/编辑涉及的4个接口(`ocr-idcard`/`upload-file`/`create`/`update`)
上,当页面已选定渠道时才携带;编辑模式下若用户未重新选择渠道,则不携带该 header。全站其它接口
请求会统一获得修正后的 `Appno`/`Serialno`/`Transdate`/`Transtradetime`(与渠道无关,全局生效),
请后端确认接口层按此大小写格式正确读取,若后端框架对 header 名大小写不敏感可忽略本条。
## Part 8:个人客户列表页字段/筛选项按最新原型截图补充 —— 2026-07-20 补充
> 产品侧提供了"客户管理 > 个人客户管理"列表页的最新原型截图,要求列表展示"性别""创建人""创建
> 时间"三列,查询区增加"资料完整""客户状态"两个筛选项,并对"证件号码"做脱敏展示。前端已按截图
> 完成列展示与查询区改造,脱敏为纯前端展示层处理(不影响提交/查询的真实参数值);但下述字段/参数
> 后端目前均不提供,前端暂按"展示但内容为空"/"传参但后端可忽略"方式实现,不编造数据,待后端补充后
> 自动生效。
77. **`IndividualCustomerListVo` 需扩展"性别""创建人""创建时间"三个字段**:当前响应仅含
`id,organizationId,customerName,certificateType,certificateNumber,mobilePhone,ocrStatus,
customerCode`(见 Part 1 第 1.7 节)。列表页新增的三列字段建议:
- `gender`(性别,复用详情接口 `IndividualCustomerDetailVo` 已有的 `MALE`/`FEMALE`/`UNKNOWN` 枚举)
- `createBy`(创建人姓名/工号文本;注意详情接口目前只有 `creatorId`,是用户ID不是姓名,列表页与
详情页展示"创建人"都需要后端直接返回可读文本,前端没有"根据用户ID查姓名"的接口可用来反查)
- `createTime`(创建时间,格式 `yyyy-MM-dd HH:mm:ss`;详情接口 `IndividualCustomerDetailVo` 已有
该字段,建议列表接口同步补充,避免语义不一致)
补充前,前端列表页这三列会因后端未返回对应字段而显示为空,不是缺陷。
78. **个人客户列表查询接口 (`FindIndividualCustomerListQto`) 需扩展"资料完整""客户状态"两个筛选
参数**:前端已在查询区新增以下两个下拉筛选框,并会随请求一并提交,但后端目前既无对应参数也无
对应枚举定义,提交后预期被后端忽略(不生效),不影响其余已支持参数的筛选结果:
- `dataCompleteEq`(资料完整,枚举,**前端临时占位值** `COMPLETE`/`INCOMPLETE`,定义于
`src/constants/customerEnums.js``DATA_COMPLETE_OPTIONS`,业务含义待产品/后端确认——例如
是否等价于身份证OCR识别成功、必填字段是否补全等,目前只是从截图外观拟定的占位选项)
- `customerStatusEq`(客户状态,复用已有的 `CUSTOMER_STATUS_OPTIONS`——`FORMAL`/`POTENTIAL`,与
Part 7 第 74 条记录的 `customerStatus` 是同一套前端占位枚举,同样需要后端确认标准编码表后
前端再替换真实值)
## Part 9:企业客户新增表单"更多信息"/股东高管信息扩展/对外投资关系信息 —— 2026-07-21 补充
> 产品侧提供了"客户管理 > 企业客户管理 > 新增企业客户"页面的最新原型截图,要求:①基础信息区多个字段
> 由非必填改为必填,法定代表人改为在"股东与高管信息"卡片内逐行勾选(删除独立字段);②新增可折叠的
> "更多信息"区块,含约30个标量字段与联系信息/地址信息两个可增删行的子表格;③"公司股东与高管信息"由
> 只读表格重构为可增删的卡片列表,补充多个新字段;④新增"对外投资关系信息"区块(全新结构,原表单不
> 存在)。经核对 `CreateEnterpriseCustomerBto`/`UpdateEnterpriseCustomerBto`/`EnterpriseCustomerDetailVo`
> /`EnterpriseRelatedPersonBto`,除 `position`/`shareholdingRatio`/`nationality`/`contactPhone` 外,
> 本次涉及的字段**基本全部不存在**于现有接口契约。前端已按截图完成 UI 改造,新增标量字段(更多信息 +
> 联系信息/地址信息)随创建/更新请求一并提交,新增数组字段(股东高管扩展字段、对外投资关系)仅在
> "新增"模式下随创建请求提交(理由见第81/82条);后端目前会忽略未声明字段,不会报错,也不会持久化,
> 待后端补充字段定义后前端无需再次改动即可自动生效。
79. **`CreateEnterpriseCustomerBto`/`UpdateEnterpriseCustomerBto`/`EnterpriseCustomerDetailVo` 需扩展
"更多信息"区块的以下标量字段**(前端字段名如下,均为新增):
- `unitNature`(单位性质,枚举,已于2026-07-21根据后端 `/api/enterprise-customer/create` 实际报错
日志中 `com.scfs.customer_info.common.enums.UnitNatureEnum` 的真实取值订正:
`ENTERPRISE_LEGAL_PERSON`/`PUBLIC_INSTITUTION`/`SOCIAL_ORGANIZATION`/`INDIVIDUAL_BUSINESS`/`OTHER`,
定义于 `src/constants/enterpriseEnums.js``UNIT_NATURE_OPTIONS`,不再是占位值)
- `economicComposition`(经济成分,枚举占位,`ECONOMIC_COMPOSITION_OPTIONS`)
- `institutionCategory`(机构类别/登记注册类型,枚举占位,`INSTITUTION_CATEGORY_OPTIONS`)
- `economicNature`(经济性质经营类型,枚举占位,`ECONOMIC_NATURE_OPTIONS`)
- `nationalEconomyCategory`(国民经济门类,枚举,采用 **GB/T 4754 标准20门类真实分类,非占位**,
定义于 `NATIONAL_ECONOMY_CATEGORY_OPTIONS`,可直接按此码表建表)
- `registeredCapital`(注册资本,数字)+ `registeredCapitalCurrency`(注册资本币种,枚举,复用
`CURRENCY_OPTIONS`,默认"人民币")
- `establishmentDate`(企业成立日期,`yyyy-MM-dd HH:mm:ss`)
- `paidInCapital`(实收资本,数字)+ `paidInCapitalCurrency`(实收资本币种,同上复用 `CURRENCY_OPTIONS`)
- `lastYearEmployeeCount`(上年末企业从业人员,数字,单位人次)
- `lastYearTotalAssets`(上年末资产总额,数字)
- `lastYearRevenue`(上年营业收入,数字)
- `netAssets`(净资产,数字)
- `basicAccountBankName`(基本账户开户行名称,字符串)+ `basicAccountNumber`(基本账户账号,字符串)
- `accountLicenseNumber`(开户许可证号,字符串)+ `accountLicenseDate`(开户日期,`yyyy-MM-dd HH:mm:ss`)
- `paymentCardNumber`(缴款卡号,字符串)
- `enterpriseScale`(企业规模,枚举,采用**工信部四级标准分类(大型/中型/小型/微型),非占位**,
定义于 `ENTERPRISE_SCALE_OPTIONS`,可直接按此码表建表)
- `foreignExchangeEnterpriseType`(外汇企业类型,枚举占位,`FOREIGN_EXCHANGE_ENTERPRISE_TYPE_OPTIONS`)
- `pbocFinancialInstitutionCode`(人行金融机构编码,字符串)
- `financialInstitutionIndustryType`(金融机构行业分类,枚举占位,`FINANCIAL_INSTITUTION_INDUSTRY_OPTIONS`)
- `interbankCustomerType`(同业客户类型,枚举占位,`INTERBANK_CUSTOMER_TYPE_OPTIONS`)
- `businessStatus`(经营状态,枚举占位,`BUSINESS_STATUS_OPTIONS`)
- `customerStatus`(客户状态,枚举,复用个人客户已有的 `CUSTOMER_STATUS_OPTIONS`——`FORMAL`/`POTENTIAL`,
与 Part 7 第74条、Part 8 第78条是同一套前端占位枚举)
- `isFrozenCustomer`(是否冰点客户,布尔,默认 `false`)+ `frozenType`(冰冻类型,枚举占位,
`FROZEN_TYPE_OPTIONS`,仅 `isFrozenCustomer``true` 时才有意义)
- `residencyArea`(境内境外,枚举,`DOMESTIC`/`OVERSEAS`,与个人客户 Part 7 第74条命名保持一致,
默认 `DOMESTIC`)
- `isGreenEnterprise`/`isGroupCustomer`/`isIndividualBusiness`/`isListedCompany`/
`isSmallMicroEnterprise`/`isTechEnterprise`(是否绿色企业/集团客户/个体工商户/上市公司/
小微企业/科技企业,均为布尔,默认 `false`)
- `remark`(备注,字符串,多行文本)
以上标注"占位"的枚举取值均为**前端临时拟定的极简占位选项**(定义于
`src/constants/enterpriseEnums.js`),非后端已有枚举,请勿直接按前端占位值建表/校验;标注
"真实分类,非占位"的 `nationalEconomyCategory`/`enterpriseScale` 采用的是国家标准/部委标准码表,
可直接按该码表在后端建表。
80. **建议新增 `contactInfoBtoList`(联系信息)、`addressInfoBtoList`(地址信息)两个子数组结构**,
均随 create/update 提交,当前后端无对应字段:
- `contactInfoBtoList` 每项:`{contactType, contactValue, isPrimary}`,`contactType` 枚举占位
(移动电话/固定电话/传真/电子邮箱/其他,定义于 `CONTACT_TYPE_OPTIONS`),`isPrimary` 布尔(主要标志)
- `addressInfoBtoList` 每项:`{addressType, country, regionText, detailAddress, isPrimary}`,
`addressType` 枚举占位(证件地址/联系地址/注册地址/经营地址/其他,定义于 `ADDRESS_TYPE_OPTIONS`),
`country` 为国家代码(默认 `CN`),`regionText` 是省市区级联拼接后的文本(不下传结构化省市区
code,见第83条),`isPrimary` 布尔(主要标志)
81. **`EnterpriseRelatedPersonBto` 需在已有 `position`/`shareholdingRatio`/`nationality`/
`contactPhone` 基础上补充以下新字段**:
- `positionStartDate`(任职时间,`yyyy-MM-dd HH:mm:ss`)
- `industryYears`(相关行业从业年限,数字)
- `contributionMethod`(出资方式,枚举占位,`CONTRIBUTION_METHOD_OPTIONS`:货币/实物/知识产权/
土地使用权/其他)
- `contributionCurrency`(出资币种,复用 `CURRENCY_OPTIONS`,默认"人民币")
- `subscribedAmount`(应出资金额,数字)
- `paidAmount`(实际出资金额,数字)
- `contributionRatio`(出资比例,数字,百分数;注意与已有 `shareholdingRatio` 持股比例含义不同——
持股比例是最终股权占比,出资比例是本次出资占应出资总额的比例,两者均需保留)
- `contributionDate`(出资日期,`yyyy-MM-dd HH:mm:ss`)
- `remark`(备注,字符串)
页面上展示的国籍/性别/职业类型/联系电话/地址信息/客户编号中,只有 `nationality`(国籍)、
`contactPhone`(联系电话,前端字段名 `mobilePhone`)是提交到本结构的;性别/职业类型/地址信息/
客户编号属于**个人客户自身的属性**(选中关联人后从个人客户 `detail` 接口只读回填展示),不属于
"企业-关联人关系"这层数据,前端不会重复提交到 `EnterpriseRelatedPersonBto`
该扩展字段**仅在"新增企业客户"时随 `enterpriseRelatedPersonBtoList` 提交**,原因见第82条。
82. **股东高管信息、对外投资关系信息均改为可增删的卡片列表,但仍只允许在"新增"模式下编辑并提交,
编辑/详情模式展示提示文案**:现有 `enterpriseRelatedPersonBtoList` 只存在于
`CreateEnterpriseCustomerBto`,`UpdateEnterpriseCustomerBto`/`EnterpriseCustomerDetailVo` 均无
此字段(Part 2 第14条已记录该限制),本次未改变这一现状。对外投资关系是全新结构,后端完全不存在,同样
无法确认 update/detail 是否可回显,为避免"编辑时列表总是空"造成用户误以为数据丢失,前端统一
按"仅新增模式可编辑"处理。**建议后端评估是否可以让 `UpdateEnterpriseCustomerBto`/
`EnterpriseCustomerDetailVo` 同步支持这两个数组字段**,以便后续开放编辑态维护。
83. **全新结构建议:`enterpriseInvestmentBtoList`(对外投资关系信息)**,后端目前完全没有对应字段,
前端仅在新增模式下随 `createEnterpriseCustomerBto` 提交,每项字段:
- `investeeEnterpriseId`(被投资企业客户ID,通过 `EnterpriseCustomerPicker.vue` 选择企业客户获得,
**仅支持选企业客户,不支持个人客户**)
- `investeeName`(被投资企业名称,选中后只读回填,不可编辑)
- `certificateType`(证件类型,固定"营业执照",只读回填)
- `certificateNumber`(营业执照号,只读回填)
- `certificateExpiryDate`(证件到期日,只读回填)
- `shareholdingRatio`(持股状况/本企业持有被投资企业的股权比例,数字,百分数)
- `contributionMethod`/`contributionCurrency`/`subscribedAmount`/`paidAmount`/
`contributionRatio`/`contributionDate`(出资相关字段,含义与第81条 `EnterpriseRelatedPersonBto`
同名字段一致)
- `investorEconomicComposition`(出资人经济成分,枚举,复用第79条 `ECONOMIC_COMPOSITION_OPTIONS`)
- `relationType`(关系类型,枚举占位,`INVESTMENT_RELATION_TYPE_OPTIONS`:控股/参股/联营/合营/其他)
- `isValid`(是否有效,布尔,默认 `true`)
- `relationExpiryDate`(关系到期时间,`yyyy-MM-dd HH:mm:ss`)
- `remark`(备注,字符串)
截图中的"关联客户编号"列固定展示"暂无关联客户编号"(新增时后端尚未生成该关系记录的编号,纯展示
占位,不对应任何提交字段,不提交)。
84. **证件地址由自由文本改为"省市区级联 + 详细地址"两部分,是纯前端展示层处理,不新增后端字段**:
省市区数据来自静态 npm 包 `china-division`(不依赖后端接口),提交时仍拼接为
`${省市区文本}${详细地址}` 写入现有的单一自由文本字段 `certificateAddress`,与个人客户/企业客户
"证件地址"既有的自由文本约定保持兼容。地址信息表格(第80条 `addressInfoBtoList`)中的行政区划
同理,只提交拼接后的 `regionText`,不提交结构化省市区 code。**建议后端后续评估是否需要将证件
地址拆分为省(`provinceCode`)/市(`cityCode`)/区(`districtCode`)结构化字段**,非阻塞项,前端
当前方案已能满足截图交互要求。
85. **"是否法定代表人"由股东与高管信息卡片逐行勾选互斥决定,提交时仍复用已有的顶层
`legalRepresentativeId` 字段,后端无需改动**:原表单"法定代表人"是独立选择字段,现改为在每张
股东/高管卡片内勾选"是否法定代表人"(同时最多一人勾选"是",前端本地维护互斥),提交时取值为
`true` 的那一条关联人的 `individualId`,写入顶层 `legalRepresentativeId` 字段提交;若无人勾选,
该字段为空。此字段本身在 swagger 中已存在,本条仅记录前端交互方式变化,不涉及接口契约变更。
86. **"新增企业客户"页面全部码值汇总(供后端逐项核对/对齐)——2026-07-21 补充**:第79-83条已分别说明各
字段"是否占位",本条按字段汇总**完整的码值:标签对照表**,供后端一次性核对哪些可直接采用、哪些
需要更正为后端已有编码表。除标注"✅已确认"外,其余均为**前端临时拟定的占位值**,后端如已有现成
编码表,请反馈真实取值,前端将比照 `unitNature` 的处理方式(见第79条,已根据 `/api/enterprise-
customer/create` 实际报错日志中的 `UnitNatureEnum` 真实取值订正)同步修正。
**(1) `CreateEnterpriseCustomerBto`/`UpdateEnterpriseCustomerBto` 标量字段枚举**
| 字段 | 码值:标签 | 状态 |
| --- | --- | --- |
| `certificateType` | 固定传值 `BUSINESS_LICENSE`(证件类型,企业客户固定为营业执照) | 沿用已有字段,非新增枚举 |
| `unitNature`(单位性质) | `ENTERPRISE_LEGAL_PERSON`:企业法人 / `PUBLIC_INSTITUTION`:事业单位 / `SOCIAL_ORGANIZATION`:社会团体 / `INDIVIDUAL_BUSINESS`:个体工商户 / `OTHER`:其他 | ✅已确认(来自后端 `UnitNatureEnum` 报错日志真实取值) |
| `economicComposition`(经济成分) | `STATE_OWNED`:全民所有制 / `COLLECTIVE`:集体所有制 / `PRIVATE`:私营 / `FOREIGN_OWNED`:外资 / `SINO_FOREIGN_JOINT_VENTURE`:中外合资 / `SINO_FOREIGN_COOPERATIVE`:中外合作 / `OTHER`:其他 | ⚠️占位 |
| `institutionCategory`(机构类别/登记注册类型) | `LIMITED_LIABILITY_COMPANY`:有限责任公司 / `JOINT_STOCK_COMPANY`:股份有限公司 / `STATE_OWNED_ENTERPRISE`:全民所有制企业 / `COLLECTIVE_ENTERPRISE`:集体所有制企业 / `SOLE_PROPRIETORSHIP`:个人独资企业 / `PARTNERSHIP`:合伙企业 / `FOREIGN_INVESTED_ENTERPRISE`:外商投资企业 / `GOVERNMENT_AGENCY`:机关 / `PUBLIC_INSTITUTION`:事业单位 / `SOCIAL_ORGANIZATION`:社会团体 / `OTHER`:其他 | ⚠️占位 |
| `economicNature`(经济性质经营类型) | `STATE_OWNED`:国有 / `COLLECTIVE`:集体 / `PRIVATE`:私营 / `FOREIGN_OWNED`:外资 / `JOINT_VENTURE`:合资 / `COOPERATIVE`:合作 / `OTHER`:其他 | ⚠️占位 |
| `nationalEconomyCategory`(国民经济门类) | `A`:农、林、牧、渔业 / `B`:采矿业 / `C`:制造业 / `D`:电力、热力、燃气及水生产和供应业 / `E`:建筑业 / `F`:批发和零售业 / `G`:交通运输、仓储和邮政业 / `H`:住宿和餐饮业 / `I`:信息传输、软件和信息技术服务业 / `J`:金融业 / `K`:房地产业 / `L`:租赁和商务服务业 / `M`:科学研究和技术服务业 / `N`:水利、环境和公共设施管理业 / `O`:居民服务、修理和其他服务业 / `P`:教育 / `Q`:卫生和社会工作 / `R`:文化、体育和娱乐业 / `S`:公共管理、社会保障和社会组织 / `T`:国际组织 | ✅已确认(GB/T 4754 国标20门类) |
| `enterpriseScale`(企业规模) | `LARGE`:大型 / `MEDIUM`:中型 / `SMALL`:小型 / `MICRO`:微型 | ✅已确认(工信部《中小企业划型标准规定》四级分类) |
| `registeredCapitalCurrency`/`paidInCapitalCurrency`(注册资本/实收资本币种,复用同一枚举) | `CNY`:人民币 / `USD`:美元 / `EUR`:欧元 / `HKD`:港币 / `OTHER`:其他 | ⚠️占位 |
| `foreignExchangeEnterpriseType`(外汇企业类型) | `WHOLLY_FOREIGN_OWNED`:外商独资企业 / `SINO_FOREIGN_JOINT_VENTURE`:中外合资企业 / `SINO_FOREIGN_COOPERATIVE`:中外合作企业 / `FOREIGN_REP_OFFICE`:外国企业常驻代表机构 / `OTHER`:其他 | ⚠️占位 |
| `financialInstitutionIndustryType`(金融机构行业分类) | `BANKING`:银行业 / `SECURITIES`:证券业 / `INSURANCE`:保险业 / `TRUST`:信托业 / `FUND`:基金业 / `FUTURES`:期货业 / `OTHER`:其他 | ⚠️占位 |
| `interbankCustomerType`(同业客户类型) | `BANK`:银行同业 / `SECURITIES`:证券同业 / `INSURANCE`:保险同业 / `TRUST`:信托同业 / `OTHER`:其他 | ⚠️占位 |
| `businessStatus`(经营状态) | `NORMAL`:正常经营 / `SUSPENDED`:停业 / `DEREGISTERED`:注销 / `REVOKED`:吊销 / `OTHER`:其他 | ⚠️占位 |
| `customerStatus`(客户状态,与个人客户 Part 7 第74条、Part 8 第78条共用) | `FORMAL`:正式客户 / `POTENTIAL`:潜在客户 | ⚠️占位 |
| `frozenType`(冰冻类型,仅 `isFrozenCustomer=true` 时有意义) | `FULL_FROZEN`:全额冻结 / `PARTIAL_FROZEN`:部分冻结 / `JUDICIAL_FROZEN`:司法冻结 / `OTHER`:其他 | ⚠️占位 |
| `residencyArea`(境内境外) | `DOMESTIC`:境内 / `OVERSEAS`:境外 | ⚠️占位 |
| `isFrozenCustomer`/`isGreenEnterprise`/`isGroupCustomer`/`isIndividualBusiness`/`isListedCompany`/`isSmallMicroEnterprise`/`isTechEnterprise` | 布尔 `true`/`false`,均默认 `false` | 布尔字段,非字符串枚举 |
**(2) `contactInfoBtoList`(联系信息子表格,见第80条)**
| 字段 | 码值:标签 | 状态 |
| --- | --- | --- |
| `contactType`(联系方式类型) | `MOBILE`:移动电话 / `TELEPHONE`:固定电话 / `FAX`:传真 / `EMAIL`:电子邮箱 / `OTHER`:其他 | ⚠️占位 |
| `isPrimary`(主要标志) | 布尔 `true`/`false` | 布尔字段 |
**(3) `addressInfoBtoList`(地址信息子表格,见第80条)**
| 字段 | 码值:标签 | 状态 |
| --- | --- | --- |
| `addressType`(地址类型) | `CERTIFICATE`:证件地址 / `CONTACT`:联系地址 / `REGISTERED`:注册地址 / `BUSINESS`:经营地址 / `OTHER`:其他 | ⚠️占位 |
| `country`(国家/地区) | `CN`:中国 / `OTHER`:其他(选"其他"时行政区划退化为自由文本,项目未引入国际地区字典) | ⚠️占位 |
| `isPrimary`(主要标志) | 布尔 `true`/`false` | 布尔字段 |
**(4) `enterpriseRelatedPersonBtoList`(股东与高管信息,见第81条)**
| 字段 | 码值:标签 | 状态 |
| --- | --- | --- |
| `relatedType`(关联人类型) | `SHAREHOLDER`:股东 / `EXECUTIVE`:高管 / `BENEFICIARY_OWNER`:受益所有人(**当前硬编码在页面模板中,未抽取到枚举字典文件,尤其需要后端确认**) | ⚠️占位 |
| `position`(职务) | `CHAIRMAN`:董事长 / `GENERAL_MANAGER`:总经理 / `DEPUTY_GENERAL_MANAGER`:副总经理 / `FINANCE_HEAD`:财务负责人 / `SUPERVISOR`:监事 / `DIRECTOR`:董事 / `OTHER`:其他 | ⚠️占位 |
| `contributionMethod`(出资方式) | `CURRENCY`:货币 / `PHYSICAL`:实物 / `INTELLECTUAL_PROPERTY`:知识产权 / `LAND_USE_RIGHT`:土地使用权 / `OTHER`:其他 | ⚠️占位 |
| `contributionCurrency`(出资币种,复用(1)中的币种枚举) | `CNY`/`USD`/`EUR`/`HKD`/`OTHER` | ⚠️占位 |
| `isLegalRepresentative`(是否法定代表人,卡片内互斥勾选,不提交到本结构,见第85条) | 布尔 `true`/`false` | 布尔字段 |
以下三项是选中关联人后从个人客户 `detail` 接口**只读回填展示**,不提交到 `EnterpriseRelatedPersonBto`(见第81条说明),仅供后端确认前端展示映射是否与个人客户既有枚举一致:
| 字段 | 码值:标签 |
| --- | --- |
| 证件类型(展示) | `ID_CARD`:居民身份证 / `PASSPORT`:护照 / `BUSINESS_LICENSE`:营业执照 / `OTHER`:其他 |
| 性别(展示) | `MALE`:男 / `FEMALE`:女 / `UNKNOWN`:未知 |
| 职业类型(展示,复用个人客户 `OCCUPATION_TYPE_OPTIONS`) | `ENTERPRISE_MANAGER`:企业管理人员 / `TECHNICAL`:专业技术人员 / `SELF_EMPLOYED`:个体经营人员 / `FARMER`:务农人员 / `FREELANCER`:自由职业者 / `STUDENT`:学生 / `RETIRED`:退休人员 / `WHOLESALE_RETAIL_OTHER`:其他批发与零售服务人员 / `OTHER`:其他 |
**(5) `enterpriseInvestmentBtoList`(对外投资关系信息,全新结构,见第83条)**
| 字段 | 码值:标签 | 状态 |
| --- | --- | --- |
| `investorEconomicComposition`(出资人经济成分,复用(1)中 `economicComposition` 枚举) | 同上 `ECONOMIC_COMPOSITION_OPTIONS` | ⚠️占位 |
| `relationType`(关系类型) | `CONTROLLING`:控股 / `PARTICIPATING`:参股 / `AFFILIATED`:联营 / `JOINT_OPERATION`:合营 / `OTHER`:其他 | ⚠️占位 |
| `contributionMethod`/`contributionCurrency`(出资方式/币种,复用(4)同名枚举) | 同上 | ⚠️占位 |
| `isValid`(是否有效,默认 `true`) | 布尔 `true`/`false` | 布尔字段 |
以上全部枚举定义于前端 `src/constants/enterpriseEnums.js`(`UNIT_NATURE_OPTIONS` 等)与
`src/constants/customerEnums.js`(`CUSTOMER_STATUS_OPTIONS`/`OCCUPATION_TYPE_OPTIONS`),
`relatedType`/证件类型展示映射/性别展示映射三项例外,当前直接硬编码在
`EnterpriseForm.vue`/`EnterpriseRelatedPersonCard.vue` 组件内,尚未抽取到枚举字典文件。
**待后端反馈真实编码表后,前端将统一替换并同步补充到本文档。**
---
## Part 10:钱包管理"个人开户"表单按最新原型截图改造 —— 2026-07-22 补充
> 产品侧提供了"钱包管理 > 个人开户"页面的最新原型截图,要求按截图重排字段顺序与只读/可编辑范围,
> 补充身份证 OCR 结果与已选客户信息的比对报错、正面免冠照采集、开户协议勾选强制项,并移除"开户
> 行号"/"开户行名称"两个截图未出现的字段(两者原为选填字段,省略不影响 `create-draft` 提交)。
> 选择客户环节改为与"客户管理 > 个人客户管理"页面一致的处理方式:先调用 `/api/customer-info/
> personal/list` 分页查询(复用现有 `PersonalCustomerPicker.vue`),选中后再调用 `/api/customer-
> info/personal/detail` 取完整字段回填表单,不使用本模块专属但字段未必与个人客户模型对齐的
> `personal-opening/query-customer` 接口(该接口仍被 `WalletCustomerPicker.vue` 在个人贷款申请
> 等其他场景使用,`src/api/wallet.js` 中的封装函数予以保留,仅本页面不再调用,详见 1.10 节)。
> 产品侧同时确认本页面**不需要短信验证码
> 环节**,前端已移除验证码输入框、发送按钮及 `send-sms-code` 调用。核对 `.../personal-opening/
> create-draft`、`/api/customer-info/personal/list`、`/api/customer-info/personal/detail`、
> `/api/customer/ocr-idcard`、`/api/customer/upload-file` 后,本次 `fileType`(01/02/04/13)、
> `gender`/`certificateType` 枚举、`jobNote` 字段均已在 swagger 中有明确文档依据,**并非新增
> 缺口**;真正的缺口集中在"人脸识别结果码枚举"、开户协议内容两方面,记录如下:
87. **"正面免冠照 + 去采集"已改用后端新提供的人脸识别接口,但识别结果码枚举未文档化——
2026-07-22 更新**:后端已提供 `POST /api/customer-info/personal/face-recognize`(凡荣e链
222005 接口透传),入参 `{fileData(人脸照片 base64,字段命名含"zip"但经后端工程师确认实际
直传图片 base64 即可,非真正的 zip 压缩包)/idNo(身份证号)/name(姓名)/channelNo}`,前端已
改为选中/拍摄照片后直接调用该接口,传入**已选客户档案**中的 `certificateNumber`/
`customerName` 作为 `idNo`/`name`(而非身份证 OCR 识别出的值);响应中 `fileNo` 即识别通过后
可直接用于 `create-draft``fileList`(`fileType=04`)的文件编号,识别与文件存档一步完成,
不再调用通用的 `upload-file` 接口做二次上传。**遗留缺口**:响应中另有 `recode`/`recodeInfo`
两个字段(经后端工程师确认用于承载识别结果码/说明),但具体枚举取值(如成功码是什么、失败时
各失败原因对应的码值)未提供文档,前端 `src/components/FacePhotoUpload.vue` 暂**只按"响应
`data.fileNo` 是否非空"判断识别是否通过**(有 `fileNo` 视为通过,直接用 `recodeInfo` 文本
展示给用户;无 `fileNo` 视为失败,同样用 `recodeInfo`/`message` 展示原因),未对 `recode`
做任何分支判断。建议后端补充 `recode` 的完整枚举取值表,前端将据此细化不同失败原因的提示
文案(例如"人脸与身份证不符" vs "照片质量过低"等更精确的引导)。另外,选择/重新选择客户后,
前端会清空之前已识别通过的人脸照片(`facePhotoFileNo` 置空并强制重新拍摄/上传),因为该
`fileNo` 是绑定"上一次识别时传入的 idNo/name"生成的,客户变更后不应继续复用。
88. **开户协议(截图中的两个协议链接)无后端字段与真实文本/URL**:`create-draft` 的 `fileList`
虽支持 `fileType=13`(开户协议文件),但该字段语义是"上传已签署的协议文件"而非"展示协议
条款文本供客户勾选同意",且后端未提供任何协议文本内容或跳转链接。前端"勾选后才允许点击
确认"的强制项已实现,但协议链接点击后弹出的说明文字为**前端占位文案**,并非真实的《个人
钱包开户协议》等法律文本,亦不随表单提交(`create-draft` 无对应字段)。建议产品侧确认协议
正式文本/链接后由前端替换占位内容,不涉及接口契约变更。
89. **民族(`ethnic`)字段本次改为只读下拉框展示,但未维护完整的56民族码表**:选中客户后从
`/api/customer-info/personal/detail` 响应的 `ethnicity` 字段回填的民族文本,直接作为该
下拉框唯一选项展示(不可编辑、不可选择其他民族),纯粹是为了在视觉上匹配截图"下拉框"样式,
**不代表前端已具备标准民族编码字典**。若后续其他表单需要"新增/编辑民族"的下拉选择能力,
需后端补充标准民族码表。
90. **职业(`occupation`)字段本次改为与"新增个人客户"页一致的 `OCCUPATION_TYPE_OPTIONS`
下拉选择(此前个人开户表单该字段为自由文本框)**:该码表在 Part 7 第74条已记录为前端临时
拟定的占位枚举,非后端真实标准职业分类码表,本条仅说明"个人开户"页面现已复用该占位码表,
并非本次新增独立缺口;选择"其他"时新增的 `jobNote`(职业备注,选"其他"时必填)字段本身在
`create-draft` swagger 中已有文档定义,并非前端新增虚构字段。
---
## Part 11:钱包管理新增"个人开户申请"列表页(比照企业开户申请) —— 2026-07-23 补充
> 产品侧要求"钱包管理"下比照已有的"企业开户申请"页面,新增一个独立的"个人开户申请"列表页
> (`/wallet/personal-open`,原路径改为列表页入口,新增/编辑/查看拆分为 `/create`、`/edit`、
> `/detail` 三个子路由),支持按申请编号/客户姓名/证件号/申请状态/创建时间/所属机构查询,并提供
> 查看详情、编辑草稿(仅 `DRAFT` 状态)入口。但当前 swagger(`personal-opening` 分组)只有
> `create-draft`/`query-customer`/`query-progress`/`mobile/*`,**没有对应企业开户
> `enterprise-open-application/{list,detail,update}` 三件套的个人开户版本**,前端已按下述假设
> 契约完成开发,mock-server 亦已同步实现,均需后端确认或补充真实接口。**此外(2026-07-23 排查
> 补充)本页面涉及的菜单可见性问题不是接口契约缺口,而是后端权限字典/角色绑定的数据缺口,
> 详见第92条**,两者需后端配合按顺序处理(先接口、后菜单)。
91. **个人开户新增 `list`/`detail`/`update` 三个接口均由后端缺失,前端参照企业开户申请
(`enterprise-open-application/{list,detail,update}`)的契约形状自行假设实现**:
- `POST /api/wallet-management/personal-opening/list`:假设请求体含
`applicationNo`/`customerNameLike`(客户姓名模糊)/`certificateNumber`/`applicationStatus`/
`startDate`/`endDate`/`page`/`pageSize`/`orgId`,响应 `{total,page,pageSize,records}`,
`records` 内每项含 `applicationNo`/`customerName`/`certificateNumber`/`mobilePhone`/
`mainAccountNo`(开户成功后的主账户账号,未开立成功前为空)/`channelNo`/
`applicationStatus`/`failReason`/`createdAt`/`updatedAt`。**2026-08-07 起列表页不再展示
银行卡号,改为展示 `mainAccountNo`**,与 `enterprise-open-application/list`
"主账户账号"列口径保持一致。
- `POST /api/wallet-management/personal-opening/detail`:假设请求 `{applicationNo}`,响应
字段在 `create-draft` 请求体(`accountProperty`/`customerId`/`certificateType`/
`certificateNumber`/`certificateEffectiveDate`/`certificateExpiryDate`/`issuingAuthority`/
`certificateAddress`/`gender`/`ethnic`/`occupation`/`jobNote`/`mobilePhone`/
`bankCardNumber`/`fileList` 等)基础上,补充开户结果相关字段
`applicationStatus`/`failReason`/`mainAccountNo`/`customerNo`/`createdAt`/`updatedAt`。
- `POST /api/wallet-management/personal-opening/update`:**与企业开户申请第23条同类问题**——
由于 `create-draft` 本身不返回数字 `id`(只返回 `applicationNo`),前端假设 `update` 接口
`applicationNo`(字符串)而非数字 `id` 定位记录,且仅允许 `DRAFT` 状态的记录可编辑提交;
若后端实际接口要求数字 `id` 定位,或对已提交(非 `DRAFT`)状态的记录有不同的编辑限制,
需后端确认后前端同步调整。**证件照片字段名与 `create-draft` 不同**:`create-draft` 用
`fileList`,而 `update` 后端要求用 `accountOpenApplicationFileBtoList`(2026-07-23 已按
后端要求调整,`PersonalOpenForm.vue` 提交 update 时使用该字段名,mock-server 内部仍归一存回
`fileList` 以保证 `detail` 回显一致)。
- 上述三个接口 mock-server 已在 `mock-server/routes/wallet.js` 中同步实现(逻辑对齐
`enterprise-open-application` 同名接口的简化实现,详见该文件),仅用于本地演示,不代表
真实后端契约,后端接口到位后需删除对应 mock 逻辑并以真实响应为准。
92. **"个人开户申请"菜单在真实后端环境完全不可见,需后端在权限字典中补充菜单节点并绑定角色
(2026-07-23 排查确认,非前端 bug)**:本项目的菜单树**完全由后端 `GET /api/auth/user/
permission-detail` 接口驱动**(前端 `src/utils/permissionTreeBuilder.js` 只做按
`permissionType==='PAGE'` 过滤 + 按 `parentId` 建树,无任何本地白名单/枚举清单兜底),前端
路由映射(`src/router/componentRegistry.js` 里 `/wallet/personal-open` 的组件映射)已就绪,
但**真实后端的权限字典里从未创建过 `routePath='/wallet/personal-open'` 这个 PAGE 权限节点**
(历史原因:阶段4最初设计"个人开户"是从账户列表页"开户"弹窗跳转的非菜单路由,详见
`docs/superpowers/specs/2026-07-09-phase4-wallet-management-design.md`;本次 Part 11 才改造
为独立菜单页,但改动仅停留在前端代码,未同步给后端)。需后端/运维在权限管理体系中新增一条
记录并绑定到相应角色,字段参照如下(与"企业开户申请"节点保持同级同构):
- `permissionType`: `PAGE`
- `permissionName`: 个人开户申请
- `routePath`: `/wallet/personal-open`
- `parentId`: 与"企业开户申请"(`/wallet/enterprise-open`)节点相同的父节点(即"钱包管理"
一级菜单在生产权限字典中的真实 id,需在生产"权限管理"页面查看后填入,本文档不臆造具体
数值)
- `level`/`sortOrder`: 与"企业开户申请"节点同级,建议排在其后(sortOrder 更大)
- 创建后需在"角色管理"页面为需要使用本功能的角色勾选该权限节点并保存,否则即使权限字典
里存在该记录,未绑定角色的用户仍看不到菜单。
- **强烈建议此步骤在本条目 91 所述的 `list`/`detail`/`update` 三个真实接口确认或补充完成
之后再执行**:若先补齐菜单可见性、后端接口仍缺失,用户点击进入列表页会直接调用不存在的
接口报错,体验上比"看不到入口"更差。
## Part 12:个人开户移动端H5确认页(`fryl_h5` 独立工程) —— 2026-07-27 补充
> 见 `docs/superpowers/specs/2026-07-27-personal-open-mobile-h5-design.md`。客户扫码后在移动端
> H5(独立子工程 `./fryl_h5`,与本仓库前端管理后台完全隔离部署)核对开户信息并提交,已复用后端
> 现有的 `personal-opening/mobile/draft-detail`/`mobile/submit` 接口,但存在以下2处前端假设契约,
> 需后端确认或补充。
>
> **⚠️ 2026-07-31 更新**:后端已提供完整 swagger(`swagger_project_scfs_2026-07-31_09-25-57.json`),
> 第93条记录的字段假设已基本被后端真实字段确认(仅字段命名与预期略有差异),第95条"人脸对比无
> 接口"已过期——后端新增了真实的人脸识别接口。详见 Part 15,以下第93-95条保留作历史记录,
> 阅读时请优先参考 Part 15 的最新结论。
93. **`mobile/draft-detail` 响应 `MobileDraftDetailVo` 字段不足,前端假设扩展**:当前 swagger 定义
只有 `customerName`/`certificateType`/`certificateNumber`/`gender`/`certificateAddress` 5个
字段,无法支撑移动端"信息核对"页所需的完整展示(需与管理后台"个人开户申请详情"页展示字段
对齐)。前端假设后端补充以下字段,命名参照 `create-draft` 请求体/ 已假设的
`personal-opening/detail` 响应字段:`certificateEffectiveDate`(证件有效期起)、
`certificateExpiryDate`(证件失效日期)、`issuingAuthority`(签发机关)、`ethnic`(民族)、
`occupation`(职业)、`jobNote`(职业备注)、`bankCardNumber`(银行卡号)、`bankCardName`
(**新增字段名假设**,银行卡户名)、`channelNo`(**新增字段名假设**,渠道编号,申请单创建时
已确定,用于发码接口)、`mobile`(**新增字段名假设**,手机号明文,2026-07-28 起改为返回明文,
不再要求后端做掩码计算——前端持有明文仅用于调用发码接口,页面展示时前端自行计算掩码,不
在 UI 渲染明文,详见
`docs/superpowers/specs/2026-07-28-personal-open-mobile-h5-send-verify-code-api-change-design.md`;
此前假设的 `mobilePhoneMask` 掩码字段已废弃)。mock-server 已在
`mock-server/routes/personalOpenMobile.js``personal-opening/mobile/draft-detail` mock
实现中同步返回上述扩展字段。
94. **短信验证码发送接口 —— 后端已提供正式实现,前端已按新契约对接(2026-07-28 更新)**:后端
已提供 `PersonalOpeningMobileController.sendVerifyCodeGeneral` 正式接口:
`POST /api/wallet-management/personal-opening/mobile/send-verify-code/general`,请求体
`{channelNo, tradeNo, tradeType, mobile, cardNo, cardName, idNo}`,响应体为标准
`{code, message, data:null}`。其中 `tradeType` 全局固定 `"221506"`,`tradeNo` 按业务场景
区分(个人开户固定 `"222101"`),均由前端硬编码补充,不依赖页面数据;`mobile`/`channelNo`
取自 `mobile/draft-detail` 响应新增字段(见第93条),`cardNo`/`cardName`/`idNo` 分别对应
`bankCardNumber`/`bankCardName`/`certificateNumber`。此前假设的
`POST .../mobile/send-verify-code`(请求体仅 `{applicationNo}`)接口已废弃,不再调用。
mock-server 已在 `mock-server/routes/personalOpenMobile.js` 同步实现该 mock 路由(复用
`mock-server/state.js``genSmsCode`/`verifySmsCode` 工具,以 `mobile` 作为 key,`submit`
路由的验证码校验 key 同步改为 `mobile`)。
95. **"人脸对比"环节完全无对应后端接口,本次仅实现前端UI占位**:移动端H5确认页流程含"人脸对比"
步骤(拍照+本地预览),但后端未提供任何人脸活体检测/比对相关接口(`mobile/submit` 只接受
`{applicationNo, verifyCode}`,不含人脸图片/比对结果字段)。前端本次只实现拍照UI交互,不
调用任何比对接口、不上传照片、不影响提交流程,待后端明确该环节的真实接口契约后再补充对接。
---
## Part 15:个人开户移动端H5 对接真实字段与人脸识别接口 —— 2026-07-31 补充
> 见 `docs/superpowers/specs/2026-07-31-personal-open-mobile-h5-face-recognize-and-field-sync-design.md`。
> `swagger_project_scfs_2026-07-31_09-25-57.json` 显示 `mobile/draft-detail` 已由后端补齐真实
> 字段,且新增了真实的人脸识别接口,H5 侧"人脸对比"步骤已从 UI 占位改为真实调用。以下为与
> Part 12 第93-95条的差异及仍未解决的契约缺口。
96. **`MobileDraftDetailVo` 已由后端补齐真实字段,但字段命名与第93条前端假设不完全一致**:
真实字段为 `customerName`/`certificateType`/`certificateNumber`/`certificateEffectiveDate`/
`certificateExpiryDate`/`issuingAuthority`/`gender`/`certificateAddress`/`occupation`/
`occupationNote`(**不是** 第93条假设的 `jobNote`)/`mobilePhone`(**不是** 假设的
`mobile`)/`mobilePhoneMask`(**后端仍直接提供掩码字段**,与2026-07-28"后端不再做掩码计算"
的判断相反,前端已改回直接使用该字段展示,不再自行调用掩码工具)/`bankCardNumber`/`ethnic`。
前端(`InfoConfirmStep.vue`/`PersonalOpenMobile.vue`)已按真实字段名调整。
97. ~~**`channelNo`/`bankCardName` 两个字段仍不存在,遗留契约缺口(沿用第93条假设模式)**~~——
**2026-08-06 更新:`channelNo` 缺口已解决**,`MobileDraftDetailVo` 已补充 `channelNo` 字段
(见 `swagger_project_scfs_2026-08-05_15-15-22.json`),`send-verify-code/general` 的
`channelNo` 已从 `draft-detail` 响应取值传入,不再固定传空字符串;真实后端联调时确认此前
`channelNo` 传空导致报错`渠道编号[channel_no]不能为空`。`bankCardName` 仍无独立字段,
前端改用 `customerName`(客户姓名)代替传给 `cardName` 参数,个人账户场景卡户名与客户姓名
一致,暂无需后端补充。`face-recognize` 请求体的 `channelNo` 因该步骤当前已从 H5 流程跳过
(见相关设计文档"步骤3"说明),暂未受此次修复影响,仍固定传空字符串,留待该步骤恢复时一并
处理。
以下为原始记录,仅作历史参考:
~~`send-verify-code/general` 请求体的 `channelNo`/`cardName` 两个参数、`face-recognize`
请求体的 `channelNo` 参数(该接口把 `channelNo` 标记为必填),均因 `MobileDraftDetailVo` 无
对应字段而无来源。前端暂固定传空字符串 `''`,不阻塞流程。**风险**:若后端 `face-recognize`
接口对空 `channelNo` 做强制校验拒绝,人脸识别环节会持续失败,阻塞整个H5流程;
`send-verify-code/general` 的 `cardName` 若被凡荣e链短信接口强校验也有类似风险。需要后端
确认这两个参数在 H5 匿名场景下允许为空,或在 `MobileDraftDetailVo` 补充对应字段。~~
97-1. **前端曾把 `tradeNo`/`tradeType` 两个固定值参数写反,且 `tradeType` 超长,已于 2026-08-06
修正,`tradeType` 后又二次修正**:真实后端联调时报错`短信类型[trade_type]字段长度超出`。按
swagger `SendVerifyCodeGeneralParamVo` 的字段描述("`tradeNo`固定221506"),已修正为
`tradeNo: '221506'`(此前误写成 `'222101'`)。`tradeType` 先临时改成 `'1'`(此前误写成
`'221506'`,超长),产品/后端随后确认正确取值应为 `'0'`,已二次修正为 `tradeType: '0'`
`tradeType` 的具体业务含义(短信类型枚举)未在 swagger 中文档化,当前只有 `'0'` 是已验证
可用的取值,如后续个人开户场景需要区分不同短信类型,需后端提供枚举说明。
97-2. **`send-verify-code/general``tradeType='0'` 场景要求 `cardNo`/`cardName`/`idNo`
三者必须同时非空,否则报错`该短信类型银行卡号、户名、证件号码或者电子账户、金额必须
填写`**:前端已从 `MobileDraftDetailVo``bankCardNumber`/`customerName`/
`certificateNumber` 取值传入(`cardName` 无独立字段,用 `customerName` 代替,见第97条),
并在发码前新增了这三个字段的非空前置校验(为空直接弹窗提示,不发起请求),但**该校验只能
拦截"确实拿不到值"的情况,不能保证后端真实校验规则与"三者都填"完全一致**(比如是否要求
`idNo` 必须是明文而非打码值,尚待第115条脱敏问题解决后进一步验证)。
98. **人脸识别接口 —— 后端已提供真实接口,"人脸对比"步骤已从UI占位改为真实调用**:后端已提供
`POST /api/customer-info/personal/face-recognize`(凡荣e链222005接口透传),与主项目管理
后台"个人开户"表单 `src/components/FacePhotoUpload.vue`/`src/api/customer.js` 调用的是同一
接口。请求体 `{fileData(人脸照片base64,字段描述含"zip"但经主项目对接确认实际直传图片
base64即可)/idNo/name/channelNo}`,`idNo`/`name` 取自 `draft-detail` 已返回的
`certificateNumber`/`customerName`。响应 `FaceRecognizeResultVo`:`fileNo`/`recode`/
`recodeInfo`/`sysSerialNo`/`sysDate`,前端沿用主项目现状,只按 `fileNo` 是否非空判断识别
是否通过,不对 `recode` 做枚举分支(枚举未文档化,与 Part 10 第87条记录的遗留缺口相同)。
`mobile/submit` 接口描述"影像资料由后端从本地查询",请求体仍只有
`{applicationNo, verifyCode}`,故人脸识别结果(`fileNo`)不需要、也没有字段可以透传给
`submit`——"人脸对比"步骤在 H5 的作用被明确为"识别通过才允许进入下一步"的前置校验关卡。
99. **匿名访问 `face-recognize` 的可行性未经真实后端环境验证**:该接口此前只被已登录的管理
后台调用(带 `Authorization`),H5 侧本次调用不带该请求头(与其余 H5 匿名接口一致的约定)。
本地无法连接真实后端环境验证 Spring Security 是否已将该接口路径纳入匿名白名单,若未纳入,
H5 场景下调用会返回401。需要后端配合确认或调整安全配置。
100. **mock-server 路由挂载顺序缺陷,已顺带修复(不是后端契约问题,记录仅为避免回归)**:
`mock-server/routes/customer.js`/`user.js`/`role.js`/`baseConfig.js`/`wallet.js`/
`invoice.js`/`payment.js`/`order.js`/`merchant.js`/`credit.js` 均用
`router.use(requireAuth)`(不带路径)在文件顶部挂鉴权中间件,这种写法会拦截"该 router 挂载
前缀下的任意路径",而不仅是该文件里定义的具体路由。这些 router 此前在 `index.js` 里均以
`app.use('/api', xxxRouter)` 形式挂载,且顺序早于同样挂载在 `/api` 前缀下、不需要鉴权的
`personalOpenMobileRouter`,导致 H5 匿名接口在本地 mock 环境下全部被误拦截返回401(已实测
复现)。已将 `personalOpenMobileRouter` 与新增的 `faceRecognizeRouter`(`face-recognize`
的匿名 mock)调整到 `index.js` 中所有"挂载在通用 `/api` 前缀且内部用 `router.use(requireAuth)`
拦截"的 router 之前(挂载在具体子路径前缀如 `/api/auth`、`/api/organization` 的 router 不受
影响,不需要调整顺序)。
---
## Part 13:钱包管理"企业开户"表单按最新原型截图改版 —— 2026-07-28 补充
> 产品侧提供了"钱包管理 > 企业开户"页面的最新原型截图,`EnterpriseOpenForm.vue` 已按截图整体
> 改版,详见 `docs/superpowers/specs/2026-07-28-enterprise-open-application-redesign.md`。同时
> 补充要求:新增个人开户申请、新增企业开户申请选择客户时须按机构(`organizationId`)隔离——
> "个人开户申请"(`PersonalOpenForm.vue`)与企业开户申请中"法定代表人/实际控制人/负责人/经办人/
> 受益人"选择均复用 `PersonalCustomerPicker.vue`,该组件已带机构筛选,符合要求;企业开户申请
> "客户"(企业客户)选择所用的 `WalletCustomerPicker.vue` enterprise 模式本次已补充"所属机构"
> 筛选 UI,实际过滤效果仍依赖第58条已登记的后端改造。本次改版新增的接口缺口记录如下:
96. **`CertificateTypeEnum` 枚举缺少"个体工商户"对应值**:企业开户申请表单新增"证件类型"单选
(营业执照/个体工商户),枚举仅有 `ID_CARD`/`PASSPORT`/`OTHER`/`BUSINESS_LICENSE` 四个取值,
选择"个体工商户"时前端暂沿用 `BUSINESS_LICENSE` 提交 `certificateType`,若后端需要区分,
需补充对应枚举值。
97. **`EnterpriseCustomerDetailVo` 无结构化省市字段,"居住地省市"无法自动回填**:企业开户申请
新增"居住地省市"字段(截图要求),但企业客户详情接口只有整串 `certificateAddress`,无法拆分
出省市编码用于 `RegionCascader` 自动选中,前端改为选客户后留空、由柜员人工手动选择,且该
字段选完后不提交给后端(`CreateEnterpriseAccountOpenBto` 无对应字段承接)。
98. **企业开户申请"查询受益人"按钮无对应后端接口**:原型截图在"营业执照/统一社会信用代码"
输入框旁新增"查询受益人"按钮,期望输入证照号码后自动查询该企业的受益人信息,后端未提供
任何工商信息/受益人反查接口,前端当前点击后仅 `message.info` 提示"该功能待后端接口就绪后
开放",不发起真实请求。
99. **无联行号字典查询接口,"开户银行/网点"三种录入方式均为简化实现**:原型要求"快速/详细/
手动"三种开户行录入方式,项目里没有联行号字典查询接口,前端新建 `BankSelector.vue`:
"快速"仅提供前端硬编码的常见银行名称下拉(不含支行/联行号)、"详细"用省市级联+支行名称
文本拼接、"手动"沿用原有 `bankNo`/`bankName` 两个文本框,三种方式均只产出 `bankName` 文本,
"快速"/"详细"两种方式下 `bankNo` 始终留空。待后端提供联行号字典接口后需重新设计"快速/
详细"两种模式的真实联动查询逻辑。
100. **受益人认定类型"实际控制"的 `actCtrlCpny` 枚举取值与原型截图默认值不一致——2026-07-28
第二轮更新**:swagger `EnterpriseAccountOpenApplicationBeneficiaryBto.actCtrlCpny` 字段说明
为"2协议约定 3其他形式",但产品补充的截图默认选中值显示为数字"1",与文档不符。前端以 swagger
文档为准,"控制类型"下拉仅提供 `2`/`3` 两个选项(不采用截图的"1"),需产品/后端确认截图默认值
"1"是否代表另有未文档化的第三个枚举取值。
101. **受益人材料证明影像资料的 `fileType` 编码待确认**:原型新增第4张"受益人材料证明"影像
上传,`enterpriseAccountOpenApplicationFileBtoList.fileType` 现有已知编码为
12营业执照/01法人身份证正面/02法人身份证反面,均未覆盖该新影像类型,前端暂用占位编码 `'14'`,
需后端确认真实编码或另行分配。
102. **`enterprise-open-application/create`、`update` 请求体无 `verifyCode` 字段**:原型新增
"短信认证"环节(手机号+验证码+发送按钮),但两个接口的 Bto(`CreateEnterpriseAccountOpenBto`/
`UpdateEnterpriseAccountOpenApplicationBto`)均无 `verifyCode` 字段。前端已完整实现发送
验证码 UI 交互(复用 `POST /api/auth/send-sms-code`,`businessType` 约定传
`'WALLET_ENTERPRISE_OPEN'`)并将 `verifyCode` 一并放入提交 payload,但该字段实际不会被
后端接收/校验,验证码环节当前只是前端展示效果,需后端补充该字段后才能真正生效。
103. **受益人"实际控制信息"二级面板的"控制内容"(`actCtrlType`)后端未提供任何枚举——2026-07-28
第二轮补充**:原型截图(截图4)对"控制内容"展示为下拉框样式,但未展示任何可选项,swagger 该
字段(`actCtrlType`)是纯自由文本,无 `$ref` 枚举。前端改用 `a-auto-complete` 自由输入组合框
(无预置选项),不编造枚举,待产品/后端确认是否需要标准化取值列表。
104. **受益人"在华最高层级高管信息"二级面板的"开始日期/结束日期"字段命名不确定——2026-07-28
第二轮补充**:原型截图(截图6)要求"在华最高层级高管"勾选后填写开始/结束日期,但
`EnterpriseAccountOpenApplicationBeneficiaryBto` 中未见该分类专属的日期字段,只有通用的
`benStartDate`/`benEndDate`(受益所有权形成/终止日期-通用)。前端暂将截图的"开始日期/结束
日期"映射到这两个通用字段,若后端认为该通用字段语义应对应其他认定类型或需要新增专属字段,
需重新确认映射关系。
105. **受益人"日常经营管理高管"/"在华最高层级高管"二级面板的"职位"下拉选项直接取自原型截图
(法定代表人/董事长/经理/董事/执行合伙事务的自然人/其他人员;分支机构负责人/其他高级管理
人员),未在 swagger 中找到对应编码枚举——2026-07-28 第二轮补充**:前端直接以截图展示的中文
文案本身作为 `seniorMgrPos`/`seniorMgrPosCn` 的提交值(即 value=label),若后端要求提交标准
编码(如数字代码)而非中文文案,需后端提供编码对照表。
补充说明(非独立缺口,仅记录本次字段裁剪范围变化,2026-07-28 第二轮已修正):受益人信息此前
(阶段4设计)仅采集 `beneName/beneIdNo/beneAddress/beneOpto/beneSex/beneNationality/actCtrl/
actHdRat` 8个字段,本次改版起新增采集 `beneMobile`(联系电话)、`beneBirthday`(出生日期),并
改为支持数组多条受益人(不再是"数组内只放1条")。`actHdRat`(持股比例)**并未被删除**——第一轮
文档曾误判"随受益人认定类型改造被取代不再采集",经产品补充截图2-6纠正:`actHdRat` 及其余
26个反洗钱扩展字段(`shareRatioStartDate`/`profitRatioStartDate`/`actCtrlCpny`/`seniorMgrPos`
等)均已按"5种认定类型各自展开二级面板"的方式实现采集,详见设计文档"五-1"节。提交时仍整体覆盖
`enterpriseAccountOpenApplicationBeneficiaryBtoList`(该数组 `update` 时全量替换 vs 增量的语义
仍未定,沿用第25条已登记的缺口,不重复登记)。
---
## Part 14:基础配置 —— 动态字典管理(全新模块) —— 2026-07-30 新增,2026-07-31 按最新 swagger 全面核对
> 「基础配置」菜单下新增"动态字典维护"页面,用于统一维护全系统下拉框使用的字典数据,支持
> 普通字典(单层取值)与级联字典(可在字典项下继续添加下级,层级不固定,如省市区)。**此前
> 该模块在真实后端 swagger 中完全不存在**,数据库存储建议采用"字典类型表 + 自关联树形字典项
> 表"两张表(普通字典即该类型下所有项 `parentId` 都为顶级,级联字典允许任意深度嵌套),不建议
> 为每种级联字典(省市区/行业分类等)单独建表。
>
> **2026-07-31 更新**:后端已提供完整 swagger(`swagger_project_scfs_2026-07-31_09-25-57.json`,
> `dict_management` 分组,共 11 个接口)。其中「字典类型管理」4 个接口(分页/创建/编辑/删除)
> 契约已完整确认,前端(`src/api/baseConfig.js`、`DictTypeList.vue`、`dictEnums.js`)与
> `mock-server` 均已按真实契约调整,不再是前端占位设计。「字典项管理」6 个接口路径与字段也已
> 按真实契约接入(`DictItemTree.vue`、`fetchDictItemListApi`/`fetchDictItemTreeApi`/
> `createDictItemApi`/`updateDictItemApi`/`deleteDictItemApi`)。其中 `dict-items/list`/
> `dict-items/tree` 的 swagger 声明响应字段不含 `id`,一度导致误判为"无法对已存在字典项做编辑/
> 删除";经 2026-07-31 前端联调实测确认**这两个接口实际响应额外返回了 `id`**(swagger 未声明),
> 前端已按实测字段实现完整的修改/删除/子级新增功能,详见 14.2 节说明。
### 14.1 字典类型管理(已按 2026-07-31 swagger 完整确认)
| 接口 | Body | 说明 |
| --- | --- | --- |
| `POST /api/dict-management/dict-type/page` | `FindDictTypePageQto`:`{dictTypeCodeLike?, dictTypeNameLike?, dictCategoryList?: DictCategoryEnum[], statusList?: DictStatusEnum[], page, pageSize}` | 编码/名称均为模糊搜索(`xxxLike` 后缀),类别/状态为精确过滤且支持多选;响应 `records: DictTypePageVo[]` |
| `POST /api/dict-management/dict-type/create` | `CreateDictTypeBto`:`{dictTypeCode, dictTypeName, dictCategory, remark, status}` | `dictTypeCode` 全局唯一,创建后不可修改;`dictCategory` 创建后不可修改;`status` 可在创建时指定 |
| `POST /api/dict-management/dict-type/update` | `UpdateDictTypeBto`:`{id, dictTypeCode, dictTypeName, status, remark}` | **不含 `dictCategory`**(字典类别创建后不可修改) |
| `POST /api/dict-management/dict-type/delete` | `DeleteDictTypeBto`:`{id, dictTypeCode}` | 该字典类型下还存在字典项时应拒绝删除并返回错误 `message`(前端不做二次确认,直接展示后端 `message`) |
| `POST /api/dict-management/dict-type/detail` | `{dictTypeCode}` | 根据编码查询详情,响应 `DictTypePageVo`;前端目前未使用(列表已含全部字段,暂无详情页需求) |
`DictCategoryEnum`:`NORMAL`(普通字典)/`CASCADE`(级联字典)。
`DictStatusEnum`:`ENABLED`(启用)/`DISABLED`(禁用)——**注意与项目其余模块常见的
`ACTIVE`/`INACTIVE` 不同**。
`DictTypePageVo` 字段:`{ id, dictTypeCode, dictTypeName, dictCategory, status, remark }`
(create/update 接口的响应体也是同一个 Vo)。
### 14.2 字典项管理(路径已确认;swagger 声明字段不完整,已按实测响应校正)
后端提供了以下 6 个字典项相关接口(均为 `dict_management` 分组,均为 POST):
| 接口 | Body | swagger 声明的响应 | 说明 |
| --- | --- | --- | --- |
| `dict-items/list` | `{dictTypeCode}` | `DictItemOptionVo[]`:`{itemValue, itemLabel, sortOrder}` | 查询**普通字典**下所有已启用字典项(扁平列表)。**实测响应额外含 `id` 字段**(2026-07-31 前端联调确认,swagger 未声明) |
| `dict-items/tree` | `{dictTypeCode}` | `DictItemTreeNodeVo[]`:`{itemValue, itemLabel, children[]}` | 查询**级联字典**完整树。**实测响应额外含 `id`/`sortOrder` 字段**(2026-07-31 前端联调确认,swagger 未声明) |
| `dict-items/children` | `{dictTypeCode, parentId}` | `DictItemOptionVo[]` | 查询级联字典某父节点的直接子节点,是否额外含 `id` 尚未实测确认(前端暂未使用该接口) |
| `dict-item/create` | `CreateDictItemBto`:`{dictTypeCode, id?, dictItemBtoList: [{itemValue, itemLabel, parentId, sortOrder, remark}]}` | `DictItemPageVo`(单个) | 请求体按数组设计(可能面向批量),但响应只返回单个 Vo,前端每次仅传 1 个元素 |
| `dict-item/update` | `{id, itemValue, itemLabel, parentId, sortOrder, status, remark, dictType: {id}}` | `DictItemPageVo` | 需同时提供字典项自身 `id` 与所属字典类型的**数字主键 id**(`dictType.id`,不是 `dictTypeCode`) |
| `dict-item/delete` | `{id, dictType: {id}}` | — | 同上,需要 `dictType.id` |
`DictItemPageVo` 字段:`{ id, dictTypeCode, itemValue, itemLabel, parentId, sortOrder, status, remark }`。
**⚠️ swagger 文档与实测行为不一致**:`dict-items/list`/`dict-items/tree` 的 swagger 声明的响应
Vo(`DictItemOptionVo`/`DictItemTreeNodeVo`)只有 `itemValue`/`itemLabel`/`sortOrder`/`children`,
不含 `id`;但 2026-07-31 前端联调时确认**实际响应额外返回了 `id`**(`dict-items/tree` 还额外返回
`sortOrder`)。前端(`DictItemTree.vue`)现在直接使用实际响应中的 `id` 字段来支持对已存在
字典项的修改/删除/子级新增操作,不再有"只读"限制。**这不是标准做法**(依赖了 swagger 未声明的
字段),建议后端后续更新 swagger 文档,将 `DictItemOptionVo`/`DictItemTreeNodeVo` 补充声明
`id` 字段(以及确认 `status` 是否也会返回,目前前端在该字段缺失时默认展示"启用"),避免该字段
在后续版本中被无声移除导致前端功能回退。
前端枚举取值:`DICT_TYPE_CATEGORY_OPTIONS`(`NORMAL`/`CASCADE`)、`DICT_STATUS_OPTIONS`
(`ENABLED`/`DISABLED`)均已通过 2026-07-31 swagger 核对确认,定义在 `src/constants/dictEnums.js`,
字典类型与字典项两个子模块通用。
### 14.3 权限表(`AUTH_PERMISSION`)新增菜单/按钮权限 SQL
新增页面必须在权限表补一条二级页面(level=1)数据,菜单树才能生成对应菜单节点(前端
`routePathComponentMap` 已同步注册 `/base-config/dict``DictTypeList.vue`)。`id` 沿用
`mock-server/db/seedAuth.js` 中已同步的编号(69 页面、70-72 前端实际调用 `v-permission`
按钮、73 补全的"查看"权限,与既有 51-66 号"查看"权限同惯例 `sortOrder=0`)。**注意**:下方
SQL 表名统一使用 `AUTH_PERMISSION`(单下划线),与本文档已提供的 id 1-50 一致;之前登记的
id 51-66 SQL 表名误写成了 `AUTH__PERMISSION`(双下划线),执行前请核对并修正为同一张表。
```sql
-- 二级页面 level=1(基础配置组 parent_id=2 下新增第3个页面节点)
INSERT INTO "SFT"."AUTH_PERMISSION"
("ID", "PERMISSION_TYPE", "PERMISSION_NAME", "PERMISSION_CODE", "PARENT_ID", "PERMISSION_LEVEL", "SORT_ORDER", "ICON", "ROUTE_PATH", "LOCK_VERSION")
VALUES (69, 'PAGE', '动态字典管理', 'menu:page:69', 2, 1, 3, NULL, '/base-config/dict', 0);
-- 三级按钮 level=2(前端 DictTypeList.vue / DictItemTree.vue 已用 v-permission 指令引用)
INSERT INTO "SFT"."AUTH_PERMISSION"
("ID", "PERMISSION_TYPE", "PERMISSION_NAME", "PERMISSION_CODE", "PARENT_ID", "PERMISSION_LEVEL", "SORT_ORDER", "ICON", "ROUTE_PATH", "LOCK_VERSION")
VALUES (70, 'BUTTON', '新增字典', 'dict:add', 69, 2, 1, NULL, NULL, 0);
INSERT INTO "SFT"."AUTH_PERMISSION"
("ID", "PERMISSION_TYPE", "PERMISSION_NAME", "PERMISSION_CODE", "PARENT_ID", "PERMISSION_LEVEL", "SORT_ORDER", "ICON", "ROUTE_PATH", "LOCK_VERSION")
VALUES (71, 'BUTTON', '编辑字典', 'dict:edit', 69, 2, 2, NULL, NULL, 0);
INSERT INTO "SFT"."AUTH_PERMISSION"
("ID", "PERMISSION_TYPE", "PERMISSION_NAME", "PERMISSION_CODE", "PARENT_ID", "PERMISSION_LEVEL", "SORT_ORDER", "ICON", "ROUTE_PATH", "LOCK_VERSION")
VALUES (72, 'BUTTON', '删除字典', 'dict:delete', 69, 2, 3, NULL, NULL, 0);
-- 补充"查看"按钮权限(id 73):仅作权限字典项补全,不对应真实前端按钮,不做前端拦截,
-- sort_order=0 排在同页面其它按钮之前,与既有 id 51-66 同惯例
INSERT INTO "SFT"."AUTH_PERMISSION"
("ID", "PERMISSION_TYPE", "PERMISSION_NAME", "PERMISSION_CODE", "PARENT_ID", "PERMISSION_LEVEL", "SORT_ORDER", "ICON", "ROUTE_PATH", "LOCK_VERSION")
VALUES (73, 'BUTTON', '查看动态字典管理', 'dict:view', 69, 2, 0, NULL, NULL, 0);
```
执行后还需给系统管理员角色补一条角色-权限绑定(`AUTH_ROLE_PERMISSION`,对应
`POST /api/auth/role-permission/bind`),否则页面新增后管理员角色也看不到该菜单;
`mock-server` 中管理员角色(`roleId=1`)已通过 `rolePermissions = permissions.map(...)`
自动绑定全部权限,真实库需手工执行 bind 接口或对应 INSERT。
### 14.4 角色-权限绑定表(`AUTH__ROLE_PERMISSION`)新增绑定 SQL
将上述 5 条新权限(id 69-73)绑定给 admin 角色(`roleId=101`)。`AUTH__ROLE_PERMISSION.ID`
按此前登记的记录延续到 50,以下 SQL 的 `ID` 从 51 起顺序分配——**执行前请先核对真实库里
`AUTH__ROLE_PERMISSION` 当前最大 `ID` 是否确实为 50**(例如权限 id 51-68 的"查看"类权限
若已提前单独绑定过,占用了 51 及之后的 `ID`,则需把下方 `ID` 顺延,避免主键冲突;`ID` 与
`PERMISSION_ID` 是两个独立字段,不要混淆)。
```sql
INSERT INTO "SFT"."AUTH__ROLE_PERMISSION" ("ID", "ROLE_ID", "PERMISSION_ID") VALUES (51, 101, 69);
INSERT INTO "SFT"."AUTH__ROLE_PERMISSION" ("ID", "ROLE_ID", "PERMISSION_ID") VALUES (52, 101, 70);
INSERT INTO "SFT"."AUTH__ROLE_PERMISSION" ("ID", "ROLE_ID", "PERMISSION_ID") VALUES (53, 101, 71);
INSERT INTO "SFT"."AUTH__ROLE_PERMISSION" ("ID", "ROLE_ID", "PERMISSION_ID") VALUES (54, 101, 72);
INSERT INTO "SFT"."AUTH__ROLE_PERMISSION" ("ID", "ROLE_ID", "PERMISSION_ID") VALUES (55, 101, 73);
```
## Part 16:授信管理 —— 贷款申请列表页按最新原型截图(`website_detail/授信管理/贷款申请`)改造 —— 2026-08-02 补充
> 产品侧提供了"授信管理 > 贷款申请"列表页的最新原型截图,查询区要求"机构/渠道/状态/客户名称/
> 钱包编号"五项,列表要求"勾选框/No/客户/钱包/经营实体/类型/申请金额/申请利率/状态/申请时间/
> 渠道/所属机构"十二列。前端已按截图完成查询区与列展示改造(移除了原型未展示但接口本身支持的
> "申请单号/申请类型/创建时间"三项查询条件,如后续产品需要可随时加回),但下述查询参数与响应
> 字段后端目前均不提供,前端暂按"传参但后端可忽略"/"展示但内容为空('-')"方式实现,不编造数据,
> 待后端补充后自动生效。
106. **`POST /api/credit-apply/loan-application/list``FindLoanApplicationListQto` 需扩展
"机构""渠道""钱包编号"三个查询参数**:当前 swagger 定义的请求体仅支持
`loanApplicationId/applicationStatus/applicationType/customerName/createDateStart/
createDateEnd/page/pageSize`(见 Part 1 第 1.12 节),不支持按机构/渠道/钱包账号过滤。原型
截图明确要求这三项作为查询条件,建议后端补充:
- `organizationId`(机构,规则同 Part 3 第 59 条"机构数据隔离"要求,`LoanApplicationVo` 已有
该字段,只差查询入参)
- `channel`(渠道,`LoanApplicationVo` 已有该字段,只差查询入参)
- `walletAccountNo`(钱包编号/客户账号,`LoanApplicationVo` 完全没有该字段,需要先补充
响应字段,见第 107 条)
补充前,前端查询区仍展示这三个筛选框并随请求一并提交,但不会生效(后端会忽略未定义的
入参),不影响其余已支持参数(`applicationStatus`/`customerName`)的筛选结果。
107. **`LoanApplicationVo` 需扩展"钱包账号+账户类型""经营实体""申请时间""机构名称"四组字段**:
当前响应仅有 13 个字段(`id/loanApplicationId/applicationType/applicationStatus/
organizationId/channel/loanProductCode/customerType/customerId/customerName/loanAmount/
createdBy/applicationInterestRate`,见 Part 1 第 1.12 节),原型截图要求展示但当前完全没有
对应字段:
- `walletAccountNo` + `accountType`(钱包账号与账户类型,截图展示为"活期A1 1021260319000026"
这种"账户类型Tag + 账号"组合,参考商户管理/钱包管理里 `accountType` 枚举:A1主账户/A2电商
A2账户/A3电商A3账户/A6保证金账户/A7分户账户)
- 经营实体(截图该列示例数据均为空,业务含义待产品确认,推测为企业贷款申请关联的经营主体
名称,需后端补充对应字段与关联关系)
- 申请时间/创建时间(注意:查询接口已支持 `createDateStart`/`createDateEnd` 按创建时间范围
过滤,但响应体里完全没有对应的时间字段返回,属于"能按时间查询但查不到时间值"的契约内部
不一致,建议后端直接复用查询入参对应的时间字段名一并在 `LoanApplicationVo` 中返回)
- 机构名称(当前只有 `organizationId` 数字,前端已用 `GET /api/organization/list-all`
做 id -> 机构名称映射作为兜底展示,但该接口返回的是全量机构列表,非最优实现,建议后端
直接在 `LoanApplicationVo` 补充 `organizationName` 冗余字段,减少前端二次查询)
补充前,前端列表页"钱包""经营实体""申请时间"三列均展示为"-",不是缺陷;"所属机构"列已可
通过机构列表接口兜底正常展示机构名称。
108. **真实后端(测试/联调环境)权限接口未返回"授信管理"菜单节点,不是接口契约缺失,是权限
数据配置缺失** —— 2026-08-02 补充,与第 106/107 条无关,单独记录:前端顶部导航栏完全
是根据登录后调用的"查询用户权限详情"接口返回的扁平权限列表动态生成(只取
`permissionType === 'PAGE'` 的节点按 `parentId` 建树),不是前端写死的菜单。联调环境
反馈"重新登录后顶部导航栏仍完全没有'授信管理'字样",说明该环境的权限字典表里可能:
- 根本没有插入"授信管理"(一级分组)和"贷款申请"(二级页面,`routePath` 应为
`/credit/loan-application`)这两条 `AUTH_PERMISSION` 记录;或
- 记录存在,但未绑定给当前联调测试账号所属的角色(`AUTH__ROLE_PERMISSION` 缺对应绑定)。
本地 `mock-server`(`mock-server/db/seedAuth.js` 第 59/78 行)里这两条节点是完整存在
且已绑定给唯一的 admin 角色的,可作为节点结构参照:一级"授信管理"(`permissionType='PAGE'`,
`parentId=null`,无 `routePath`),二级"贷款申请"(`permissionType='PAGE'`,`parentId`
指向"授信管理"节点,`routePath='/credit/loan-application'`)。请后端/运维核对真实库
`AUTH_PERMISSION`/`AUTH__ROLE_PERMISSION` 表,确认这两条记录是否存在及是否已绑定角色,
参照 Part 14.3/14.4 的 SQL 写法补充(具体 `ID` 需以真实库当前最大值顺延分配,不能直接
沿用 mock 的 id=5/19)。前端代码本身("贷款申请"页面已按 Part 16 完成改造、路由已在
`componentRegistry.js` 注册)无需改动,补齐权限数据并重新登录后应自动生效。
**执行步骤(务必先跑第0步诊断查询,确认现状后再决定要不要插入,避免重复插入或 ID 冲突;
下方 SQL 中 `<...>` 均为占位符,需替换成诊断查询得到的真实值后再执行)**:
0. 先诊断当前真实库里到底缺的是"节点"还是"绑定":
```sql
-- 0a. 确认"授信管理"/"贷款申请"这两条 AUTH_PERMISSION 记录是否已存在(存在则跳过第1步,
-- 只需检查/补第3步的角色绑定;不存在才需要执行第1、2步插入)
SELECT * FROM "SFT"."AUTH_PERMISSION"
WHERE "PERMISSION_NAME" IN ('授信管理', '贷款申请') OR "ROUTE_PATH" = '/credit/loan-application';
-- 0b. 若确实不存在,查当前最大 ID,后续两条新记录的 ID 从 (最大值+1)/(+2) 顺序分配
SELECT MAX("ID") FROM "SFT"."AUTH_PERMISSION";
-- 0c. 查现有一级模块(level=0)的 SORT_ORDER,确定"授信管理"插入到导航栏第几位
-- (原型截图里"授信管理"排在"首页"之后、"支付管理"之前,建议 SORT_ORDER 取"客户管理"
-- 与"支付管理"现有值之间的数,或直接排在末位,与产品确认最终顺序即可)
SELECT "ID", "PERMISSION_NAME", "SORT_ORDER" FROM "SFT"."AUTH_PERMISSION"
WHERE "PERMISSION_LEVEL" = 0 ORDER BY "SORT_ORDER";
-- 0d. 查当前联调测试账号(如 admin)绑定的角色 ID,以及该角色已绑定的最大
-- AUTH__ROLE_PERMISSION.ID(用于第3步顺延分配新绑定记录的 ID)
SELECT "ID", "ROLE_ID", "PERMISSION_ID" FROM "SFT"."AUTH__ROLE_PERMISSION" ORDER BY "ID" DESC LIMIT 5;
```
1. 若第0a步确认两条记录均不存在,插入"授信管理"一级分组(`PERMISSION_LEVEL=0`,
`PARENT_ID` 为空,不挂 `ROUTE_PATH`;`<GROUP_ID>` 取第0b步 `MAX(ID)+1`,`<GROUP_SORT>`
取第0c步核对后的顺序值):
```sql
INSERT INTO "SFT"."AUTH_PERMISSION"
("ID", "PERMISSION_TYPE", "PERMISSION_NAME", "PERMISSION_CODE", "PARENT_ID", "PERMISSION_LEVEL", "SORT_ORDER", "ICON", "ROUTE_PATH", "LOCK_VERSION")
VALUES (<GROUP_ID>, 'PAGE', '授信管理', 'menu:group:<GROUP_ID>', NULL, 0, <GROUP_SORT>, NULL, NULL, 0);
```
2. 插入"贷款申请"二级页面(`PERMISSION_LEVEL=1`,`PARENT_ID` 指向上一步的 `<GROUP_ID>`,
`ROUTE_PATH` 必须是 `/credit/loan-application`,与前端 `componentRegistry.js`
`routePathComponentMap` 的 key 完全一致才能被正确解析;`<PAGE_ID>` 取 `<GROUP_ID>+1`):
```sql
INSERT INTO "SFT"."AUTH_PERMISSION"
("ID", "PERMISSION_TYPE", "PERMISSION_NAME", "PERMISSION_CODE", "PARENT_ID", "PERMISSION_LEVEL", "SORT_ORDER", "ICON", "ROUTE_PATH", "LOCK_VERSION")
VALUES (<PAGE_ID>, 'PAGE', '贷款申请', 'menu:page:<PAGE_ID>', <GROUP_ID>, 1, 1, NULL, '/credit/loan-application', 0);
```
3. 给需要看到该菜单的角色(联调测试账号所属角色,`<ROLE_ID>` 取第0d步查到的
`ROLE_ID`)补两条 `AUTH__ROLE_PERMISSION` 绑定记录(`<BIND_ID_1>`/`<BIND_ID_2>` 取
第0d步 `MAX(ID)+1`/`+2`;若第0a步发现记录本就存在、只是缺绑定,则把 `<GROUP_ID>`/
`<PAGE_ID>` 换成查出来的真实已存在 ID,只执行这一步即可,不必执行第1、2步):
```sql
INSERT INTO "SFT"."AUTH__ROLE_PERMISSION" ("ID", "ROLE_ID", "PERMISSION_ID") VALUES (<BIND_ID_1>, <ROLE_ID>, <GROUP_ID>);
INSERT INTO "SFT"."AUTH__ROLE_PERMISSION" ("ID", "ROLE_ID", "PERMISSION_ID") VALUES (<BIND_ID_2>, <ROLE_ID>, <PAGE_ID>);
```
执行后重新登录联调测试账号,顶部导航栏应出现"授信管理",点开可见"贷款申请"。
## Part 17:个人开户移动端H5"信息核实"页按最新原型截图改版 —— 2026-08-03 补充
> 产品侧提供了`fryl_h5`工程"信息核实"页(`InfoConfirmStep.vue`)的最新原型截图,要求在原有开户
> 信息明细之上新增一张"信息确认"摘要卡片(客户名称/证件号码/手机号/账号/业务类型/办理渠道),
> 并把"下一步"按钮文案改为"确认无误,下一步",新增"更改信息""取消申请"两个操作入口。前端已
> 按截图完成改版,原有全部开户信息明细字段保留(挪到摘要卡片下方的"更多开户信息"分组),
> 不影响任何已提交字段的取值逻辑。
109. **"业务类型""办理渠道"为前端固定文案,不是后端字段,`MobileDraftDetailVo` 无需新增
对应字段**:本 H5 工程当前只服务"个人账户开立·线上"这一单一场景(唯一路由
`/mobile/personal-open`,唯一入口是柜员在"个人开户申请"创建草稿后生成的二维码),不存在
业务类型/办理渠道会变化的情况,故前端直接写成常量`'个人账户开立'`/`'线上'`展示,不从
`draftDetail` 读取,也不需要后端补充字段。若后续新增企业开户或其它办理渠道(如线下柜台
录入生成的确认链接)的移动端确认页,需要重新评估是否要改为后端可配置字段。
110. ~~"取消申请"没有对应的后端接口,当前仅做前端终态展示,不发起任何请求~~——**2026-08-05
更新:该功能已按产品要求整体移除**(顶部"×"图标、底部"取消申请"链接及对应的确认弹窗/
本地终态展示均已删除),不再存在此前端行为,以下遗留说明仅作历史记录,后端**不需要**
再为其补充接口:
~~点击信息核实页顶部"×"图标或底部"取消申请"链接,弹出确认对话框,确认后仅把页面切到
"已取消本次开户申请"的本地终态(类似已有的"错误态"展示),不调用任何接口,也不会让草稿
(`applicationNo`)在后端产生任何状态变化。~~
111. ~~"更改信息"没有对应的后端接口,当前仅弹窗提示联系柜员,不进入任何可编辑状态~~——
**2026-08-05 更新:该功能已按产品要求整体移除**(底部"更改信息"链接及对应提示弹窗已
删除),以下遗留说明仅作历史记录,后端**不需要**再为其补充接口:
~~草稿数据(`MobileDraftDetailVo`)由柜员在管理后台的"个人开户申请"创建/编辑,H5 侧设计
上就是只读确认,点击"更改信息"目前只弹出提示"如需修改开户信息,请联系办理柜员在管理
后台重新生成申请",不跳转、不修改任何本地状态。~~
## Part 18:个人开户移动端H5"相关协议"步骤独立并接入真实协议模板接口 —— 2026-08-05 补充
> 产品侧提供了"业务确认"最新原型截图,要求把原来合并在"信息核对"步骤里的协议勾选拆分为独立的
> "相关协议"步骤,且该步骤需要展示协议原文(而非占位文案)。后端已新增
> `GET /api/customer-info/bank-agreement/base64` 接口(见 `swagger_project_scfs_
> 2026-08-05_15-15-22.json`),前端已按此接口完成对接,详见
> `docs/superpowers/specs/2026-07-27-personal-open-mobile-h5-design.md` 的 2026-08-05 更新记录。
112. **`BankAgreementFileTypeEnum` 共4项,`fryl_h5`"相关协议"步骤只用到其中2项**:枚举值为
`PAY_SERVICE_AGREEMENT`(用户支付服务协议)/`PAY_SERVICE_PRIVACY`(用户支付服务隐私政策)/
`CREDIT_REPORT_AUTH`(个人信用信息报送查询使用授权书)/`THIRD_PARTY_DATA_AUTH`(第三方数据
信息查询和使用授权书)。个人开户H5场景与产品确认后仅展示前2项,与最新原型截图一致;
后2项当前无使用场景(疑似服务于授信/贷款相关流程),暂不在任何前端页面接入,如后续贷款
申请等模块需要,需要产品明确具体使用场景后再补充对接。
113. **该接口无请求参数校验以外的业务缺口,已完整对接,仅记录一个已知的本地 mock 简化点**:
mock-server 的 `GET /customer-info/bank-agreement/base64` 用本地占位 PDF(非正式协议文本)
模拟返回,仅用于验证前端 iframe 展示 PDF 的效果,真实条款内容以后端联调环境返回的正式
模板文件为准。
114. **人脸识别步骤(`FaceCompareStep.vue`)自 2026-08-05 起从 `fryl_h5` 向导流程中跳过,
`POST /api/customer-info/personal/face-recognize` 暂不再被本 H5 页面调用**:客户完成
"相关协议"确认后直接进入"手机验证+提交"步骤,不再拍照/识别。该组件与接口封装代码均未
删除(按产品要求仅从流程中跳过,便于后续随时恢复),后端**不需要**因此下线或调整
`face-recognize` 接口(管理后台 `src/components/FacePhotoUpload.vue` 仍在使用同一接口)。
115. **待确认:真实后端环境下某条测试申请单的 `draft-detail` 响应,`channelNo` 缺失、
`mobilePhone``null`,且 `bankCardNumber`/`certificateNumber` 疑似已是打码值** ——
2026-08-06 真实后端联调时,`send-verify-code/general` 报错`渠道编号不能为空`+`手机号不能
为空`,抓包发现请求体里 `channelNo` 整个 key 缺失(说明来源 `draftDetail.channelNo`
`undefined`)、`mobile` 字面值是 `null`,而 `cardNo`/`idNo` 却是类似 `6210****6009`/
`3*************7723` 的打码格式——这与第96/97条基于 swagger 假设的"`bankCardNumber`/
`certificateNumber` 是明文、`channelNo`/`mobilePhone` 应该有值"不一致。**尚未确认**是
"这条测试申请单本身数据缺失"(比如创建时未选渠道/未录手机号)还是"后端对
`draft-detail` 接口统一做了脱敏处理,明文字段实际都拿不到"。如果是后者,需要后端说明
`send-verify-code/general``mobile` 参数在拿不到明文手机号的情况下应该怎么传(比如
改传掩码值,或后端内部改为按 `applicationNo`/`idNo` 反查,不再需要客户端传明文
`mobile`)。前端已临时加了发码前的 `mobile`/`channelNo` 非空校验(缺失时弹窗提示"请联系
柜员核实"并阻止请求),避免带着必然失败的参数发请求,但**不能**从根本上解决数据/脱敏
问题,需要后端确认后再对应调整。
---
## Part 19:个人开户移动端H5"相关协议"步骤新增PDF自动填充+上传,`mobile/submit` 新增
`payAgreementFileNo` 字段 —— 2026-08-06 补充
> 产品要求:用户在"相关协议"步骤勾选两个协议 checkbox 后,前端自动在《用户支付服务协议》
> PDF模板末页的空白签名区域(见模板原文"用户:/证件号码:/时间: 年 月 日")填入客户姓名/
> 证件号码/当前日期,调用通用文件上传接口拿到文件编号,提交开户时随 `mobile/submit` 一起传给
> 后端。前端实现细节(PDF坐标测量方法、中文字体渲染的两个已知 bug 及规避方式)见
> `docs/superpowers/specs/2026-08-06-agreement-pdf-fill-and-upload-design.md`,本节只记录与
> 后端接口契约相关的部分。
116. **`mobile/submit` 新增 `payAgreementFileNo` 字段 —— 尚无更新版 swagger 佐证,依据是产品/
后端口头确认,需要后端补充到正式接口文档后再次核对字段名拼写与类型**:截至本次改动,
仓库里最新的 `swagger_project_scfs_2026-08-05_15-15-22.json` 显示 `mobile/submit` 请求体
仍只有 `{applicationNo, verifyCode}`(与第98条记录的"影像资料由后端从本地查询"描述一致),
没有任何字段可以传"协议PDF文件编号"。前端本次按产品口头确认的字段名
`payAgreementFileNo`(string,可选)直接对接,**这是一个尚未在 swagger 中体现的契约缺口**,
一旦后端更新正式接口文档,需要重新核对:(1) 字段名拼写是否与本次假设一致;(2) 是否为
必填(前端目前当作可选传参,值来自本次新增的PDF生成+上传流程,理论上"相关协议"步骤通过
后必定非空,但后端如果做了必填校验,需要确认空值场景如何处理,比如用户使用老版本H5缓存
导致该字段为空的兜底方案);(3) 后端拿到该文件编号后的实际用途(是否需要真正归档校验
文件内容,还是仅留痕)。
117. **只有《用户支付服务协议》一份文件需要走"填充+上传"流程,《用户支付服务隐私政策》不
需要**:产品确认隐私政策本身不需要收集客户签名信息,勾选后仍走原有的"仅本地记录已勾选
状态"逻辑,不调用 `upload-file`,`mobile/submit` 也没有对应的 `privacyAgreementFileNo`
之类字段。
118. ~~**匿名访问 `upload-file` 的可行性未经真实后端环境验证,风险与第99条记录的
`face-recognize` 完全同构**~~——**2026-08-06 更新:该风险已解除**,后端已确认
`customer-info/personal/upload-file` 接口已去除鉴权要求,H5 侧匿名调用(不带
`Authorization`)可以正常访问,不会被拦截返回401。以下为原始记录,仅作历史参考:
~~该接口此前只被已登录的管理后台调用(带 `Authorization`),H5 侧本次调用不带该请求头。
本地无法连接真实后端环境验证 Spring Security 是否已将该接口路径纳入匿名白名单,若未纳入,
H5 场景下调用会返回401,导致"相关协议"步骤卡死在生成协议文件阶段(前端已有失败重试UI,
但无法绕过后端鉴权拒绝)。需要后端配合确认或调整安全配置。~~
119. **mock-server 配套改造(不是后端契约问题,记录仅为避免回归)**:(1)
`customer-info/personal/upload-file` 的 mock 实现已从带 `requireAuth`
`mock-server/routes/customer.js` 移至新建的匿名 `mock-server/routes/uploadFile.js`,挂载
顺序放在 `mock-server/index.js``personalOpenMobileRouter`/`faceRecognizeRouter` 同一
批"必须在 customerRouter 之前"的匿名路由块内,原理与第100条记录的修复完全一致;(2)
`mock-server/assets/bank-agreements/pay_service_agreement.pdf`/`pay_service_privacy.pdf`
已从"纯占位文本PDF"替换为银行提供的正式模板原件,因为本次新增的填充逻辑依赖模板末页真实
存在的"用户:/证件号码:/时间: 年 月 日"空白区块及其精确坐标,占位文本PDF没有这个区块,
第113条记录的"mock场景用占位PDF模拟"已过期,不再准确;(3) `mobile/submit` 的 mock 实现
已同步接收 `payAgreementFileNo` 并原样存到申请单记录(仅用于本地调试排查,不做真实归档
校验)。
120. **真实后端环境下,`upload-file` 请求体过大(约2.4MB base64)会在网络层直接失败,疑似网关/
Nginx 存在请求体大小限制,具体阈值未与运维确认**:2026-08-06 最初版本的"协议PDF生成"实现
(给PDF嵌入完整中文字体,导致生成的PDF约1.7MB、base64约2.4MB)在真实后端环境联调时,调用
`upload-file` 在浏览器 Network 面板直接显示请求失败(无正常HTTP状态码),`request.js` 响应
拦截器报"网络异常"——不是后端业务校验拒绝,是请求在网络层就没有正常完成,现象与"请求体
超出网关/Nginx/CDN默认大小限制(常见默认值如 `client_max_body_size 1m`)"高度吻合。前端已
通过改变实现方式规避(姓名改用 Canvas 渲染成图片再嵌入,不再嵌入整个字体文件,生成的PDF
体积降到约130KB,详见
`docs/superpowers/specs/2026-08-06-agreement-pdf-fill-and-upload-design.md`"姓名渲染方案"
一节),**但没有从根本上确认这个大小限制的具体阈值**。如果后续其它场景需要通过
`upload-file`(或类似的Base64文件上传接口)上传体积明显更大的文件(比如高清扫描件、多页
PDF合同原件),建议提前找运维/后端确认网关层的请求体大小限制,避免重复踩同样的坑。
121. **待确认:真实后端环境下,状态为"申请失败"(`APPLY_FAILED`)的个人开户申请,
`personal-opening/list`/`personal-opening/detail` 响应里的 `failReason` 字段值为空**
(2026-08-06 用户在真实后端环境实测反馈,列表页"失败原因"列空白,点进详情页"失败原因"字段
显示为占位符"-",也没有具体文字)。前端字段命名/取值路径与最新 swagger
(`swagger_project_scfs_2026-08-05_15-15-22.json` 里 `PersonalOpeningListVo`/
`PersonalOpeningDetailVo` 均声明 `failReason: string`)完全一致,列表页/详情页两处渲染代码
也均已有 `record.failReason`/`detailInfo.failReason || '-'` 的正常取值逻辑,**排除是前端
字段名写错或渲染逻辑遗漏**——问题指向后端在把开户申请状态置为 `APPLY_FAILED` 时,没有把
具体失败原因文案写入 `failReason` 字段(或者该字段只在某些失败场景下才由后端填充,本次
实测的两条记录恰好落在未填充的场景里)。需要后端确认:(1)开户失败时是否总是会调用某个
具体的第三方/核心系统接口并拿到明确的失败文案,如果有,是否真的会写回 `failReason`;
(2)如果确实存在"无法获取具体原因"的失败场景(比如网关超时、核心系统无响应),前端应该
展示什么占位文案更合适(目前是空字符串走 `|| '-'` 兜底显示"-",如果后端确认这类场景会
长期存在,可以考虑换成更明确的"未获取到具体失败原因,请联系技术核实"之类的文案,但这个
UI 调整需要后端先确认这是"预期行为"还是"待补的后端bug"才能定,不属于前端可单方面解决的
问题)。
122. **个人开户申请"职业"字段 2026-08-06 改为动态字典驱动(`dictTypeCode='job'`),用法与第24条
"所属行业"(`industry_type`)完全一致**:`PersonalOpenForm.vue` 的职业字段改用 `DictSelect.vue`
(`dict-type-code="job"`),要求该字典类型 `dictCategory=CASCADE`、三级(大类->中类->小类)。
提交给 `personal-opening/create-draft`/`update` 的 `occupation` 值为**最终选中叶子字典项的
`itemValue` 字符串**(不是数字 `id`,也不是整条路径数组),与"所属行业"字段的约定一致。
**`job` 这个字典类型目前后端/mock 均不存在**,需要后端在真实环境的"动态字典管理"里新增
`job` 字典类型及其三级字典项数据(mock 已在 `mock-server/db/seedBaseConfig.js` 补充等价的
演示数据,参照 GB/T 6565 职业分类精简版,仅供本地演示,不代表真实分类应该长什么样,后端/
产品配置真实数据时不需要照抄)。
**"其他"职业需要填写备注的判断方式有变化**:改造前判断依据是硬编码 `occupationType === 'OTHER'`
(前端占位枚举里固定的一个值);字典驱动后叶子项的 `itemValue` 由字典管理页维护,前端不再
固化任何具体编码,只能约定"叶子节点的中文文案(`itemLabel`)写作‘其他’两个字"来判断(字典
接口本身没有专门的"是否其他类别"标记字段),这是前端假设,依赖字典维护人员按此约定配置
数据,若后端/产品有更好的判断方式(比如给字典项加一个 `isOther` 布尔标记字段),欢迎调整。
**顺带修正一个既有的字段名不一致 bug(与本次字典改造无直接关系,但改动同一块代码时一并
修复)**:"职业备注"字段在 `personal-opening/create-draft` 的字段名是 `jobNote`,但在
`personal-opening/update`(`UpdateAccountOpenApplicationBto`)与 `personal-opening/detail`
(`PersonalOpeningDetailVo`)里实际字段名是 `occupationNote`——是后端两个接口本身命名不
一致,不是前端笔误。改造前 `PersonalOpenForm.vue` 的编辑/详情逻辑一直在用 `jobNote` 读写
这两个接口,已随本次改动一并修正为按接口分别使用 `jobNote`(create-draft)/`occupationNote`
(update/detail)。
123. **`upload-file`(`POST /api/customer-info/personal/upload-file`)没有独立的
`fileName`/`fileExt`/`contentType` 字段,后端判断文件真实类型/存储扩展名的唯一依据是
`fileBase64` 字符串本身是否带 `data:<mime>;base64,` 前缀——不带前缀时,真实后端环境实测会
把文件存成 `.jpg`(2026-08-06 发现)**:`fryl_h5`"相关协议"步骤生成协议PDF后上传时,
`agreementPdfFill.js` 出于内部 `pdf-lib` 处理需要,返回的是不含任何前缀的"纯净"base64(见
该文件顶部注释),`AgreementStep.vue` 最初直接把这个纯 base64 传给 `uploadFileApi`,没有
补前缀。而项目里其它所有已跑通的 `upload-file`/`face-recognize` 调用(`IdCardUpload.vue`/
`LicenseUpload.vue`/`BeneficiaryProofUpload.vue`/`FacePhotoUpload.vue`)全部是
`FileReader.readAsDataURL()` 产出的带 `data:image/xxx;base64,` 前缀的完整 Data URI——这个
"靠前缀嗅探 MIME"的隐性契约在此之前从未被打破过,直到协议PDF这个"第一个非图片文件"的上传
场景才暴露出来。管理后台侧反馈:该协议PDF上传后在后台被存成了 jpg,打不开。已修复:
`AgreementStep.vue` 调用 `uploadFileApi` 前补上 `` `data:application/pdf;base64,${filledBase64}` ``
前缀,与其它上传场景保持一致。**mock-server 不会重现此问题**(`mock-server/routes/uploadFile.js`
不落盘、不做任何类型判断,只返回一个 `fileNo`),这类"依赖 base64 字符串前缀自描述 MIME"
的契约细节只能在真实后端环境验证,后续如果有新的"非图片文件通过 upload-file 上传"的场景,
务必记得带上正确的 `data:<mime>;base64,` 前缀,不要照抄纯 base64 payload 的写法。已用
Puppeteer 起真实 headless Chrome + mock-server 跑通实际请求,确认修复后请求体
`fileBase64` 字段值以 `data:application/pdf;base64,JVBERi0x...`(`%PDF` 魔数)开头。
**2026-08-06 补充待确认**:用户反馈后台看到的文件名具体是不是字面意义上的 `image.jpg`
(而不只是"扩展名变成了 jpg")。这里需要澄清一个关键点:**前端从始至终都没有任何渠道能给
这个接口传文件名**——swagger 的 `required`/`properties` 只有 `channelNo`/`fileBase64`
两项,没有 `fileName` 字段,所以如果后台看到的文件名真的是固定的 `image.jpg` 这个字面值,
这个名字**100%是后端自己生成的**,不是前端传过去的任何值。上面记录的"靠 base64 前缀嗅探
MIME"只是**根据其它成功场景的行为归纳出的推断**,并没有拿到后端源码或后端团队的确认;
不能排除另一种可能——`upload-file` 这个接口的存储层原本就是只为图片场景写的,不管
`fileBase64` 内容/前缀是什么,都会**硬编码**用 `image.jpg` 作为存储文件名/默认扩展名
(历史上这个接口确实只被身份证/营业执照/免冠照这类图片调用过,从未处理过PDF)。如果是后
一种情况,前端补 `data:application/pdf;base64,` 前缀**不会起作用**,因为后端根本没有解析
这个前缀的逻辑——需要用本次已修复的版本在真实后端环境**重新测一次**,确认后台存储的文件
名/扩展名是否已经变成 PDF 相关;如果还是 `image.jpg`,说明问题在后端硬编码,前端没有任何
参数可以覆盖这个行为,必须让后端加上真正的 `fileName`/`fileExt` 请求字段,或者让后端的
存储逻辑去解析 `fileBase64` 前缀/内容魔数,前端单方面无法解决。