688c403 只订正了 :182 / :259 / :383 三处,同一文件里另有三处原样保留:
- :215 请求示例的 Authorization 头写「任意已登录管理端 token,无需 group-batch:view」
- :284 契约表行「✅ 任意已登录管理端角色调用」
- :288 「本端点无权限码限制」
其中 :284/:288 落在「四、契约约束与正确调用方式」——正是 mmg 据以实现判权与
589507 兜底的那一节,照它实现会完全不处理 589507,危害最大。
三处均已改为现行口径并标注订正日期,另补一条前端提醒:
FINANCE 角色持 finance:view 但不持 finance:advance,写端点按钮需单独控显隐。
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
34 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 | 7316 | 全团需求汇总补权限码 group-batch:view,activeOrderCount 口径改为在团(仅排除 CANCELLED,含 COMPLETED);团期结算 D2 汇总 settledOrderCount 改用 settled 标志判据 | admin | wx(GIT) | 修改接口 | deployed | verified | not_required | 本篇覆盖两个 PR:#7407(合并提交 d951c0469,全团需求汇总补权限码 + activeOrderCount 在团口径)+ #7408(合并提交 aafc6ae48,团期结算 D2 汇总 settledOrderCount 判据由 summary==null 改为 !Boolean.TRUE.equals(settled))。两次改动均已部署 TEST 环境(hl-order-service-v3 @ aafc6ae48,双实例 UP)并完成网关实测,backend_status=deployed、gateway_status=verified 均为真实态,实测证据见第八节。 | 2026-09-10 | dev-v3 |
团期模块:全团需求汇总补权限码 group-batch:view 并纠正在团口径 + 结算汇总已核单数纠正
服务:
hl-order-service-v3PR: #7407、#7408 Issue: #7316 日期: 2026-09-10 影响范围: 管理后台团期看板「全团需求汇总」面板 + 团期结算 D2 汇总面板(两个只读端点)
⚠️ 关键变化
两个端点的请求参数与响应结构均完全不变,本次共改四件事:
-
新增权限码校验:
GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary现在要求当前角色持平台权限码group-batch:view,校验在 Controller 入口执行,先于任何业务逻辑。不持有 → 业务错误码 589507(GROUP_BATCH_PERMISSION_DENIED,message:「无操作权限(非团期管理员 / 非本定制师名下)」,HTTP 仍 200)。此前该端点零判权,任一能过网关的后台账号都能读到整团逐户特殊需求(orderSpecialTags,忌口 / 婚房 / 无障碍等客户隐私)与逐日房型间数。持group-batch:view的角色为ADMIN/FINANCE/SUPER_ADMIN(hl-user-service迁移V20260831_002__add_group_batch_permissions.sql:16-25)。同一 Tab 内「看」用group-batch:view、「动」(确认 / 打回需求)用group-batch:demand:confirm,两码不复用。 -
数据口径纠正(PR #7407):
requirement-summary的activeOrderCount与settlement/summary的totalActiveOrderCount由「活跃子订单数(按productBatchId,额外排除COMPLETED)」改为「在团子订单数(按groupBatchId双通道并集,仅排除CANCELLED,含COMPLETED)」。- 后果 A:团期进入核单期后子订单多为
COMPLETED,旧口径会把这些整批滤掉,两处汇总常年趋近全 0 / 空;新口径能正常显示。 - 后果 B(前端会看到数字变化,务必知悉):新集合走「按
group_batch_id的双通道并集」——一跳直连order_main.group_batch_id优先占位,回退通道(按旧product_batch_id关联)仅在该行group_batch_id IS NULL时才补位。也就是说换过团的子订单按group_batch_id(权威归属)计入新团、从原product_batch_id所指的团移出。因此同一团期这个数字有的会变大、有的会变小,这是纠正原有错算,不是缺陷。
- 后果 A:团期进入核单期后子订单多为
-
settlement/summary的settledOrderCount取值口径修正(PR #7408,dev-v3 增补一):- 缺陷:
SettlementService.getSummary查不到order_settlement_summary行时返回的是settled=false的空壳 VO(仅orderId/advanceSummary有值),从不返回null;而GroupBatchSettlementService#summary侧原判据是if (summary == null) continue;,表达「该户尚未核单则跳过」,该分支恒不命中 →settledOrderCount恒等于在团户数(把没人核过单的团直接显示成「全团已核单」)。 - 为什么现在才暴露:#7407 合并之前,取数口径额外排除
COMPLETED,核单期这个集合本就为空、循环根本不执行,settledOrderCount恰好落在 0,看起来「对」;#7407 把集合补全为「在团」之后,循环开始真正执行,上述恒不命中的判据才第一次产生可观察的错误结果。 - 修复:判据改为
!Boolean.TRUE.equals(summary.getSettled())。 - 前端影响:
settledOrderCount从「恒等于在团户数」变成「真实已核单户数」,多数团期该数字会变小。若前端有「已核单 / 在团」的进度条或百分比展示,数值会随之变化(变准确)。subOrderTotalActualCost/subOrderTotalProfit的累加逻辑本身未变,只是现在只累加settled=true的户(此前判据虽然写的是「跳过未核单」,但因恒不命中,实际上从未跳过任何户,累加范围与本次修复后一致,金额侧数值不受影响,只有计数字段settledOrderCount变化)。 - 契约:请求与响应结构完全不变,仅
settledOrderCount取值变化。
- 缺陷:
-
部署与实测状态:以上改动已合并
dev-v3(PR #7407 合并提交d951c0469+ PR #7408 合并提交aafc6ae48),已部署 TEST 环境(hl-order-service-v3@aafc6ae48,双实例 UP)并完成网关实测,实测记录见「八、测试环境已验证」。
前端无需改代码即可正常运行(权限拒绝走通用错误展示);如页面把 requirement-summary.activeOrderCount 文案写成「活跃子订单数」,建议改为「在团子订单数」;另建议为该面板补权限态隐藏与 589507 专属提示文案(非强制)。若前端把 settlement/summary.settledOrderCount 用作进度条分子,请确认分母仍取 totalActiveOrderCount(本身不受本次修复影响)。
⚠️ 请 mmg 确认一件事(#7316 定案 10):请确认 hl-ui 调用 requirement-summary 的页面与角色范围。后端侧已核实:group-batch:view 在 origin/dev-v3 上已被同一块团期看板的多个只读路径要求(看板芯片 / 行程 / 合同 / 流团审批),因此能读这块看板的角色本来就持有该权限,本次是把最后一个漏网端点对齐;若 mmg 发现某页面被挡,处置办法是给该角色补 group-batch:view 种子,不改后端代码。
一、背景
GroupBatchRequirementService#summary 与 GroupBatchSettlementService#summary(D2 汇总)此前都调用 OrderService#selectActiveOrderIdsByProductBatchId(productBatchId) 取「活跃子订单」,其口径来自 assignment 域「还需派活」的语义(额外排除 COMPLETED)。团期进核单期后子订单大量变为 COMPLETED,导致两处汇总被整批滤掉。同时该只读端点透出的 orderSpecialTags(客户特殊需求)与逐日房型间数属敏感/运营数据,此前完全零权限校验。PR #7407(合并提交 d951c0469)一次性处理两处口径 + 补权限码。
PR #7407 合并后测试环境实测发现:settlement/summary 的 settledOrderCount 在「全团都没人核单」的场景下仍然显示等于在团户数(见「二、变更接口清单」#2 说明),根因是 GroupBatchSettlementService#summary 判「已核单」用的是 summary == null,而 SettlementService.getSummary 对未核单户从不返回 null(返回 settled=false 空壳)。PR #7408(合并提交 aafc6ae48)修正该判据,作为 #7316 的增补一并入同一工单。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 全团需求汇总 | GET | /v3/admin/order/group-batch/{groupBatchId}/requirement-summary |
行为修改 | 新增权限码 group-batch:view 校验 + activeOrderCount 口径改为「在团」 |
| 2 | 团期结算 D2 汇总 | GET | /v3/admin/order/group-batch/{groupBatchId}/settlement/summary |
行为修改 | totalActiveOrderCount 口径改为「在团」(同 #7407)+ settledOrderCount 判据改用 settled 标志,不再判 null(#7408) |
三、接口详情
1. 全团需求汇总 GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary
VO: 无请求体 → GroupRequirementSummaryRespVO
使用场景
团期看板「全团需求汇总」面板,团期管理员 / 定制师查看整团逐日房型需求、大巴座位需求、各户特殊需求汇总。调用前端必须持有权限码 group-batch:view 的角色(ADMIN / FINANCE / SUPER_ADMIN),否则拿到 589507。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
groupBatchId |
Path | Long | 是 | 团期主键(雪花 ID) | 团期 ID |
无 Body、无 Query 参数。
出参字段表 Result<GroupRequirementSummaryRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
data.activeOrderCount |
int | 在团子订单数(仅排除 CANCELLED,含 COMPLETED)——PR #7407 唯一改动的字段口径 |
data.hotelRequirementCount |
int | 已提交用房需求的子订单数 |
data.vehicleRequirementCount |
int | 已提交用车需求的子订单数 |
data.dailyRoomBreakdown |
Array<DailyRoomItem> | 逐日房间汇总(按 dayNumber 升序;不含客户自订晚 customerSelfBooked=true,某晚全团自订则无该晚条目) |
data.dailyRoomBreakdown[].dayNumber |
int | 行程天数(从 1 开始) |
data.dailyRoomBreakdown[].rooms |
Array<RoomTypeSummary> | 该天各房型合计 |
data.dailyRoomBreakdown[].rooms[].roomCategory |
String | 房型名称(如 双床房 / 大床房) |
data.dailyRoomBreakdown[].rooms[].totalRoomCount |
int | 合计间数 |
data.vehicleSeatSummary |
Array<VehicleSeatItem> | 大巴座位汇总(按车型) |
data.vehicleSeatSummary[].vehicleType |
String | 车型名称(如 大巴 / 商务车) |
data.vehicleSeatSummary[].totalSeats |
int | 合计座位数(seats × count 之和) |
data.vehicleSeatSummary[].totalCount |
int | 合计车辆台数 |
data.orderSpecialTags |
Array<OrderSpecialTagItem> | 各子订单 specialTags(每户特殊需求) |
data.orderSpecialTags[].orderId |
Long | 子订单 ID |
data.orderSpecialTags[].requirementType |
String | 需求类型(HOTEL / VEHICLE) |
data.orderSpecialTags[].specialTags |
Array<String> | 特殊需求标签列表 |
请求示例
GET /v3/admin/order/group-batch/1001/requirement-summary
Authorization: Bearer <持 group-batch:view 的管理端 token>
(路径示例中的 groupBatchId=1001 取自单测 GroupBatchRequirementSummaryTest 的 GROUP_BATCH_ID 常量,非真实团期;真实网关实测使用的团期 ID 见「八、测试环境已验证」。)
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"activeOrderCount": 3,
"hotelRequirementCount": 2,
"vehicleRequirementCount": 1,
"dailyRoomBreakdown": [
{ "dayNumber": 1, "rooms": [ { "roomCategory": "双床房", "totalRoomCount": 3 } ] },
{ "dayNumber": 2, "rooms": [ { "roomCategory": "大床房", "totalRoomCount": 3 } ] }
],
"vehicleSeatSummary": [
{ "vehicleType": "大巴", "totalSeats": 90, "totalCount": 2 },
{ "vehicleType": "商务车", "totalSeats": 14, "totalCount": 2 }
],
"orderSpecialTags": [
{ "orderId": 100, "requirementType": "HOTEL", "specialTags": ["高楼层", "婴儿床"] }
]
}
}
取值来源说明:以上字段结构与数值组合自源码仓库既有单测夹具(GroupBatchRequirementControllerTest#requirementSummary_success_returns200、GroupBatchRequirementSummaryTest 的逐日房间与大巴座位聚合用例),用于展示字段完整结构与嵌套形态,不是同一次真实网关调用的输出。本端点已在 TEST 环境完成真实网关实测(code/activeOrderCount 等具体数值见「八、测试环境已验证」),二者不是同一次调用,请以第八节的实测数值为准。
空数据 / 降级响应
团期无在团子订单时(selectInGroupOrderIdsByGroupBatchId 返回空列表),activeOrderCount / hotelRequirementCount / vehicleRequirementCount 均为 0,dailyRoomBreakdown / vehicleSeatSummary / orderSpecialTags 均为空数组,返回 200(GroupBatchRequirementSummaryTest#summary_noActiveOrders_returnsEmptySummary 覆盖)。本接口不调用外部 Feign / MQ(权限校验除外),无第三方降级分支。
错误响应
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "success": false, "data": null }
{ "code": 589500, "message": "团期不存在", "success": false, "data": null }
(589507 已在 TEST 环境网关实测复现,见「八、测试环境已验证」。)
业务边界
- 权限校验在 Controller 入口执行(
GroupBatchRequirementController#summary先调permissionGuard.require(...)再调 Service),先于任何业务逻辑,满足「事务内禁同步 Feign」。 - 持
group-batch:view的角色仅ADMIN/FINANCE/SUPER_ADMIN;其余角色(含CUSTOMIZER、VEHICLE_MANAGER等)一律 589507。 adminId或角色 key 缺失(未经网关鉴权透传)同样 fail-closed 直接拒绝 589507,不发 Feign。- 权限服务(user-service)Feign 异常 / 非成功响应 / 空值一律按无权限处理(fail-closed),不放行。
- 团期不存在返回 589500,判定顺序在权限通过之后(Service 层
groupBatchService.requireById判断)。 - 「看」与「动」权限码不复用:本端点只读用
group-batch:view;需求确认 / 打回等写动作用group-batch:demand:confirm(#7210)。 activeOrderCount与「## 2. 团期结算 D2 汇总」的totalActiveOrderCount同口径(同一selectInGroupOrderIdsByGroupBatchId通道),两个端点的计数应始终一致。
2. 团期结算 D2 汇总 GET /v3/admin/order/group-batch/{groupBatchId}/settlement/summary
VO: 无请求体 → GroupBatchSettlementSummaryRespVO
使用场景
团期看板「结算」Tab,团期管理员 / 财务查看整团核单进度(已核单户数 / 在团户数)与成本、毛利、共享成本、预支汇总。⚠️ 2026-09-10 订正:本篇发布时(#7316/PR #7407+#7408 合并时点)本端点确实零判权(当前登录态能过网关即可,不校验 group-batch:view,与「## 1.全团需求汇总」不同)——这不是有意的设计,而是 #7411 发现并修复的权限缺口(越权可读毛利 subOrderTotalProfit、团期预支 groupAdvanceApproved/groupAdvancePending 等敏感经营数据)。#7411(PR #7447,合并提交 856ab69bf)已给本端点补齐权限码 group-batch:finance:view,不持该码 → 589507(GROUP_BATCH_PERMISSION_DENIED,HTTP 恒 200)。详见 10_7411_团期核单共享成本三端点补判权-修改接口-管理后台.md。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
groupBatchId |
Path | Long | 是 | 团期主键(雪花 ID) | 团期 ID |
无 Body、无 Query 参数。
出参字段表 Result<GroupBatchSettlementSummaryRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
data.settledOrderCount |
int | 已核单子订单数(SettlementSummaryRespVO.settled=true 的数量)——PR #7408 唯一修复点:判据改用 settled 标志,不再判 summary==null(旧判据恒不命中) |
data.totalActiveOrderCount |
int | 在团子订单总数(仅排除 CANCELLED,含 COMPLETED,与「## 1.」的 activeOrderCount 同口径)——PR #7407 随口径切换改动 |
data.subOrderTotalActualCost |
BigDecimal | 子订单实际成本合计(Σ settlement_summary.totalActualCost,仅累加 settled=true 的户) |
data.subOrderTotalProfit |
BigDecimal | 子订单毛利合计(Σ settlement_summary.profitAmount,仅累加 settled=true 的户) |
data.sharedCostTotal |
BigDecimal | 团期共享成本合计(Σ order_batch_settlement.amount) |
data.sharedCostByType |
Array<SharedCostByTypeVO> | 团期共享成本按类型明细 |
data.sharedCostByType[].costType |
String | 成本类型(如 BUS) |
data.sharedCostByType[].costTypeDesc |
String | 成本类型展示标签(如 整团大巴) |
data.sharedCostByType[].total |
BigDecimal | 该类型成本合计 |
data.groupAdvanceApproved |
BigDecimal | 整团已拨付预支(scope=GROUP_BATCH 且 APPROVED 合计;不含子订单级;不计入 grandTotalCost) |
data.groupAdvancePending |
BigDecimal | 整团待审批预支(scope=GROUP_BATCH 且 SUBMITTED 合计;仅提示,不参与任何成本或毛利公式) |
data.grandTotalCost |
BigDecimal | 团期总成本(subOrderTotalActualCost + sharedCostTotal,不含预支) |
data.actualTravelerCount |
int | 实际出行人数(所有活跃子订单 adultCount+childCount+youngChildCount 之和) |
data.perPersonSharedCost |
BigDecimal | 人均共享成本(sharedCostTotal ÷ actualTravelerCount,出行人数为 0 时为 null) |
请求示例
GET /v3/admin/order/group-batch/10/settlement/summary
Authorization: Bearer <持 group-batch:finance:view 的管理端 token> # ⚠️ 2026-09-10 订正:发布时此处写「任意已登录 token」,那是缺陷不是设计,#7411(PR #7447 / 856ab69bf)已补判权
(路径示例中的 groupBatchId=10 取自单测 GroupBatchSettlementControllerTest,非真实团期;真实网关实测使用的团期 ID 见「八、测试环境已验证」。)
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"settledOrderCount": 3,
"totalActiveOrderCount": 4,
"subOrderTotalActualCost": "30000.00",
"subOrderTotalProfit": null,
"sharedCostTotal": "2000.00",
"sharedCostByType": [],
"groupAdvanceApproved": null,
"groupAdvancePending": null,
"grandTotalCost": "32000.00",
"actualTravelerCount": 15,
"perPersonSharedCost": "133.33"
}
}
取值来源说明:以上字段结构与数值取自源码仓库既有单测夹具(GroupBatchSettlementControllerTest#summary_success_returnsSummary),该夹具未设置 subOrderTotalProfit / groupAdvanceApproved / groupAdvancePending,故按未赋值字段的真实序列化结果标 null;用于展示字段完整结构,不是同一次真实网关调用的输出。本端点已在 TEST 环境完成真实网关实测,settledOrderCount(本次修复的字段)的实测数值见「八、测试环境已验证」,与本示例不是同一次调用,请以第八节为准。
空数据 / 降级响应
团期无在团子订单时(selectInGroupOrderIdsByGroupBatchId 返回空列表),settledOrderCount / totalActiveOrderCount / subOrderTotalActualCost / subOrderTotalProfit 均为 0;团期共享成本合计 sharedCostTotal 独立计算,不受在团订单数影响(即使 0 个在团订单,已录入的共享成本仍会汇总进 grandTotalCost);实际出行人数为 0 时 perPersonSharedCost 为 null(不除零),GroupBatchSettlementServiceTest#summary_zeroPeople_perPersonNull 覆盖该场景。本接口不调用外部 Feign(除 OrderService/SettlementService/GroupBatchAdvanceQueryService 内部契约),无第三方降级分支。
错误响应
{ "code": 589500, "message": "团期不存在", "success": false, "data": null }
业务边界
- 「已核单」判据只看
SettlementSummaryRespVO.settled标志,不能判null——SettlementService.getSummary对查不到order_settlement_summary行的子订单返回的是settled=false的空壳 VO(仅orderId/advanceSummary有值),从不返回null,这是 PR #7408 修复的唯一缺陷点。 totalActiveOrderCount与「## 1.全团需求汇总」的activeOrderCount同口径(在团 = 仅排除CANCELLED,含COMPLETED),两者用同一条OrderService#selectInGroupOrderIdsByGroupBatchId(groupBatchId)。- ⚠️ 2026-09-10 订正:本篇发布时本端点确实无权限码校验,任何能过网关鉴权的后台账号都能调用,与
requirement-summary的判权状态不同步(一个要group-batch:view、一个不要)——这不是既有设计,是缺陷:#7316发布后被#7411发现并列为安全缺口(同 Controller 的写端点POST .../settlement/cost同样零判权,任意后台账号可写入团期成本)。#7411(PR #7447,合并提交856ab69bf)已修复:本端点(GET .../settlement/summary)与GET .../settlement/cost现要求权限码group-batch:finance:view;POST .../settlement/cost要求group-batch:finance:advance;不持有对应权限码 → 589507。详见10_7411_团期核单共享成本三端点补判权-修改接口-管理后台.md。 subOrderTotalActualCost/subOrderTotalProfit只累加settled=true的子订单;未核单户是跳过不计入累加,不是计0累加(数学效果一致,但语义上是跳过)。groupAdvanceApproved/groupAdvancePending只计 scope=GROUP_BATCH的团期级预支,不含子订单级(子订单级预支已进各户核单报销单,整团层再算一次会重复扣回);groupAdvanceApproved/groupAdvancePending均不计入grandTotalCost(预支是资金拨付不是成本)。- 团期不存在 → 589500,判定在
groupBatchService.requireById完成。
四、契约约束与正确调用方式
全团需求汇总(GET .../requirement-summary)
| 场景 | 结果 |
|---|---|
✅ 当前角色(token 角色)持 group-batch:view |
200,正常返回汇总 |
❌ 当前角色不持 group-batch:view(含库角色持但 token 角色未持的分叉场景) |
589507 |
❌ 未经网关鉴权(X-Admin-Id/角色缺失) |
589507(fail-closed,不发 Feign 判权) |
❌ groupBatchId 对应团期不存在 |
589500 |
- 本端点无请求体,前端不需要拼装任何 payload,只需确保网关正确透传当前登录角色的鉴权头。
- 若某角色此前能看到该面板但今后应该被拒绝(或反之),须去
hl-user-service的admin_role_permission调整该角色是否持有group-batch:view,接口本身不提供角色开关。
团期结算 D2 汇总(GET .../settlement/summary)
| 场景 | 结果 |
|---|---|
✅ 持 group-batch:finance:view 的角色调用 |
200,正常返回汇总 |
❌ 不持 group-batch:finance:view(包括只持 group-batch:view 的账号) |
589507,HTTP 恒 200,判 Result.code(⚠️ 2026-09-10 订正:本行发布时写「任意角色可调」,已由 #7411 修复) |
❌ groupBatchId 对应团期不存在 |
589500 |
⚠️ settledOrderCount 小于 totalActiveOrderCount |
正常现象(本次修复后为真实已核单户数,不再恒等于在团户数),不是缺陷 |
- ⚠️ 2026-09-10 订正:本条发布时写「本端点无权限码限制」——那不是设计,是越权安全缺陷,已由 #7411(PR #7447,合并提交
856ab69bf)修复。 现行口径:本端点与GET .../settlement/cost均需group-batch:finance:view,POST .../settlement/cost需group-batch:finance:advance;不持码返 589507(HTTP 恒 200)。 详见10_7411_团期核单共享成本三端点补判权-修改接口-管理后台.md。 ⚠️ 前端注意:FINANCE角色持finance:view但不持finance:advance,即能看不能录,写端点按钮应单独按finance:advance控显隐。 - 前端若用
settledOrderCount / totalActiveOrderCount算「核单进度」百分比,修复后该比例会更小、更准确;无需改代码,只是数值会变化。
五、数据库行为
- 两个端点均只读,无写操作,无 Flyway、无表结构变更。
requirement-summary的权限判定读hl-user-service的角色权限码(FeignroleHasPermission),非本服务库表;远端异常按失败关闭处理,不放行。activeOrderCount/totalActiveOrderCount统计口径底层改调OrderService#selectInGroupOrderIdsByGroupBatchId(groupBatchId):一跳直连查order_main.group_batch_id(主通道,结果优先占位),若能解析出对应旧product_batch_id再查一次回退通道(仅补一跳没有覆盖、且该行group_batch_id IS NULL的订单),两者按orderId去重合并,全程只读、无写入。settledOrderCount判据依据SettlementSummaryRespVO.settled布尔标志,而非「该子订单是否存在order_settlement_summary行」的等价判断误用——SettlementService.getSummary在查不到行时会构造并返回settled=false的空壳对象(非null),本身不涉及任何写操作。
六、边界行为
- 未持
group-batch:view调用requirement-summary→ 589507(fail-closed,先于业务逻辑)。 - 团期不存在 → 两端点均 589500。
- 团期无在团子订单 →
requirement-summary计数全 0、列表全空;settlement/summary的settledOrderCount/totalActiveOrderCount/子订单成本合计均为 0,共享成本合计独立计算不受影响,不报错。 requirement-summary的逐日房间汇总不含客户自订晚(customerSelfBooked=true);某晚全团自订则该晚在dailyRoomBreakdown里无条目(既有规则,本次未改动)。- 权限服务(user-service)不可用时
requirement-summary同样按无权限处理,不因下游故障放宽判权;settlement/summary本身不依赖权限服务。 settlement/summary的未核单户不计入settledOrderCount,也不计入subOrderTotalActualCost/subOrderTotalProfit累加(跳过,不是计 0)。
六.5、枚举 / 数据字典
权限码 group-batch:view 的角色授权(来源:hl-user-service sys_role.role_key × admin_role_permission,迁移 V20260831_002__add_group_batch_permissions.sql:16-25)
所属字段: Controller 入口 permissionGuard.require(adminId, roleKey, GroupBatchPermissionGuard.PERMISSION_VIEW) | 类型: 角色 role_key 字符串
| role_key | 是否持有 group-batch:view |
|---|---|
ADMIN |
是 |
FINANCE |
是 |
SUPER_ADMIN |
是 |
其余角色(如 CUSTOMIZER / VEHICLE_MANAGER) |
否 → 589507 |
requirementType(GroupRequirementSummaryRespVO.OrderSpecialTagItem.requirementType)
所属字段: data.orderSpecialTags[].requirementType | 类型: String
| 值 | 含义 |
|---|---|
HOTEL |
该子订单的用房需求 specialTags |
VEHICLE |
该子订单的用车需求 specialTags |
settled(SettlementSummaryRespVO.settled,GroupBatchSettlementService#summary 判「已核单」唯一依据)
所属字段: SettlementService.getSummary(orderId) 返回值内部字段(不直接出现在 settlement/summary 响应里,是 settledOrderCount 计数逻辑的判据) | 类型: Boolean
| 值 | 含义 |
|---|---|
true |
order_settlement_summary 存在该子订单的核单行,VO 其余成本/毛利字段均有值 |
false |
查不到核单行,返回空壳 VO(仅 orderId/advanceSummary 有值,其余字段为 null)——不是 null 本身,PR #7408 之前误判 null 恒不命中 |
六.6、修改前后对比
字段级对比
| 字段 | 改前 | 改后 |
|---|---|---|
requirement-summary.activeOrderCount / settlement/summary.totalActiveOrderCount 语义 |
活跃子订单数(按 productBatchId,额外排除 COMPLETED,即 assignment 域「还需派活」口径) |
在团子订单数(按 groupBatchId 双通道并集,仅排除 CANCELLED,含 COMPLETED) |
| 取数方法 | OrderService#selectActiveOrderIdsByProductBatchId(productBatchId) |
OrderService#selectInGroupOrderIdsByGroupBatchId(groupBatchId) |
requirement-summary 权限要求 |
无 | 需要 group-batch:view,否则 589507 |
settlement/summary 权限要求 |
无 | 无(未变) |
settlement/summary.settledOrderCount 判据 |
summary == null → continue(getSummary 从不返回 null,恒不命中) |
!Boolean.TRUE.equals(summary.getSettled()) → continue |
| 两端点响应结构(字段名 / 类型 / 嵌套) | — | 完全不变 |
行为级对比
| 行为 | 改前 | 改后 |
|---|---|---|
团期核单期调用 requirement-summary/settlement/summary(子订单多为 COMPLETED) |
整批被旧活跃口径滤掉,汇总趋近全 0 / 空 | COMPLETED 计入在团,能正常显示 |
无 group-batch:view 的账号调用 requirement-summary |
200,能读到整团逐户 specialTags(客户特殊需求)与逐日房型间数 |
589507,拒绝读取 |
换过团的子订单(旧 product_batch_id 与当前团不一致) |
按 productBatchId 计入原(旧)团 |
按 group_batch_id(权威归属)计入新团,从原团移出 |
全团都没人核单的团调用 settlement/summary |
settledOrderCount 恒等于在团户数(错误显示「全团已核单」) |
settledOrderCount = 0(真实进度) |
部分核单的团调用 settlement/summary |
settledOrderCount 恒等于在团户数(不反映真实核单进度) |
settledOrderCount = 真实已核单户数(小于或等于在团户数) |
六.7、影响评估
- 是否破坏向后兼容:部分——①
requirement-summary新增权限,原来任意能过网关的后台账号都能调,现在非ADMIN/FINANCE/SUPER_ADMIN会从 200 变 589507;②activeOrderCount/totalActiveOrderCount与逐日 / 逐车汇总的口径变化会让同一团期返回的数字发生变化(核单期由少变多;跨团换团的户可能使某些团期数字变大、另一些变小,双向皆有可能);③settlement/summary.settledOrderCount从「恒等于在团户数」变成「真实已核单户数」,多数团期该数字会变小。 - 前端是否必须同步上线:否——不同步页面仍可用:
requirement-summary权限拒绝会走通用错误提示展示后端message;settlement/summary契约结构不变,数值自动变准确,无需前端配合。只是非授权角色看到的是通用文案而非专属提示,且字段文案「活跃子订单数」与新语义不完全贴合。 - 前端 workaround 清理点:若前端曾因「团期进核单期后全团需求汇总常年空白」做过特殊兜底或隐藏逻辑,可在确认新口径部署生效后清理;若前端曾把
settledOrderCount当作「总户数」展示或作为进度条分母,需确认分母取的是totalActiveOrderCount而不是settledOrderCount(settledOrderCount本身是分子,此前因缺陷恰好等于分母,掩盖了这个潜在误用,本次修复后会暴露出来)。
七、不影响范围
- 两个端点的请求参数、响应结构(字段名、类型、嵌套层级)本次完全不变,只变语义、取值口径与其中一个端点的鉴权。
- 团期需求确认 / 打回三个端点(
confirm/confirm-check/reject)使用的权限码仍是group-batch:demand:confirm,不受本次影响。 settlement/summary本身在#7316发布时没有新增权限码,本次(#7316)确实未改变其判权状态;⚠️ 2026-09-10 订正:但「与requirement-summary判权不同步」并非有意设计,而是#7411发现并修复的缺陷——#7411已给settlement/summary、settlement/cost(GET/POST)三端点补齐group-batch:finance:view/group-batch:finance:advance判权,详见10_7411_团期核单共享成本三端点补判权-修改接口-管理后台.md。- 无 Flyway、无表结构变更、无新端点、网关路由零改动,只涉及
hl-order-service-v3单模块。
八、测试环境已验证
环境:测试服网关 https://api.test.1814.love:9443,hl-order-service-v3 @ aafc6ae48(含 PR #7407 + #7408),双实例 UP。
| 场景 | 请求 | 结果 |
|---|---|---|
| 无权限 | CUSTOMIZER token 调 requirement-summary(团期 2096412454643802114) |
code=589507,data=null |
| 有权限 | ADMIN token 调同一端点 | code=200,activeOrderCount=55、hotelRequirementCount=53、vehicleRequirementCount=1 |
| CANCELLED 不计 | 同一团 72 单中 17 单 CANCELLED |
activeOrderCount=55(72−17),5 个已知 CANCELLED 订单号在响应里 grep 命中数均为 0 |
| 全 COMPLETED 团 | 团期 2097498512387104770(3 户全部改成 COMPLETED 后) |
activeOrderCount=3、dailyRoomBreakdown 非空、orderSpecialTags 非空;同一份数据按旧口径手算为 0 |
| 已核单数 | 同一团,3 户中仅 1 户有 order_settlement_summary 行 |
settledOrderCount=1(不是 totalActiveOrderCount=3)、subOrderTotalActualCost=3000.00、subOrderTotalProfit=7000.00 |
换团归属对照(说明为什么部分团期数字会变):
| 团期 | 旧口径手算 | 实际返回 |
|---|---|---|
2096495107078328322(#7158验收班期) |
2 | 1(该户已换团,从本团移出) |
2096510069465088002(#7178验收班期) |
0 | 1(该户权威归属是本团,移入) |
造数说明:上述全 COMPLETED 团与核单行均为取证造数,取证后已全部删除并二次核对恢复。
十、相关文档
- 团期看板权限地基 #6902:
group-batch:list/group-batch:view/group-batch:export三权限码首次注册(V20260831_002__add_group_batch_permissions.sql)。 - 团期需求确认 / 打回改造 #7210:
group-batch:demand:confirm权限码,同一GroupBatchPermissionGuard类。 - 团期财务总览与预支 #7154:
groupAdvanceApproved/groupAdvancePending的 scope=GROUP_BATCH预支口径来源。
关联 / 联系人
链接
联系人
- 后端负责人: wx
- 待确认对象(前端 hl-ui): mmg——请确认
requirement-summary调用方的页面与角色范围(见「⚠️ 关键变化」末尾)