11 条有业务交付改判 verified(#6397/6903/6904/6905/6950/6979/6986/7013/7029/7036/7066,owner=mmg+对应业务 commit ref+交付日 verified_at); 6 条实证零改动改判 not_required(#6014/6016/6140/6938/6842/7087,仅翻 frontend_status 不填 owner/ref)。 #5935 挂起待后端补字段,保持 pending 不动。sync-log 均已记账。
17 KiB
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 | 6950 | 团期人员配置候选列表(导游位并收 GUIDE/LEADER) | admin | jw(GIT) | 新增接口 | deployed | verified | verified | mmg | 44f51c13 | 2026-09-03 | PR #6962 已合入 dev-v3;2026-09-03 经测试环境网关实测,6 条正负向用例全部通过。前端尚未接入 | 2026-09-03 | dev-v3 |
团期人员配置: 新增人员配置候选列表接口,并修正 staffRole 取值校验
存放目录: 二期(order-v3)→
changelogs-v2/2026-09/服务: hl-order-service-v3 PR: #6962 Issue: #6950 日期: 2026-09-02 影响范围: 管理后台「团期详情 → 配置导游 / 配置摄影」弹窗的人员资源库列表
⚠️ 关键变化
本次除新增候选列表接口外,另有一处行为变更,调用方必须知道:
保存团期人员配置 PUT /v3/admin/group-batch/:groupBatchId/staff 的 staffList[].staffRole 加了枚举白名单校验。
- 前端以前以为:
staffRole只做非空校验,传什么都能存进去。 - 实际现在是:白名单外的取值 返回 400,不再原样入库。
- 白名单:
LEADER/GUIDE/DRIVER/PHOTOGRAPHER/OTHER。
另外该字段的 Swagger 描述此前 漏了 GUIDE,本次补全。若前端此前按旧描述认为只有 4 个取值,现在是 5 个。
一、背景
「配置导游」「配置摄影」弹窗需要一份可选人员列表,此前 order-v3 侧没有这个接口,前端无从取候选池。
资源域零改动:GET /internal/staff/list-available 本就存在(其 Swagger 注释写明「供管理后台团批分配人员的下拉选择框使用」),order-v3 侧只补了 Feign 方法与降级处理。
导游位为什么并收两类(2026-09-02 jw 裁决):资源域字典里 GUIDE 是导游、LEADER 是领队,两者在团期现场都可能承担带团职责,由配置人按实际情况挑,服务端不替业务做取舍。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 查询团期人员配置候选列表 | GET | /v3/admin/group-batch/:groupBatchId/staff/candidates |
新增接口 | 导游位并收 GUIDE/LEADER,摄影位只收 PHOTOGRAPHER |
| 2 | 保存团期人员配置 | PUT | /v3/admin/group-batch/:groupBatchId/staff |
请求体新增校验 | staffList[].staffRole 加枚举白名单,越界返 400 |
三、接口详情
1. 查询团期人员配置候选列表 GET /v3/admin/group-batch/:groupBatchId/staff/candidates
VO: StaffCandidateRespVO
使用场景
管理后台「团期详情 → 配置导游 / 配置摄影」弹窗打开时调用,用于渲染可选人员资源库列表。
弹窗按配置位分别调用:导游弹窗传 role=GUIDE,摄影弹窗传 role=PHOTOGRAPHER。
列表中 assigned=true 的项应预置为已勾选状态,供配置人在原有选择基础上增删。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
groupBatchId |
Path | Long | ✅ | — | 团期批次 ID。这里是产品侧 group_tour_batch.batch_id,不是订单侧运营团期主键,与 order_batch_staff.group_batch_id 同源 |
role |
Query | String | ✅ | 只接受 GUIDE / PHOTOGRAPHER |
配置位。GUIDE=导游位(并收 GUIDE/LEADER,可选多人);PHOTOGRAPHER=摄影位。其余取值(含 DRIVER)返回 582113 |
DRIVER被显式拒绝:司机由车务派车产生,不在团期人员配置里手工指定。
出参 Result<List<StaffCandidateRespVO>>
| 字段 | 类型 | 说明 |
|---|---|---|
staffId |
Long | 人员 ID |
staffName |
String | 姓名 |
staffPhone |
String | 手机号,前 3 后 4 脱敏(与 getConfig 口径一致) |
staffType |
String | 资源域人员类型:GUIDE / LEADER / PHOTOGRAPHER |
avatarUrl |
String | 头像 URL;资源域无头像或取头像失败时为 null |
assigned |
Boolean | 是否已被本团期选中。true 时前端应显示为已勾选 |
assignedRole |
String | 已选中时对应的 order_batch_staff.staff_role;未选中为 null |
assigned与assignedRole要分开判:存在「已选但角色为空」的历史数据,此时assigned=true而assignedRole=null,前端不能用assignedRole != null判断勾选态。
请求示例
GET /v3/admin/group-batch/1823456789012345678/staff/candidates?role=GUIDE
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": [
{
"staffId": 1823456789012345678,
"staffName": "张三",
"staffPhone": "138****8000",
"staffType": "GUIDE",
"avatarUrl": "https://oss.example.com/avatar/1.jpg",
"assigned": true,
"assignedRole": "LEADER"
},
{
"staffId": 1823456789012345679,
"staffName": "李四",
"staffPhone": "139****1234",
"staffType": "LEADER",
"avatarUrl": null,
"assigned": false,
"assignedRole": null
}
]
}
空数据 / 降级响应
资源域无可用人员时返回空数组,不报错:
{
"code": 200,
"message": "成功",
"success": true,
"data": []
}
头像服务取不到时不阻断列表,avatarUrl 降级为 null,其余字段照常返回。
错误响应
role 传了 GUIDE / PHOTOGRAPHER 之外的值(如 DRIVER):
{
"code": 582113,
"message": "人员配置位不合法,只支持导游位与摄影位",
"success": false,
"data": null
}
资源域人员查询失败:
{
"code": 582103,
"message": "员工信息查询失败,请稍后重试",
"success": false,
"data": null
}
业务边界
- 只读接口,不产生任何写入,可安全重复调用
- 同一人同时属于
GUIDE与LEADER时按staffId去重,只返回一条 assigned反映的是本团期当前配置状态,与role入参无关:传role=GUIDE时, 已被配成摄影位的人不会出现在结果里,但导游位候选中若有人已被选中则assigned=true- 资源域仅返回启用状态人员,停用人员不在候选池
- 头像属展示增强,取不到时降级为
null,不影响可选性
2. 保存团期人员配置 PUT /v3/admin/group-batch/:groupBatchId/staff
VO: BatchStaffConfigReqVO
本次只改请求体校验,路径、出参均不变。
使用场景
「配置导游 / 配置摄影」弹窗点击保存时调用,整批覆盖该团期的人员配置。
本次变更后,前端必须保证 staffRole 取值落在白名单内,否则整个请求被拒、无一条生效。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
groupBatchId |
Path | Long | ✅ | — | 团期批次 ID,同接口 1 |
staffList |
Body | Array | ✅ | — | 整批覆盖语义:传入列表即最终配置,未包含的人员被移除 |
staffList[].staffId |
Body | Long | ✅ | @NotNull |
人员 ID |
staffList[].staffRole |
Body | String | ✅ | 本次新增 @Pattern:LEADER|GUIDE|DRIVER|PHOTOGRAPHER|OTHER |
变更前仅 @NotBlank,任意非空值原样入库;变更后越界返 400 |
staffList[].sortOrder |
Body | Integer | ❌ | — | 展示排序 |
staffList[].remark |
Body | String | ❌ | @Size(max=500) |
备注 |
出参 Result<BatchStaffConfigRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
staffList |
Array | 保存后的人员配置列表,字段同 getConfig |
affectedOrderCount |
Integer | 本次保存扇出影响的子订单数 |
请求示例
{
"staffList": [
{
"staffId": 1002,
"staffRole": "GUIDE",
"sortOrder": 0
}
]
}
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"staffList": [
{
"id": "2095403473712418817",
"staffId": 1002,
"staffRole": "GUIDE",
"staffName": "李雪梅",
"staffPhone": "138****1002",
"avatarUrl": null,
"sortOrder": 0,
"remark": null
}
],
"affectedOrderCount": 0
}
}
空数据 / 降级响应
传入空 staffList 数组表示清空该团期的人员配置,返回 200 且 staffList 为空数组,
不报错。这也是回退误配置的正规手段。
错误响应
staffRole 越界(本次新增行为):
{
"code": 400,
"message": "员工角色只能是 LEADER / GUIDE / DRIVER / PHOTOGRAPHER / OTHER",
"success": false,
"data": null
}
业务边界
- 整批覆盖语义,不是增量追加;未包含在
staffList里的既有人员会被移除 - 校验在
@Valid阶段完成,任一条目越界则整个请求被拒,不会部分写入 @Pattern区分大小写,guide不等于GUIDE- 空串会同时触发
@Pattern与@NotBlank,消息合并返回 - 存量
order_batch_staff数据不迁移,新校验只在下次保存时触发
四、契约约束与正确调用方式
role必传且只接受两个值:GUIDE、PHOTOGRAPHER。不要传DRIVER——司机由车务派车投影产生, 不在团期人员配置里手工指定,传了会返回 582113。- 勾选态判断用
assigned,不要用assignedRole != null。存在「已选但角色为空」的历史数据, 用后者会漏掉这批人。 groupBatchId是产品侧group_tour_batch.batch_id,不是订单侧运营团期主键。传错会返回空列表而非报错。- 保存前先过白名单:
staffRole只能是LEADER/GUIDE/DRIVER/PHOTOGRAPHER/OTHER, 且区分大小写。整批中任一条越界会导致整个请求 400、无一条生效。 - 保存是整批覆盖:每次提交需带上该团期的完整人员列表,只传增量会导致其余人员被清除。
- 错误码
582113是业务码,HTTP 状态仍为 200,判断成败要读响应体的code字段。
五、数据库行为
仅描述外部可观察行为:
- 接口 1(GET candidates)只读,不产生任何写入。
- 接口 2(PUT staff)按整批覆盖语义重写该团期的人员配置:提交列表中的人员被保留或新增,
未包含的既有人员被移除;响应的
affectedOrderCount表示随之扇出更新的子订单数量。 - 越界校验发生在写入之前,校验失败时数据库无任何变更。
- 本次变更不涉及表结构调整,也不对存量数据做迁移或回填。
六、边界行为
role非法 → 582113,不是 400(业务码,HTTP 仍 200)- 资源域无可用人员 → 返回
[],不报错、不阻断弹窗 - 头像服务异常 →
avatarUrl为null,列表照常返回(头像属展示增强) - 资源域只返回
status=1的人员,按sortOrder、staffId排序 - 同一人同时命中
GUIDE与LEADER两类 → 按staffId去重,只出现一次 - 已选人员
staffRole为null(历史脏数据)→assigned=true、assignedRole=null,接口不 500 - 未登录 → 401(网关拦截)
六.5、枚举 / 数据字典
role(Query 参数,配置位)
| 取值 | 含义 | 实际拉取的资源域 staffType |
|---|---|---|
GUIDE |
导游位 | GUIDE + LEADER 两类并收,按 staffId 去重,可选多人 |
PHOTOGRAPHER |
摄影位 | 只收 PHOTOGRAPHER |
其他(含 DRIVER) |
— | 拒绝,返回 582113 |
staffType(响应字段,资源域人员类型)
本接口可能返回 GUIDE(导游)、LEADER(领队)、PHOTOGRAPHER(摄影)三种。
资源域完整字典还包含 GUIDE_ASSISTANT / LIFE_TEACHER / STUDY_TEACHER / OTHER,但不会出现在本接口响应里。
staffRole(保存接口入参,取值域对齐 SettlementStaffRoleEnum)
| 取值 | 含义 |
|---|---|
LEADER |
领队 |
GUIDE |
导游 |
DRIVER |
司机(由车务派车投影产生,一般不由本接口写入) |
PHOTOGRAPHER |
摄影 |
OTHER |
其他 |
六.6、错误码
段位 582100-582199,owner hl-order-service-v3(AssignmentErrorCode)。
| 码 | 符号 | 消息 | 触发 |
|---|---|---|---|
582113 |
STAFF_CANDIDATE_ROLE_INVALID |
人员配置位不合法,只支持导游位与摄影位 | 本次新增。role 不是 GUIDE / PHOTOGRAPHER |
582103 |
STAFF_INFO_FETCH_FAILED |
员工信息查询失败,请稍后重试 | 既有码。资源域 list-available 返回失败或空结果对象 |
400 |
— | 员工角色只能是 LEADER / GUIDE / DRIVER / PHOTOGRAPHER / OTHER | 本次新增。保存接口 staffRole 越界 |
七、不影响范围
- 仅影响:管理后台团期详情的「配置导游 / 配置摄影」弹窗
- 零影响:
- 资源域
hl-resource-service(结构与接口零改动,只是被新调用方使用) - 团期人员保存后的子订单扇出逻辑(
order_staff_assignment) - 车务派车产生的司机行
- 已有的
GET/PUT /v3/admin/group-batch/:groupBatchId/staff出参 - 存量
order_batch_staff数据(不迁移,staffRole新校验只在下次保存时触发)
- 资源域
八、测试环境已验证
✅ 已验证。 2026-09-03 于测试环境网关实测,真实鉴权(管理端 admin 账号)。
- 网关:
https://api.test.1814.love:9443 - 路由:
/v3/admin/**由网关直挂 hl-order-service-v3(无/order-v3前缀) - 分支:
dev-v3
| # | 用例 | 期望 | 实测 |
|---|---|---|---|
| 1 | ?role=GUIDE |
200,并收 GUIDE+LEADER | ✅ 200,7 条,staffType 去重 = [GUIDE, LEADER],staffId 唯一 |
| 2 | ?role=PHOTOGRAPHER |
200,只含摄影 | ✅ 200,1 条,staffType 去重 = [PHOTOGRAPHER] |
| 3 | ?role=DRIVER |
582113 | ✅ 582113 人员配置位不合法,只支持导游位与摄影位 |
| 4 | ?role=XXX 非法值 |
582113 | ✅ 同上 |
| 5 | 缺 role 参数 |
400 | ✅ 400 缺少必要参数: role |
| 6 | 无 Authorization | 401 | ✅ 401 缺少有效的 Authorization 头 |
脱敏核对:138****1002 / 139****1011 / 139****1010,前 3 后 4 生效。
头像降级核对:用例 1 中多条 avatarUrl 为 null 未阻断返回;用例 2 中摄影师返回真实 OSS 地址。
本地单元与架构测试(提交信息记载):
GroupBatchStaffConfigServiceTest 27 例全过 ✓
LayerEnforcement / MapperBoundary / RedLine /
ErrorCodeRegistry 45 例全过 ✓
保存接口 staffRole 白名单实测(本次行为变更)
同日于同一网关实测 PUT /v3/admin/group-batch/:groupBatchId/staff:
| # | payload staffList[0].staffRole |
期望 | 实测 |
|---|---|---|---|
| 7 | "SUPERVISOR" 越界值 |
400 | ✅ 400 员工角色只能是 LEADER / GUIDE / DRIVER / PHOTOGRAPHER / OTHER |
| 8 | "guide" 小写 |
400 | ✅ 同上(正则区分大小写) |
| 9 | "" 空串 |
400 | ✅ 400 …; 该字段不能为空(@Pattern 与 @NotBlank 同时触发) |
| 10 | "GUIDE" 白名单内 |
放行 | ✅ 200,affectedOrderCount: 0 |
用例 7–9 在 @Valid 阶段即被拒,不产生任何写入。用例 10 会落库,测试后已用空 staffList 数组 PUT 还原,
复核 GET .../staff 返回 data: [],与测试前一致。
仍待补:空候选池分支未构造(需一个无可用人员的团期)。
九、相关历史 PR
| PR | Issue | 说明 | 是否仍有效 |
|---|---|---|---|
| 本 PR #6962 | #6950 | 新增候选列表接口 + staffRole 白名单校验 |
✅ 最新 |
十、相关文档
- 关联 Issue: wx/HL#6950
- 关联 PR: wx/HL#6962
- 合入提交:
809462321(feat),merge35d0a8f2c→dev-v3 - 需求与契约:
docs/group/团期模块接口文档-v2.0.htmlGB-ADM-014
关联 / 联系人
- Issue: #6950
- PR: #6962(合并提交
35d0a8f2c,落入dev-v3) - 服务: hl-order-service-v3
- 后端: jw
- 前端: 待认领(
frontend_status: pending) - 口径裁决: 2026-09-02 jw —— 导游位并收
GUIDE与LEADER,摄影位只收PHOTOGRAPHER,DRIVER显式拒绝