文件
hl-api-changelog/changelogs-v2/2026-09/04_7063_团期导游位下游按配置位归组-修改接口-管理后台.md
T
jw 2096a47340
changelog-filename-gate / validate (push) Successful in 2s
团期导游位下游按配置位归组,GUIDE 与 LEADER 一视同仁(#7063)
PR #7065 合入 dev-v3(合并提交 f37b6ee0f),2026-09-04 部署测试环境
并经真实网关鉴权实测 AC-2/AC-3/AC-4/AC-5/AC-9。

4 个接口的副作用与取值口径变更,路径/入参/出参结构全部不变:
- PUT  /v3/admin/group-batch/:groupBatchId/staff      导游位配 GUIDE 也置 guide_ready
- POST /v3/admin/order/:id/staff                      GUIDE 也写 guide_status 与 GUIDE_DONE
- DELETE /v3/admin/order/:id/staff/:staffAssignmentId 按导游位整组计数,不再误置空
- GET  /v3/admin/order/:id/print-itinerary            导游栏 GUIDE 优先、缺位回退 LEADER

顺带修一处更深的缺陷:ready 回填此前用产品侧 batchId 打 order_group_batch 主键,
rows=0 只 WARN 不抛,改前不论配 GUIDE 还是 LEADER,guide_ready 从未被置位过。

frontend_status=not_required:路径、入参、出参结构均未变,前端无需改动。
2026-09-04 14:25:48 +08:00

17 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 7063 团期导游位下游按配置位归组,GUIDE 与 LEADER 一视同仁 admin jw(GIT) 修改接口 deployed verified not_required PR #7065 已合入 dev-v3(合并提交 f37b6ee0f);2026-09-04 部署测试环境并经网关实测 AC-2/AC-3/AC-4/AC-5/AC-9。第 2 波配置位字典化未做 2026-09-04 dev-v3

团期人员配置: 导游位下游判定由单一角色改为按配置位归组

服务: hl-order-service-v3 PR: #7065 | Issue: #7063 | 合并提交: f37b6ee0f 影响范围: 管理后台「团期详情 → 配置导游/摄影」、「订单详情 → 人员」、行程单打印


⚠️ 关键变化

「导游位并收导游与领队」这条裁决此前只落到了候选列表一层,保存之后的下游仍只认单一角色。

结果是配置人怎么选都会踩一边:

存成 丢什么
GUIDE guide_ready 不置位、guide_status 不写、GUIDE_DONE 流水不写、订单资源就绪闸门推不动
LEADER 行程单「导游」栏为空

本机实测还暴露了第二处更深的缺陷:团期人员配置保存时,ready 回填用的是产品侧 batchId, 而 order_group_batch 的 ready 列按主键更新,两者不是同一个 ID 空间。 Mapper rows=0 只打 WARN 不抛,所以这个洞一直没被发现—— 改前不论配 GUIDE 还是 LEADER,guide_ready / photographer_ready 从来就没被置位过。


一、背景

人员类型 staff_type 在服务人员管理里是维护好的字典,团期现场谁带团由业务按实际安排: 有的团派导游,有的团派领队,也有两人都上。2026-09-02 已裁决「导游位并收 GUIDE 与 LEADER, 服务端不替业务做取舍」,本次把这条裁决贯彻到保存之后的全部下游链路。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 全量保存团期人员配置 PUT /v3/admin/group-batch/:groupBatchId/staff 副作用变更 导游位配 GUIDE 也置 guide_ready;并修正 ready 回填的 ID 空间
2 订单新增人员 POST /v3/admin/order/:id/staff 副作用变更 staffRole=GUIDE 也写 guide_status 与 GUIDE_DONE 流水
3 订单删除人员 DELETE /v3/admin/order/:id/staff/:staffAssignmentId 副作用变更 计数按导游位整组,删其一不再误置空 guide_status
4 打印行程单 GET /v3/admin/order/:id/print-itinerary 出参取值口径变更 导游栏 GUIDE 优先、缺位回退 LEADER

三、接口详情

1. 全量保存团期人员配置 PUT /v3/admin/group-batch/:groupBatchId/staff

VO: BatchStaffConfigReqVO

使用场景

团期详情「配置导游 / 配置摄影」弹窗点保存时调用。全量覆盖语义:传入列表即为最终配置。

入参

字段 位置 类型 必填 约束 说明
groupBatchId Path Long ✅ — 产品侧团期 batchId,非 order_group_batch 主键
staffList Body Array ❌ 传空则清空 人员配置列表,全量覆盖
staffList[].staffId Body Long ✅ — 资源域人员 ID
staffList[].staffRole Body String ✅ LEADER GUIDE DRIVER PHOTOGRAPHER OTHER 团期角色
staffList[].sortOrder Body Integer ❌ 缺省 0 展示排序

出参 Result<BatchStaffConfigRespVO>

字段 类型 说明
staffList Array 落库后的人员快照,结构未变
affectedOrderCount Integer 扇出到的活跃子订单数,未变

出参结构完全没有变化,变的是写库之后的副作用。

请求示例

{
  "staffList": [
    {
      "staffId": 1002,
      "staffRole": "GUIDE",
      "sortOrder": 0
    }
  ]
}

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "staffList": [
      {
        "staffId": 1002,
        "staffRole": "GUIDE",
        "staffName": "李雪梅",
        "staffPhone": "138****1002",
        "sortOrder": 0
      }
    ],
    "affectedOrderCount": 2
  }
}

