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:路径、入参、出参结构均未变,前端无需改动。
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 | 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)