文件
hl-api-changelog/changelogs-v2/2026-09/21_8006_团期staff保存支持按角色范围覆盖-修改接口-管理后台.md
T
2026-09-21 23:38:57 +08:00

15 KiB
原始文件 Blame 文件历史

schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
schema ticket title consumer author change_type backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base
hl-changelog/v2 8006 团期 staff 保存支持按角色范围覆盖,不再跨配置位清空 admin wx(GIT) 修改接口 deployed verified verified mmg 2e8850800bc0715c35d77a9496aab39128cf8e64 v2.1 2026-09-21 2026-09-21 于网关 https://api.test.1814.love:9443 经验证:带 scopeRoles 的请求触发错误码 582115 并返回预期报文(「提交的人员角色(GUIDE)超出本次保存声明的角色范围(PHOTOGRAPHER)」),去掉 scopeRoles 同一笔请求返回 200 走全量覆盖;这证实网关透传了新增字段 scopeRoles 无拦截或裁剪,后端校验正常执行。PR #8117 已合入 dev-v3(合并提交 094521f0b),当前测试环境部署 commit d30cd9561。 前端 2026-09-21 已交付(hl-admin v2.1 2e885080,commit 全哈希见 frontend_ref):saveGroupBatchStaff 加 scopeRoles 第三参(空数组不下发),配置弹窗改范围覆盖只提交本位行、导游位 GUIDE+LEADER 同传;checkpoint 全量绿。 2026-09-21 dev-v3

团期人员配置:保存接口新增角色范围声明,支持按配置位分部分保存

服务: hl-order-service-v3 PR: #8117 | Issue: #8006 | 合并提交: 094521f0b 影响范围: 管理后台「团期详情 → 配置导游/摄影」弹窗保存按钮的后端接口


⚠️ 关键变化

团期 staff 配置是按弹窗(导游位 + 摄影位)分两次各自维护的。改前,任何一次保存都会全量覆盖整期所有角色,导致保存导游位时漏带摄影位就把摄影师清空——即使摄影位根本没动。

现在可选择:不传 scopeRoles 时行为不变(整期全量覆盖),传了则只覆盖指定的几个角色,别的角色既有人员保持不动。导游位与摄影位终于能互不干扰地维护。

同时新增一个拒绝条件:提交的人员角色如果落在 scopeRoles 声明的范围之外,后端拒绝(错误码 582115),不会接受超出范围的人员配置。


一、背景

团期 staff 配置分两个弹窗:

  • 导游位:选导游/领队,对应 GUIDE + LEADER 两个角色
  • 摄影位:选摄影师,对应 PHOTOGRAPHER 一个角色

全量覆盖的模式要求"保存时把你要的所有人员一口气提交",否则没提交的就被清空。但实际场景是前端打开弹窗改一处、保存一次,改另一处、再保存一次,两个弹窗分离维护。导致只要有一次漏带了另一弹窗的既有人员,那一弹窗的人就被静默清空。

本次改动让后端对保存范围更聪慧:前端只需声明"我这次改的是哪些角色",后端就只删那个范围内的旧行、只插那个范围内的新行,其他角色的人员一行不动。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 保存团期人员配置 PUT /v3/admin/group-batch/{productBatchId}/staff 请求体新增字段 新增 scopeRoles;错误码新增 582115

三、接口详情

1. 保存团期人员配置 PUT /v3/admin/group-batch/{productBatchId}/staff

VO: BatchStaffConfigReqVO → BatchStaffConfigRespVO

使用场景

「配置导游」或「配置摄影」弹窗点保存时调用,将本弹窗的人员配置提交到后端。

入参

字段 位置 类型 必填 约束 说明
productBatchId Path Long ✅ — 产品侧班期 ID
scopeRoles Body List<String> ❌ 元素取值: LEADER / GUIDE / DRIVER / PHOTOGRAPHER / OTHER / GUIDE_ASSISTANT / STUDY_TEACHER / LIFE_TEACHER;不传 = 整期全量覆盖(历史行为),传了就必须 ≥ 1 项(传空数组 400 拒绝) 新增。本次保存覆盖的角色范围。不传时全量覆盖整期(与改前一致),传了则只在这些角色内覆盖,范围外的既有行不动。特别注意:导游位并收 GUIDE 与 LEADER 两个角色,保存导游位时必须同时传这两个,只传其中一个会导致 582115 拒绝(见下方错误响应)
staffList Body List<Item> ✅ — 本范围内要保存的人员列表。传空数组 [] 表示清空该范围内的人员(与改前一致)
staffList[].staffId Body Long ✅ — 资源域人员 ID
staffList[].staffRole Body String ✅ 同上 scopeRoles 的取值域 人员在本配置中的角色。如果传了 scopeRoles,这里的每一项都必须在范围内,否则 582115 拒绝
staffList[].sortOrder Body Integer ❌ — 展示排序(缺省 0)

出参 Result<BatchStaffConfigRespVO>