空数据 / 降级响应

staffList 传空数组即清空该团期全部人员配置,返回空列表:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "staffList": [],
    "affectedOrderCount": 2
  }
}

错误响应

团期不存在时 ready 回填跳过并打 WARN,接口本身仍成功;未携带令牌时:

{
  "code": 401,
  "message": "缺少有效的 Authorization 头",
  "success": false,
  "data": null
}

业务边界

  • 导游位认 GUIDE 与 LEADER 两种角色,任一有人即置 guide_ready
  • 摄影位只认 PHOTOGRAPHER
  • 同时配了导游和领队时 markGuideReady 只调一次,幂等
  • 团期查不到(未成团 / 已解散)时 ready 回填跳过,不抛异常
  • DRIVER 不参与任何配置位判定,由车务派车投影产生

2. 订单新增人员 POST /v3/admin/order/:id/staff

VO: StaffAssignReqVO

使用场景

订单详情「人员」区手工新增一名服务人员时调用。

入参

字段 位置 类型 必填 约束 说明
id Path Long ✅ — 订单 ID
staffId Body Long ✅ — 资源域人员 ID
staffRole Body String ✅ LEADER GUIDE DRIVER PHOTOGRAPHER OTHER 团期角色

出参 Result<StaffAssignmentVO>

字段 类型 说明
assignmentId Long 新建行 ID,未变
staffRole String 角色,未变
staffPhone String 脱敏手机号,未变

请求示例

{
  "staffId": 1002,
  "staffRole": "GUIDE"
}

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "assignmentId": 2095758914858471425,
    "staffId": 1002,
    "staffRole": "GUIDE",
    "staffName": "李雪梅",
    "staffPhone": "138****1002"
  }
}

空数据 / 降级响应

本接口为写入,无空数据形态;资源域人员查询失败时整批 fail-fast:

{
  "code": 582103,
  "message": "员工信息查询失败,请稍后重试",
  "success": false,
  "data": null
}

错误响应

同一订单同一角色重复添加同一人:

{
  "code": 582101,
  "message": "该员工已分配此角色到此订单",
  "success": false,
  "data": null
}

业务边界

  • staffRole=GUIDE 现在与 LEADER 等价地触发 guide_status=DONE 与 GUIDE_DONE 流水
  • 流水姓名脱敏为首字符加星号,操作人由 operatorResolver 自动带出
  • 写完状态后触发 maybeAdvanceToConfirm,尝试推进订单资源就绪
  • DRIVER 与 OTHER 不落任何 staff 状态列
  • 团期来源行(source=GROUP_BATCH)不允许在订单侧增删改

3. 订单删除人员 DELETE /v3/admin/order/:id/staff/:staffAssignmentId

VO: StaffAssignmentVO

使用场景

订单详情「人员」区移除一名手工添加的服务人员。

入参

字段 位置 类型 必填 约束 说明
id Path Long ✅ — 订单 ID
staffAssignmentId Path Long ✅ — 人员配置行 ID

出参 Result<Void>

字段 类型 说明
data Null 无返回体,未变

请求示例

DELETE /v3/admin/order/990706300101/staff/2095758914858471425

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": null
}

空数据 / 降级响应

行不存在或不属于该订单:

{
  "code": 582102,
  "message": "员工分配记录不存在",
  "success": false,
  "data": null
}

