--- schema: "hl-changelog/v2" ticket: "8679" title: "收款账户关联服务人员档案:新增选服务人员候选接口 + 个人类收款方 payeeRefId 必填并挂档案校验(#8679)" consumer: "admin" author: "yst(GIT)" change_type: "修改接口" backend_status: "merged" gateway_status: "not_required" frontend_status: "implemented" frontend_owner: "hl-admin(claude-opus-4-8)" frontend_ref: "85267fc3e3837d5ebaf619dada70fb4564a0b488" target_release: "v2.1" verified_at: "2026-10-01" status_note: "财务「资金账户→收款账户」个人类收款方(司机/导游/摄影/领队)从手填人员 ID/按名认人,改为从人员档案下拉选具体人。新增「选服务人员」聚合候选接口;create 个人类 payeeRefId 由可空变必填、payeeName 由手填变档案真名覆盖(后端强制),并新增档案存在性+在册校验(拦下架人员/黑名单·休假司机)。司机接 fleet 车队档案、导游/摄影/领队接 resource 服务人员档案。组织类(车队/导游公司/供应商)不变仍手填。前端已交付:表单名称位三态——编辑个人类只读禁改(595202)/新建个人类「选择服务人员」下拉远程选人(payeeRefId 必填前置 595203)/组织类维持手填;staff-candidates 按 payeeType 拉候选,label 拼姓名·手机号·staffType(字典 staff_type 兜底原值)·导游等级,选中回填档案真名,新建态切类型清空已选与候选防错配;payload 编辑个人类剔 payeeName;595202/595203/595204 一律拦截器透 message 不建映射。新建表单 spec 8 例全绿。" updated_at: "2026-10-01" base: "dev-v3" --- # finance:收款账户关联服务人员档案(管理后台) ## 1. 接口背景 财务「资金账户 → 收款账户」登记收款方时,**个人类收款方**(司机 DRIVER / 导游 GUIDE / 摄影 PHOTOGRAPHER / 领队 LEADER)原来是**手填人员 ID + 手填姓名**,无档案校验——可填错人、填黑名单司机、填已下架人员,钱打给谁的依据不严谨。 本次让个人类收款方**挂到真实人员档案**:前端表单改为「选择服务人员」下拉(从档案源选人),后端 create 强制校验档案真实存在且在册。司机档案在 **fleet 车队域**,导游/摄影/领队在 **resource 服务人员域**。 ## 2. 变更清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 选服务人员候选 | GET | `/admin/finance/payee-accounts/staff-candidates` | **新增接口** | 按 payeeType 分流返回人员候选下拉数据 | | 2 | 登记收款方账户 | POST | `/admin/finance/payee-accounts` | **修改接口** | 个人类 `payeeRefId` 必填 + `payeeName` 被档案真名覆盖 + 档案校验 | | 3 | 编辑收款方账户 | PUT | `/admin/finance/payee-accounts/{payeeId}` | **修改接口** | 个人类禁止手改 `payeeName` | ## 3. 接口详情 ### 3.1 选服务人员候选(新增) - **使用场景**:收款账户表单选了「收款方类型」为个人类后,「选择服务人员」下拉/弹窗的数据源 - **认证**:JWT(管理后台) - **入参(query)**: | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | payeeType | string | 是 | `DRIVER`/`GUIDE`/`PHOTOGRAPHER`/`LEADER`(个人类;传组织类或非法值报 595202) | | keyword | string | 否 | 姓名模糊;输入 11 位手机号按手机精确匹配。≤50 字,超限报 595202 | | page | int | 否 | 缺省 1 | | pageSize | int | 否 | 缺省 20,最大 100 | - **行为**:按 payeeType 分流到对应档案源——DRIVER→fleet 司机、GUIDE→resource 导游(含助理导游)、PHOTOGRAPHER→摄影、LEADER→领队。档案服务故障时**降级返回空集合**(不打塌表单,前端下拉显示"暂无可选人员"即可)。 - **出参**:`data.records[]` + `data.total` ### 3.2 登记收款方账户(修改) - 个人类:`payeeRefId` **必填**(选中的服务人员 ID),后端校验该 ID 在对应档案源**真实存在且在册**(staff 上架 status=1;driver 在册 season=active 且非休假/待激活),并以**档案真名覆盖 payeeName**。 - 组织类(FLEET/GUIDE_CO/SUPPLIER):不变,`payeeRefId` 可空、`payeeName` 手填。 ### 3.3 编辑收款方账户(修改) - 个人类:禁止手改 `payeeName`(名称以人员档案为准),传了报 595202。账户要素(qr/bankAccount/bankName)仍可改。 - 组织类:不变。 ## 4. 接口入参 ### 4.1 登记请求体关键字段(变化部分) | 字段 | 类型 | 必填 | 说明 | 校验规则 | |------|------|------|------|----------| | payeeType | string | 是 | 收款方类型 | 个人类见下 | | payeeRefId | string(Long) | **个人类必填** | 服务人员 ID(staff-candidates 返回的 refId);组织类可空 | 个人类必填否则 595203;档案不存在/已下架 595204 | | payeeName | string | 否 | 收款方姓名 | **个人类会被档案真名强制覆盖**(手填无效);组织类手填 | ## 5. 出参(响应) ### 5.1 staff-candidates 出参 records[] | 字段 | 类型 | 说明 | |------|------|------| | refId | string(Long) | 人员 ID(司机=driverId,其余=staffId)——登记时填入 payeeRefId | | name | string | 姓名(档案真名) | | phone | string | 手机号(**明文**,出纳选人看全号区分同名;落库后的列表/详情仍脱敏) | | payeeType | string | 回显收款方类型 | | staffType | string | 人员类型(仅 staff 类有值:`GUIDE`/`GUIDE_ASSISTANT`/`PHOTOGRAPHER`/`LEADER`;DRIVER 为 null)。GUIDE 候选合并导游+助理导游,前端据此字段区分 | | guideLevel | string | 导游等级(仅 staff 导游类有值:初级/中级/高级/特级;其余为 null) | ## 6. 枚举 / 数据字典 ### 6.1 staffType(人员类型,字典 staff_type) | 值 | 中文 | |----|------| | `GUIDE` | 导游 | | `GUIDE_ASSISTANT` | 助理导游 | | `PHOTOGRAPHER` | 摄影师 | | `LEADER` | 领队 | > payeeType=GUIDE 的候选同时含 GUIDE 和 GUIDE_ASSISTANT 两类,前端用 `staffType` 区分展示;payeeType=PHOTOGRAPHER/LEADER 只含对应一类。 ### 6.2 payeeType 个人类 ↔ 档案源 | payeeType | 档案源 | refId 含义 | |-----------|--------|-----------| | DRIVER 司机 | fleet 车队 | driverId | | GUIDE 导游 | resource 服务人员 | staffId | | PHOTOGRAPHER 摄影 | resource 服务人员 | staffId | | LEADER 领队 | resource 服务人员 | staffId | ## 7. 错误码 | code | 含义 | 触发场景 | |------|------|----------| | 595202 | 收款方账户字段组合不合法 | staff-candidates 传组织类/非法 payeeType、keyword 超 50;编辑个人类手改 payeeName | | 595203 | 个人类收款方须选择服务人员 | create 个人类未传 payeeRefId | | 595204 | 服务人员不存在或已下架 | create 个人类 payeeRefId 在档案源不存在、staff 已下架、driver 黑名单/归档/休假/待激活;或档案服务暂不可用(文案为"档案服务暂不可用,请稍后重试") | ## 8. 示例(3 组) ### 8.1 典型成功(选司机 → 登记司机收款账户) **① 候选** `GET /admin/finance/payee-accounts/staff-candidates?payeeType=DRIVER&keyword=张`: ```json { "code": 200, "success": true, "data": { "total": 1, "records": [ { "refId": "1823456789012345678", "name": "张三", "phone": "13800138000", "payeeType": "DRIVER", "staffType": null, "guideLevel": null } ] } } ``` **② 登记** `POST /admin/finance/payee-accounts`: ```json { "payeeType": "DRIVER", "payeeRefId": "1823456789012345678", "accountType": "WECHAT_QR", "qrUrl": "https://oss.example.com/qr/zhangsan.png", "isDefault": 1 } ``` **响应**(payeeName 由后端用档案真名"张三"覆盖,无需前端传): ```json { "code": 200, "success": true, "data": { "id": "2105..." } } ``` ### 8.2 边界(导游候选含助理导游,staffType 区分) `GET /admin/finance/payee-accounts/staff-candidates?payeeType=GUIDE`: ```json { "code": 200, "success": true, "data": { "total": 2, "records": [ { "refId": "1811...", "name": "李四", "phone": "13911112222", "payeeType": "GUIDE", "staffType": "GUIDE", "guideLevel": "高级" }, { "refId": "1822...", "name": "王五", "phone": "13933334444", "payeeType": "GUIDE", "staffType": "GUIDE_ASSISTANT", "guideLevel": "初级" } ] } } ``` ### 8.3 业务失败(个人类未选服务人员) `POST /admin/finance/payee-accounts`: ```json { "payeeType": "GUIDE", "accountType": "WECHAT_QR", "qrUrl": "https://..." } ``` **响应**: ```json { "code": 595203, "message": "个人类收款方须选择服务人员", "success": false } ``` 选了已下架人员: ```json { "code": 595204, "message": "服务人员不存在或已下架", "success": false } ``` ## 9. 业务边界 - ✅ 个人类收款方:必须先调 staff-candidates 选人,把返回的 `refId` 作为 `payeeRefId` 提交;`payeeName` 不用传(后端用档案真名覆盖)。 - ✅ 组织类(车队/导游公司/供应商):维持手填 `payeeName`,`payeeRefId` 可空,不调候选接口。 - ❌ 不要再手填个人类的 payeeRefId/payeeName——后端强制档案校验+真名覆盖,手填无效或被 595203/595204 拦。 - ⚠️ 候选接口降级:档案服务故障时返回空 records(非报错),前端下拉显示"暂无可选人员,请稍后重试"即可,不要当成"无此人员"。 ## 10. 修改前后对比 | 行为 | 改前 | 改后 | |------|------|------| | 个人类选人方式 | 手填人员 ID + 手填姓名 | staff-candidates 下拉选人,payeeRefId=选中人员 ID | | 个人类 payeeRefId | 可空 | **必填**(595203) | | 个人类 payeeName | 手填生效 | **被档案真名覆盖**(手填无效) | | 档案校验 | 无 | 存在性+在册校验(595204),拦下架/黑名单/休假 | | 编辑个人类 payeeName | 可手改 | 禁止(595202) | ## 11. 影响评估 / 回滚 - **是否破坏向后兼容**:**是**。个人类 create 原来可不传 payeeRefId,现在必填;原来手填 payeeName 生效,现在被档案覆盖。前端个人类表单必须改为「先选人」流程。 - **前端是否必须同步上线**:是。个人类收款账户表单需加「选择服务人员」下拉(数据源 staff-candidates),并移除个人类的手填姓名/手填 ID 输入。 - **影响已有数据**:测试库 fin_payee_account 个人类存量少,存量数据不受影响(仅新增/编辑走新校验)。 - **回滚方式**:revert PR #8692(finance)+ PR #8688(fleet)。 ## 12. 注意事项 - **选人下拉数据分端**:司机候选来自 fleet(含 season/driverStatus 过滤),导游/摄影/领队来自 resource staff。前端只调一个 staff-candidates 接口,后端按 payeeType 分流,无需关心来源。 - **staffType/guideLevel 仅 staff 类有**:DRIVER 候选这两字段为 null,前端展示时判空。 - **手机号明文仅选人下拉里**:落库后的收款账户列表/详情 phone 仍按 PII 口径脱敏,不要在别处用候选接口的明文 phone 当展示数据源。 ## 13. 关联 / 联系人 ### 13.1 链接 - **Issue**: [#8679](https://git.1814.love/wx/HL/issues/8679) - **PR(finance)**: [#8692](https://git.1814.love/wx/HL/pulls/8692) · merge [7cb5aa9e6d](https://git.1814.love/wx/HL/commit/7cb5aa9e6d717efe34ce099b9e11eabdc9811dbf) - **PR(fleet 司机候选数据源)**: [#8688](https://git.1814.love/wx/HL/pulls/8688) · merge [b4edefd08e](https://git.1814.love/wx/HL/commit/b4edefd08e5a439db6e7a6e2d245a5d0fd1cc067) ### 13.2 联系人 - **后端负责人**: @yst - **前端对接(管理后台)**: 待认领