From a611948477fc2a3206f19d9bfc824d67701523c1 Mon Sep 17 00:00:00 2001 From: jw Date: Fri, 4 Sep 2026 15:37:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=A2=E6=9C=9F=E4=BA=BA=E5=91=98=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=BD=8D=E6=88=90=E5=91=98=E6=94=B9=E7=94=B1=E5=AD=97?= =?UTF-8?q?=E5=85=B8=E5=86=B3=E5=AE=9A=EF=BC=8C=E5=8F=96=E5=80=BC=E5=9F=9F?= =?UTF-8?q?=E4=B8=89=E5=A4=84=E5=AF=B9=E9=BD=90=EF=BC=88#7079=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #7085 合入 dev-v3(合并提交 d1b724d59),2026-09-04 部署测试环境 并经真实网关鉴权实测 7 条用例。 核心证据:15:30:33 往 group_batch_staff_slot_guide 插一行 GUIDE_ASSISTANT, 15:35:44(301 秒后)导游位候选由 7 人变 8 人——未改代码、未重启、未重新部署。 随后连打 8 次全部返回 8 人,两个实例都已回源。演示行验证后已撤除。 2 个接口的候选池来源与落库取值变更,路径/入参/出参结构全部不变: - GET /v3/admin/group-batch/:groupBatchId/staff/candidates 候选池成员由字典决定,582113 文案变更 - PUT /v3/admin/group-batch/:groupBatchId/staff staffRole 落真实 staffType 含字典初始化脚本 sql/dict_group_batch_staff_slot.sql(幂等,需手工执行)。 dict_type_id 取 8071/8072——8021/8022 在本机与测试环境都已被 contract_status / contract_platform 占用。 staff_role 取值域测试环境实测三处一致(字典 = 枚举 = @Pattern,8 项)。 frontend_status=not_required:路径、入参、出参结构均未变,前端无需改动; 但不要再硬编码候选池成员,也不要沿用自己提交的 staffRole 回显。 --- ...员配置位成员改由字典决定-修改接口-管理后台.md | 375 ++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 changelogs-v2/2026-09/04_7079_团期人员配置位成员改由字典决定-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/04_7079_团期人员配置位成员改由字典决定-修改接口-管理后台.md b/changelogs-v2/2026-09/04_7079_团期人员配置位成员改由字典决定-修改接口-管理后台.md new file mode 100644 index 00000000..38e3c87c --- /dev/null +++ b/changelogs-v2/2026-09/04_7079_团期人员配置位成员改由字典决定-修改接口-管理后台.md @@ -0,0 +1,375 @@ +--- +schema: "hl-changelog/v2" +ticket: "7079" +title: "团期人员配置位成员改由字典决定,取值域三处对齐" +consumer: "admin" +author: "jw(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "not_required" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "PR #7085 已合入 dev-v3(合并提交 d1b724d59);2026-09-04 部署测试环境并经网关实测。含字典初始化脚本 sql/dict_group_batch_staff_slot.sql,需手工执行" +updated_at: "2026-09-04" +base: "dev-v3" +--- + +# 团期人员配置: 配置位收哪些人员类型改由数据字典决定 + +> **服务**: hl-order-service-v3 +> **PR**: #7085 | **Issue**: #7079 | **合并提交**: `d1b724d59` +> **影响范围**: 管理后台「团期详情 → 配置导游/摄影」弹窗的候选池 + +--- + +## ⚠️ 关键变化 + +**「导游位收哪些人员类型」不再写死在代码里,改由数据字典决定。业务在字典管理页面加一行,最多 5 分钟后生效,不需要发版。** + +``` +group_batch_staff_slot_guide → GUIDE、LEADER +group_batch_staff_slot_photographer → PHOTOGRAPHER +``` + +另有一处**落库取值变化**:导游位并收导游与领队,改前不论选的是谁,`order_batch_staff.staff_role` 落的都是前端传的配置位名(往往是 `GUIDE`),「这一位上站的到底是导游还是领队」在数据里就丢了。现在落该人员在资源域的真实 `staffType`。 + +--- + +## 一、背景 + +人员类型 `staff_type` 本来就是「服务人员管理」里维护的字典,实有 7 项。而团期人员配置位只认死了导游位与摄影位两个、成员也写死在 Java 字面量里,要让研学老师也能配进导游位就得改代码发版。本次把这件事交还给业务。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 团期人员候选列表 | GET | `/v3/admin/group-batch/:groupBatchId/staff/candidates` | **候选池来源变更** | 成员由字典决定;`582113` 文案变更 | +| 2 | 全量保存团期人员配置 | PUT | `/v3/admin/group-batch/:groupBatchId/staff` | **落库取值变更** | `staffRole` 落真实 `staffType` | + +--- + +## 三、接口详情 + +### 1. 团期人员候选列表 `GET /v3/admin/group-batch/:groupBatchId/staff/candidates` + +**VO**: `StaffCandidateRespVO` + +#### 使用场景 + +「配置导游 / 配置摄影」弹窗打开时调用,渲染可选人员列表。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| `groupBatchId` | Path | Long | ✅ | — | 产品侧团期 batchId | +| `role` | Query | String | ✅ | `GUIDE` / `PHOTOGRAPHER`,大小写敏感 | 配置位标识;未知值返回 `582113` | + +#### 出参 `Result>` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `staffId` | Long | 人员 ID,**未变** | +| `staffName` | String | 姓名,**未变** | +| `staffType` | String | 资源域人员类型,**未变** | +| `staffPhone` | String | 脱敏手机号,**未变** | +| `assigned` | Boolean | 是否已选,**未变** | + +> **出参结构完全没有变化**,变的是这个列表里会出现哪些人——由字典 `group_batch_staff_slot_*` 决定。 + +#### 请求示例 + +```http +GET /v3/admin/group-batch/990707900901/staff/candidates?role=GUIDE +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": [ + { + "staffId": 1002, + "staffName": "李雪梅", + "staffType": "GUIDE", + "staffPhone": "138****1002", + "assigned": false + }, + { + "staffId": 1005, + "staffName": "刘大山", + "staffType": "LEADER", + "staffPhone": "138****1005", + "assigned": false + } + ] +} +``` + +#### 空数据 / 降级响应 + +字典读空或字典服务不可用时**不报错**,回落到内置默认值(导游位 `GUIDE`+`LEADER`,摄影位 `PHOTOGRAPHER`)并打 WARN,候选列表照常返回。资源域无可用人员时返回空数组: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": [] +} +``` + +#### 错误响应 + +配置位标识未知(含小写 `guide`、`DRIVER`): + +```json +{ + "code": 582113, + "message": "人员配置位不合法,请检查配置位标识", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 配置位成员来自字典,改字典后各实例最多 5 分钟内生效(本地缓存有界 TTL) +- 字典里写了取值域外的人员类型会被忽略并 WARN,其余行照常生效 +- 字典整体读不到时回落内置默认值,行为与改前一致 +- `role` 大小写敏感,小写不认(沿用既有口径) +- 司机不设配置位,由车务派车投影产生 + +### 2. 全量保存团期人员配置 `PUT /v3/admin/group-batch/:groupBatchId/staff` + +**VO**: `BatchStaffConfigReqVO` + +#### 使用场景 + +「配置导游 / 配置摄影」弹窗点保存时调用,全量覆盖该团期人员配置。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| `groupBatchId` | Path | Long | ✅ | — | 产品侧团期 batchId | +| `staffList` | Body | Array | ❌ | 传空则清空 | 全量覆盖 | +| `staffList[].staffId` | Body | Long | ✅ | — | 资源域人员 ID | +| `staffList[].staffRole` | Body | String | ✅ | 取值域本次扩至 8 项 | 见下方取值域说明 | +| `staffList[].sortOrder` | Body | Integer | ❌ | 缺省 0 | 展示排序 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `staffList` | Array | 落库后的人员快照;其中 `staffRole` **取值口径变更** | +| `affectedOrderCount` | Integer | 扇出到的活跃子订单数,**未变** | + +#### 请求示例 + +```json +{ + "staffList": [ + { + "staffId": 1005, + "staffRole": "GUIDE", + "sortOrder": 0 + } + ] +} +``` + +#### 响应示例 + +请求传的是配置位名 `GUIDE`,但 1005 在资源域是领队,出参与落库都归一为 `LEADER`: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "staffList": [ + { + "staffId": 1005, + "staffRole": "LEADER", + "staffName": "刘大山", + "staffPhone": "138****1005", + "sortOrder": 0 + } + ], + "affectedOrderCount": 1 + } +} +``` + +#### 空数据 / 降级响应 + +`staffList` 传空数组即清空该团期全部人员配置: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "staffList": [], + "affectedOrderCount": 1 + } +} +``` + +#### 错误响应 + +`staffRole` 不在取值域内: + +```json +{ + "code": 400, + "message": "员工角色不在取值域内,取值域对齐 SettlementStaffRoleEnum 与 staff_role 字典", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 落库 `staffRole` 只在**配置位内部**归一:请求角色能解析到配置位、且该人员真实类型也在这个配置位成员里时才替换 +- 跨位不匹配(领队被配到摄影位)沿用入参,本次不做拒绝 +- `DRIVER` / `OTHER` 不属于任何配置位,沿用入参 +- 资源域反查不到人员类型时沿用入参 +- 导游位有人(`GUIDE` 或 `LEADER`)即置 `guide_ready`,与改前一致 + +--- + +## 四、契约约束与正确调用方式 + +- **候选池成员不再由代码保证**,前端不要硬编码「导游位只会出现导游和领队」;以接口返回为准。 +- **`staffRole` 落库值可能与提交值不同**:提交配置位名,落库是该人员的真实人员类型。前端若需回显角色,读出参里的 `staffRole`,不要沿用自己提交的值。 +- **`staffRole` 取值域本次由 5 项扩至 8 项**:新增 `GUIDE_ASSISTANT` / `STUDY_TEACHER` / `LIFE_TEACHER`。 +- **改字典不是立即全局生效**:各实例本地缓存 5 分钟有界 TTL,期间不同实例口径可能不同,只影响候选池多列/少列一类人员。 + +--- + +## 五、数据库行为 + +无 DDL、无 Flyway 迁移。**但需手工执行一个字典初始化脚本**: + +``` +sql/dict_group_batch_staff_slot.sql 新增,幂等,可重复执行 +``` + +| 表 | 变化 | +|---|---| +| `sys_dict_type` | 新增 2 行:`group_batch_staff_slot_guide`(8071) / `group_batch_staff_slot_photographer`(8072) | +| `sys_dict_data` | 新增 3 行配置位成员;`staff_role` 补 `STUDY_TEACHER` / `LIFE_TEACHER` 两行 | +| `order_batch_staff.staff_role` | 结构未变;**新写入的取值口径变了**,存量行不动 | + +> `dict_type_id` 取 8071/8072 而非号段顺序的 8021/8022——后者在本机与测试环境**都已被** `contract_status` / `contract_platform` 占用(两处实测确认)。 + +--- + +## 六、边界行为 + +- 字典未初始化 → 回落内置默认值,行为与改前完全一致 +- 字典含非法人员类型 → 忽略该行 + WARN,其余生效 +- 字典全部非法 → 视同读空,回落默认值 +- 字典行 `dictValue` 为空或空白 → 直接跳过,不刷告警 +- Feign 返回 null / 失败 / 抛异常 → 三种都回落默认值,不把异常透给调用方 +- `role` 小写 / 未知 → `582113` + +## 六.5、枚举 / 数据字典 + +**新增字典类型 2 个**:`group_batch_staff_slot_guide`、`group_batch_staff_slot_photographer`。取值域 = 资源域 `staff_type`。 + +**`staff_role` 字典扩至 8 项**,与 `SettlementStaffRoleEnum` 和接口 `@Pattern` 三处对齐(测试环境实测三处完全一致)。 + +## 六.6、修改前后对比 + +| 项 | 变更前 | 变更后 | +|---|---|---| +| 配置位成员 | 写死在 3 处 Java 字面量 | 由字典决定,加一行即生效 | +| 扩一种人员类型 | 改代码 + 发版 | 改字典,最多 5 分钟生效 | +| 落库 `staffRole` | 前端传的配置位名 | 该人员真实 `staffType` | +| `staffRole` 取值域 | 5 项 | 8 项 | +| `582113` 文案 | 「只支持导游位与摄影位」 | 「请检查配置位标识」 | +| 字典不可用 | — | 回落内置默认值,不中断 | +| 路径 / 入参 / 出参结构 | — | **全部不变** | + +## 六.7、影响评估 + +| 维度 | 评估 | +|---|---| +| 兼容性 | 字典未初始化时回落默认值,与改前**完全一致**,不会坏 | +| 前端 | **无需改动**。路径、入参、出参结构均未变;但不要再硬编码候选池成员,也不要沿用自己提交的 `staffRole` 回显 | +| 数据 | 无 DDL;需手工执行一个幂等字典脚本;存量 `staff_role` 行不动 | +| 性能 | 每个配置位一次字典 Feign,5 分钟缓存;候选池按类型逐类拉取,类型多一种多一次资源域调用 | +| 回滚 | `git revert`;字典行留着无害(回滚后代码不读它) | +| 风险 | 中低。主要风险是下游若有按 `staff_role == 'GUIDE'` 硬比对处会受落库取值变化影响;order-v3 内已全部改为按配置位归组 | + +## 七、不影响范围 + +- **零影响**:两个接口的路径、HTTP 方法、入参字段、出参结构 +- **零影响**:摄影位口径,字典默认仍只收 `PHOTOGRAPHER` +- **零影响**:staff-fees 费用录入分流——只加枚举取值域,未动 `SettlementStaffFeeDetailCodec` 的 `GUIDE`/`PHOTOGRAPHER` 两族判定 +- **零影响**:存量 `order_batch_staff` / `order_staff_assignment` 数据 +- **零影响**:`DRIVER` / `OTHER` 角色的处理 +- **未新建端点** + +--- + +## 八、测试环境已验证 + +✅ 2026-09-04 于测试环境网关实测,真实鉴权(管理端 `test_admin`,角色定制师)。 + +- 网关 `https://api.test.1814.love:9443`,分支 `dev-v3`,合并提交 `d1b724d59` +- 部署方式:双实例滚动更新(8186 → 8086),各 10s 就绪,零停机 +- 部署内容经部署面板 `/api/git/backend` 交叉核对:构建时点 `d1b724d59` 已是 `dev-v3` 顶端 +- 字典初始化脚本已在测试环境执行,幂等 + +| # | 用例 | 期望 | 实测 | +|---|---|---|---| +| 1 | 导游位候选(字典 GUIDE+LEADER) | 与改前一致 | ✅ 7 人 | +| 2 | 摄影位候选 | 口径不变 | ✅ 1 人 | +| 3 | `role=DRIVER` | `582113` 新文案 | ✅ 「请检查配置位标识」 | +| 4 | 导游位选领队(提交 `GUIDE`) | 落库真实类型 | ✅ 出参与落库均为 `LEADER` | +| 5 | 同场景 `guide_ready` | 仍置位 | ✅ 1 | +| 6 | `staff_role` 取值域三处对齐 | 字典 = 枚举 = `@Pattern` | ✅ 8 项完全一致 | +| 7 | **字典加一行 `GUIDE_ASSISTANT`** | 无需发版即生效 | ✅ **301 秒后候选 7 → 8 人,陈小燕出现;两个实例连打 8 次均为 8** | + +**观察**:双实例持续 running,服务日志最近 200 行 `ERROR` / `Exception` 命中 **0** 条;配置位字典相关 WARN **0** 条,说明字典读取正常、未走降级。 + +用例 7 是本单的核心证据:15:30:33 往字典插一行,15:35:44(301 秒后)候选列表由 7 人变 8 人——**没有改代码、没有重启服务、没有重新部署**。延迟符合 5 分钟有界 TTL 的设计;随后连打 8 次全部返回 8 人,两个实例都已回源。演示用的那一行验证后已撤除,出厂默认仍是 `GUIDE` + `LEADER`。 + +**本地单测**:**431 例全过**。新增 `GroupBatchStaffSlotResolverTest` 14 例(正常读取、加行即生效、去重、TTL 命中缓存、读空/Feign null/Feign 异常三种降级、降级后判定仍工作、非法值过滤、全非法回落、空白值跳过、未知角色无配置位、枚举大小写敏感);`GroupBatchStaffConfigServiceTest` 新增 3 例覆盖落库取值三个分支。 + +**测试数据**:一律 `T7079-` 前缀,验证后物理删除。`test_admin` 口令一次性置换,取到令牌后立即按备份还原,逐字节一致;共享库写入全程持 `test-db` 租约。 + +--- + +## 九、相关历史 PR + +- #7065(Refs #7063 第 1 波)本 PR 承接它,`StaffSlotRoles` 由事实源退化为兜底默认值 +- 裁决来源:#7063 的 D-1 / D-3 / D-4(jw 2026-09-04) + +--- + +## 十、相关文档 + +- 团期需求文档:`docs/group/`(dev-v3 分支) +- 字典初始化脚本:`sql/dict_group_batch_staff_slot.sql` + +--- + +## 关联 / 联系人 + +- **Issue**: #7079 +- **PR**: #7085(合并提交 `d1b724d59`)