错误响应

团期共享行不可在订单侧删除:

{
  "code": 582109,
  "message": "团期共享员工不可在订单侧增删改,请到团期配置页操作",
  "success": false,
  "data": null
}

业务边界

  • 删除后按导游位整组重新计数,而不是只数被删的那个角色
  • 导游位同时有导游和领队时,删掉其一 guide_status 保持 DONE
  • 整组归零时才把 guide_status 置空,且不写状态流水
  • 摄影位口径不变,仍只数 PHOTOGRAPHER

4. 打印行程单 GET /v3/admin/order/:id/print-itinerary

VO: PrintItineraryRespVO

使用场景

订单详情点「打印行程单」,抬头区展示司机 / 导游 / 领队的姓名与电话。

入参

字段 位置 类型 必填 约束 说明
id Path Long ✅ — 订单 ID

出参 Result<PrintItineraryRespVO>

字段 类型 说明
guideName String 导游姓名。取值口径变更:导游位内 GUIDE 优先,缺位回退 LEADER
guidePhone String 导游电话,同上口径
leaderName String 领队姓名,仍只取 LEADER 行,未变
leaderPhone String 领队电话,未变

请求示例

GET /v3/admin/order/990706300101/print-itinerary

响应示例

导游位只配了领队时,导游栏回退取到领队:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "header": {
      "guideName": "刘大山",
      "guidePhone": "13800001005",
      "leaderName": "刘大山",
      "leaderPhone": "13800001005"
    }
  }
}

空数据 / 降级响应

导游位无人时四个字段均为 null,接口仍成功:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "header": {
      "guideName": null,
      "guidePhone": null,
      "leaderName": null,
      "leaderPhone": null
    }
  }
}

错误响应

非本人名下订单且无数据权限:

{
  "code": 581008,
  "message": "无权查看此订单",
  "success": false,
  "data": null
}

业务边界

  • 只读接口,不产生任何写入
  • 导游位同时有导游和领队时,导游栏取导游,不取领队
  • leaderName 与 leaderPhone 语义不变,仍是领队本身
  • 导游与领队是同一人时两栏显示同一姓名,属预期

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

  • 导游位的角色集合是 GUIDE 与 LEADER 两个,前端配置弹窗可以把两类人员一起列出来,服务端不做取舍。
  • guideName 不再等价于「staffRole=GUIDE 那一行」,它是「导游位上的人」。要精确区分导游与领队请分别读 guideName 与 leaderName。
  • groupBatchId 在人员配置接口里是产品侧 batchId,不是 order_group_batch 主键,两者不可互换。
  • 判断「导游是否配齐」请读团期的 guide_ready 或订单的 guide_status,不要自行按角色字符串比对。

五、数据库行为

无 DDL、无迁移、无新增列。写入行为的变化:

表 列 变化
order_group_batch guide_ready 改前因 ID 空间用错从未被置位;改后按主键正确置 true
order_group_batch photographer_ready 同上
order_main guide_status staffRole=GUIDE 的行现在也会写 DONE;删除时按导游位整组判空
order_status_log — staffRole=GUIDE 现在也会产生一条 GUIDE_DONE 数据流水
order_batch_staff / order_staff_assignment — 结构与写入内容均未变

六、边界行为

  • 导游位配 GUIDE → guide_ready=true
  • 导游位配 LEADER → guide_ready=true
  • 导游位同时配两者 → markGuideReady 只调一次
  • 团期查不到 → ready 回填跳过并 WARN,接口不报错
  • 导游位删剩一人 → guide_status 保持 DONE
  • 导游位清空 → guide_status 置空且不写流水
  • 行程单导游位只有领队 → 导游栏取领队

六.5、枚举 / 数据字典

本次无新增枚举。staffRole 取值域与 SettlementStaffRoleEnum 保持一致,未扩展。

六.6、修改前后对比

项 变更前 变更后
guide_ready 回填 从未生效(用产品侧 batchId 打主键,rows=0 只 WARN) 反查真实主键后正确置位
导游位判定 只认 LEADER 认 GUIDE 与 LEADER
guide_status 写入 GUIDE 行不写 GUIDE 行同样写
GUIDE_DONE 流水 GUIDE 行不写 GUIDE 行同样写
删除后计数 只数被删角色,可能误置空 按导游位整组计数
行程单导游栏 只取 GUIDE 行,配领队时为空 GUIDE 优先,缺位回退 LEADER
接口路径 / 入参 / 出参结构 — 全部不变

