feat(finance): 收款账户关联服务人员档案 changelog(#8679)
changelog-filename-gate / validate (push) Failing after 2s

个人类收款方从手填改为档案下拉选人:新增 staff-candidates 候选接口 +
create 个人类 payeeRefId 必填/payeeName 档案真名覆盖/档案校验。财务域推
changelogs-v2/ 管理后台。
这个提交包含在:
yaosutu
2026-10-01 16:35:55 +08:00
父节点 df02512101
当前提交 fbd759f003
@@ -0,0 +1,223 @@
---
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: "pending"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: "财务「资金账户→收款账户」个人类收款方(司机/导游/摄影/领队)从手填人员 ID/按名认人,改为从人员档案下拉选具体人。新增「选服务人员」聚合候选接口;create 个人类 payeeRefId 由可空变必填、payeeName 由手填变档案真名覆盖(后端强制),并新增档案存在性+在册校验(拦下架人员/黑名单·休假司机)。司机接 fleet 车队档案、导游/摄影/领队接 resource 服务人员档案。组织类(车队/导游公司/供应商)不变仍手填。"
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
- **前端对接(管理后台)**: 待认领