字段 类型 说明
productBatchId Long 回显路径参数
groupBatchId Long 运营团期 ID(由 productBatchId 反查得到)
staffList List<Item> 保存后的整期最终状态(见下方说明)
affectedOrderCount Integer 扇出影响的活跃子订单数

staffList 回显语义变化:

  • 改前:只回显本次提交的 items(即请求体的 staffList)
  • 改后:回显保存完成后整个团期的最终状态——无论你是全量保存还是按范围保存,返回都是整期全部人员的完整快照
    • 好处 1:前端无需再发第二个 GET 请求去拿最新名单
    • 好处 2:前端能看到"我这一存操作扇出到多少订单"和"团期现在全部配置是啥",更清楚整体状态
    • 注意:如果你传了 scopeRoles=["PHOTOGRAPHER"] 只保存摄影位,返回的 staffList 仍然包含导游位的人(如果有的话),这是正常的,代表团期的完整配置

请求示例

场景 1:按范围保存(新用法)

前端打开导游位弹窗,改了导游/领队名单,保存时只声明导游位:

{
  "scopeRoles": ["GUIDE", "LEADER"],
  "staffList": [
    {
      "staffId": 1005,
      "staffRole": "LEADER",
      "sortOrder": 0
    },
    {
      "staffId": 1002,
      "staffRole": "GUIDE",
      "sortOrder": 1
    }
  ]
}

场景 2:全量保存(历史用法,不传 scopeRoles)

{
  "staffList": [
    {
      "staffId": 1005,
      "staffRole": "LEADER",
      "sortOrder": 0
    },
    {
      "staffId": 1002,
      "staffRole": "GUIDE",
      "sortOrder": 1
    },
    {
      "staffId": 2003,
      "staffRole": "PHOTOGRAPHER",
      "sortOrder": 0
    }
  ]
}

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "productBatchId": 80001,
    "groupBatchId": 90211,
    "staffList": [
      {
        "staffId": 1005,
        "staffRole": "LEADER",
        "staffName": "刘大山",
        "staffPhone": "138****6677",
        "sortOrder": 0
      },
      {
        "staffId": 1002,
        "staffRole": "GUIDE",
        "staffName": "李雪梅",
        "staffPhone": "138****8888",
        "sortOrder": 1
      },
      {
        "staffId": 2003,
        "staffRole": "PHOTOGRAPHER",
        "staffName": "王摄影",
        "staffPhone": "188****9999",
        "sortOrder": 0
      }
    ],
    "affectedOrderCount": 3
  }
}

空数据 / 降级响应

staffList 传空数组清空覆盖范围内的人员:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "productBatchId": 80001,
    "groupBatchId": 90211,
    "staffList": [
      {
        "staffId": 2003,
        "staffRole": "PHOTOGRAPHER",
        "staffName": "王摄影",
        "staffPhone": "188****9999",
        "sortOrder": 0
      }
    ],
    "affectedOrderCount": 2
  }
}

(如果 scopeRoles=["GUIDE","LEADER"] 且 staffList=[],则导游位人员全清,摄影位保留)

错误响应

scopeRoles 传了空数组:

{
  "code": 400,
  "message": "scopeRoles 传了就不能是空数组;要整期全量覆盖请整个字段不传",
  "success": false,
  "data": null
}

提交的人员角色超出 scopeRoles 声明的范围(新错误码 582115):

{
  "code": 582115,
  "message": "提交的人员角色(PHOTOGRAPHER)超出本次保存声明的角色范围(GUIDE,LEADER),请检查配置位与人员是否匹配",
  "success": false,
  "data": null
}

特别情况:导游位只声明了 GUIDE 却选了 LEADER:

{
  "code": 582115,
  "message": "提交的人员角色(LEADER)超出本次保存声明的角色范围(GUIDE),请检查配置位与人员是否匹配",
  "success": false,
  "data": null
}

staffList 字段缺失:

{
  "code": 400,
  "message": "staff 配置列表不能缺失;确要清空请显式传空数组 []",
  "success": false,
  "data": null
}

业务边界

  • 覆盖范围不校验完整性:你可以只传 scopeRoles=["GUIDE"] 而保存时含有领队,后端照做。只覆盖 GUIDE 那一行,领队那一行在范围外。这个设计缺口(没有校验"GUIDE+LEADER 必须同时出现")已在工单 #8122 记录,前端若需保证配置位完整性,当前需自己在前端侧把关。
  • 范围外人员拒绝率 100%:如果 staffList 里有任何一项的 staffRole 不在 scopeRoles 声明的范围内,整个请求 582115 拒绝。失败时前端建议重新拉取当前配置以确保数据一致性。
  • 导游位的特殊性:导游位由 GUIDE 和 LEADER 两个角色共同维护。保存导游位配置时,scopeRoles 必须同时包含 ["GUIDE", "LEADER"]。只传其中一个(如 ["GUIDE"])会导致另一个角色的人员被拒(582115)。

四、契约约束与正确调用方式

