文件
hl-api-changelog/changelogs-v2/2026-09/10_7316_团期全团需求汇总补权限码与在团口径-结算汇总已核单数纠正-修改接口-管理后台.md
T
API Changelog Bot和Claude Fable 5.1 7b1601a7c2
changelog-filename-gate / validate (push) Successful in 2s
docs(changelog): 补全 #7316 的订正——第四节仍有三处「无权限码限制」与已订正段落自相矛盾
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>
2026-09-10 15:47:31 +08:00

34 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 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-v3 PR: #7407、#7408 Issue: #7316 日期: 2026-09-10 影响范围: 管理后台团期看板「全团需求汇总」面板 + 团期结算 D2 汇总面板(两个只读端点)


⚠️ 关键变化

两个端点的请求参数与响应结构均完全不变,本次共改四件事:

  1. 新增权限码校验: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,两码不复用。

  2. 数据口径纠正(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 所指的团移出。因此同一团期这个数字有的会变大、有的会变小,这是纠正原有错算,不是缺陷。
  3. 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 取值变化。
  4. 部署与实测状态:以上改动已合并 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 的角色权限码(Feign roleHasPermission),非本服务库表;远端异常按失败关闭处理,不放行。
  • 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 调用方的页面与角色范围(见「⚠️ 关键变化」末尾)