六.7、影响评估

维度 评估
兼容性 只放宽不收紧。原来不触发的现在会触发,存量 staffRole=LEADER 的配置行行为与改前完全一致
前端 无需改动。路径、入参、出参结构均未变;行程单导游栏由「可能为空」变为「有值」,是修复不是破坏
数据 无 DDL、无迁移、无回填
性能 删除路径的计数由单值等值查询改为 IN 两值;行程单导游栏由一次点查改为一次整组查询后按序挑选,查询次数不增
回滚 git revert,单一 PR,无数据侧残留
风险 低。guide_ready 修复后会让原本推不动的团期开始推进资源就绪闸门,属预期恢复

七、不影响范围

  • 零影响:所有接口的路径、HTTP 方法、入参、出参结构
  • 零影响:摄影位口径,仍只收 PHOTOGRAPHER
  • 零影响:leaderName / leaderPhone 的语义与取值
  • 零影响:DRIVER 与 OTHER 角色的处理
  • 零影响:staff-fees 导游费用录入分流逻辑(裁决 D-2,配在导游位的领队不录导游费用,属预期)
  • 未新建端点,未改动任何路径与入参

八、测试环境已验证

✅ 2026-09-04 于测试环境网关实测,真实鉴权(管理端 test_admin,角色定制师)。

  • 网关 https://api.test.1814.love:9443,分支 dev-v3,合并提交 f37b6ee0f
  • 部署方式:双实例滚动更新(8186 → 8086),各 11s 就绪,零停机
  • 部署内容经部署面板 /api/git/backend 交叉核对:构建时点 f37b6ee0f 已是 dev-v3 顶端
# 用例 期望 实测
1 配 staffType=GUIDE 到导游位 guide_ready 置 1 ✅ 0 → 1(AC-2)
2 配 staffType=LEADER 到导游位 guide_ready 置 1 ✅ 0 → 1(AC-4)
3 配摄影师 photographer_ready 置 1 ✅ 0 → 1(AC-9)
4 订单侧配 staffRole=GUIDE guide_status=DONE + GUIDE_DONE 流水 ✅ 流水操作人 test_admin,内容脱敏为首字加星
5 导游位删剩领队 guide_status 保持 DONE ✅ 未被置空
6 行程单,导游位只有领队 导游栏取到领队 ✅ guideName 为领队姓名,电话一致
7 行程单,导游位两者都有 导游栏取导游 ✅ 导游优先生效
8 扇出 团期配置扇出到活跃子订单 ✅ 2 单,source=GROUP_BATCH

观察:部署后双实例持续 running 无重启,服务日志最近 200 行 ERROR / Exception 命中 0 条。 日志中可见 Parameters: 990706300101(Long), GUIDE(String), LEADER(String),即按导游位整组计数的查询确已在测试环境执行。

本地单测:AssignmentServiceTest 65 例、GroupBatchStaffConfigServiceTest 31 例、 AssignmentServiceCallbackDriverTest 42 例、OrderServiceTest 205 例、 架构与错误码门禁 51 例,合计 401 例全过,其中新增 7 例覆盖本次全部行为变化。

测试数据:测试环境所造数据一律 T7063- 前缀,验证后已物理删除,残留合计 0 条。 所用 test_admin 口令为一次性置换,取到令牌后立即按备份还原,与备份逐字节一致。

⚠️ 一处未做实测:团期人员配置的扇出链路(doFanOutForOrder)不调用 syncStaffStatusToOrder, 因此经团期配置这条路进来时 order_main.guide_status 始终为 null。用例 4 走的是订单侧新增人员端点。 这是先于本次变更就存在的第三处缺口,不在本波范围,已在工单中记录待另议。


九、相关历史 PR

  • #7065 本次变更(第 1 波 P1 止血)
  • #6962(Refs #6950)团期人员配置候选列表——本次修的正是它的下游遗漏
  • 裁决来源:2026-09-02「导游位并收 GUIDE 与 LEADER,服务端不替业务做取舍」

十、相关文档

  • 团期需求文档:docs/group/(dev-v3 分支)
  • 一期实施拆分详细设计 v1.0 §0.26.4(AC-TD-13~16)

关联 / 联系人

  • Issue: #7063
  • PR: #7065(合并提交 f37b6ee0f)