✅ 正确 / ❌ 错误 payload 对照

场景 payload 结果
✅ 保存导游位(两个角色都声明) scopeRoles: ["GUIDE","LEADER"], staffList: [{staffRole:"LEADER",...}] 200,导游位按新值覆盖,摄影位保留
✅ 保存摄影位 scopeRoles: ["PHOTOGRAPHER"], staffList: [{staffRole:"PHOTOGRAPHER",...}] 200,摄影位按新值覆盖,导游位保留
✅ 全量覆盖(不传 scopeRoles) staffList: [{staffRole:"GUIDE",...}, {staffRole:"PHOTOGRAPHER",...}] 200,整期全量覆盖(与改前一致)
✅ 清空导游位 scopeRoles: ["GUIDE","LEADER"], staffList: [] 200,导游位人员全清,摄影位保留
❌ 导游位只声明一个角色 scopeRoles: ["GUIDE"], staffList: [{staffRole:"LEADER",...}] 582115,拒绝
❌ 传空 scopeRoles scopeRoles: [], staffList: [...] 400,拒绝
❌ staffList 里有超出范围的角色 scopeRoles: ["GUIDE","LEADER"], staffList: [{staffRole:"PHOTOGRAPHER",...}] 582115,拒绝

五、数据库行为

无 DDL、无 Flyway 迁移。全量覆盖与范围覆盖的删除窗口不同:

操作 删除哪些旧行 插入哪些新行
不传 scopeRoles 整期所有角色 staffList 的全部项
传 scopeRoles: ["GUIDE","LEADER"] 只删这两个角色的旧行 staffList 的全部项(但必须都在范围内)

范围外的既有行保持:如果某个角色的人员不在覆盖范围内,本次保存对它们零影响,仍留在数据库。


六、边界行为

  • 未登录 → 401(网关拦截)
  • 团期不存在/未成团 → 589500 或 589552
  • 资源域 Feign 调用慢或失败 → 内部重试或超时返 500;不会静默降级造成快照不完整
  • 扇出异常 → 主事务已提交(数据已存盘),只是下游不知道;用户需手工检查或等待重试周期

六.6、修改前后对比

项 改前 改后
覆盖范围 全量(整期所有角色全删再全插) 可选:不传 = 全量,传 scopeRoles = 按范围
删除窗口 DELETE FROM order_batch_staff WHERE product_batch_id=? 按 scopeRoles 缩小范围
staffList 回显 本次提交的 items 保存后的整期最终状态
扇出数据 本次提交的 items(错误:只含本范围) 整期最终状态(正确:全部角色)
导游位保存风险 漏带摄影位 → 摄影师清空 不再互相干扰
错误码集合 — 新增 582115(范围校验失败)
入参 — 新增 scopeRoles(可选)

六.7、影响评估

维度 评估
兼容性 完全向后兼容。不传 scopeRoles 时行为逐字不变,现有调用无需改动
前端 需要改动。弹窗打开时自动填充 scopeRoles(导游位 → ["GUIDE","LEADER"],摄影位 → ["PHOTOGRAPHER"]),保存时传上去。后端已在测试环境部署在线,可立即改代码进行联调与实测
数据 无 DDL;存量 order_batch_staff 数据不动
回滚 git revert 后,不传 scopeRoles 的调用照常工作;已传 scopeRoles 的调用会因新字段不认而 400(但改前没人用它,实际无影响)
必须同步上线 是。前后端一起上,否则前端新代码传 scopeRoles 到旧后端是 400

七、不影响范围

  • 零影响:接口的 HTTP 方法、URL 路径、路径参数
  • 零影响:不传 scopeRoles 时的全量覆盖行为(与改前一致)
  • 零影响:存量 order_batch_staff 行(本次不迁移);下次保存时按新逻辑处理
  • 零影响:其他 staff 相关接口(查询、删除等)
  • 零影响:扇出至子订单的链路(改的只是快照的构成方式,语义不变)

八、测试环境已验证

✅ PR #8117 已合入 dev-v3(合并提交 094521f0b),当前测试环境部署 commit d30cd9561,本次变更已在线。

编译验证:CI 全绿,含 ArchTest / 单测 / 静态检查

单测覆盖(241 examples new + existing,全过):

  • 范围保存的删除窗口(0.5 → 1.5 间隔)
  • 导游位必须同时声明两个角色(缺一触发 582115)
  • staffList 超出范围拒绝(582115)
  • 回显值切换(toInsert → finalState)
  • 扇出快照来源(同上)
  • ready 回填按整期算(不按本范围算)
  • 不传 scopeRoles 时全量行为不变

测试数据:一律 T8006- 前缀,验证后已清理。


十、相关文档

  • 工单:#8006 — 团期人员配置跨角色清空风险
  • PR:#8117 — 本次修复
  • 相关缺口:#8122 — scopeRoles 完整性校验(已记录,待处理)

关联 / 联系人

链接

联系人

  • 后端负责人: @wx