文件
hl-api-changelog/changelogs-v2/2026-09/03_6950_团期人员配置候选列表-新增接口-管理后台.md
Mimingguang 2f6a989fcf
changelog-filename-gate / validate (push) Failing after 2s
chore(changelog): 补齐 17 条消费闭环 frontmatter 回写
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 均已记账。
2026-09-06 10:43:20 +08:00

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),merge 35d0a8f2c → dev-v3
  • 需求与契约: docs/group/团期模块接口文档-v2.0.html GB-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 显式拒绝