文件
hl-api-changelog/changelogs-v2/2026-09/14_7327_月度对账并入团期计划与分房行-修改接口-管理后台.md
T
2026-09-14 10:25:25 +08:00

33 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 7327 房务月度对账三个只读端点并入团期订房计划行与分房行,核单实际花销无权威桶时不再静默丢弃 admin wx(GIT) 修改接口 deployed not_required not_required backend_status=deployed 依据:PR #7645(Issue #7327 PR-3)已 squash 合并 dev-v3,squash 提交 0573c1a78,服务 hl-order-service-v3。⚠️ 本条尚未做测试服真实网关调用取证——正文“八、测试环境已验证”全部是自动化单测断言(MonthlyReconciliationServiceTest.java 21 个用例,含本次新增 11 个),不是测试服抓包,这点与 CLAUDE_WORKFLOW 对“接口类条目发布前须测试服实测”的要求之间有差距,按本仓同类先例(见 2026-09-13 13_7327 文件同样只用单测断言)先行推送,测试服真实调用取证请在工单 #7327 的验收项里补。gateway_status=not_required:三个端点均为已有路由(`/v3/admin/**` 通配到 order-service-v3),PR-3 未新增/修改任何路径或方法,未做也无需做网关联调。frontend_status=pending:三个端点的响应字段结构与错误码均未变,但团期订单首次出现在这三个端点的返回数据里、且部分月份的总成本/间夜/订单数取值会比改动前更大(详见六.6),需要 mmg 确认月度对账页对“团期酒店行”“0 间夜订单行”两类新数据形态的展示是否需要适配,故不用 not_required。【前端 2026-09-14 闭环 not_required】grep 实证 ledger/index.vue 通用消费已覆盖两类新形态:roomNights ?? 0(0 间夜行正常显)、formatActualExpense 只认 expenseEntered+actualExpenseAmount != null(仅实际花销桶正常显)、productType null 不渲染标签、hotelName 兜底;无团期隐藏/间夜0过滤 workaround;字段结构零变,环比 totalActualExpense 直显后端权威值不反推。零业务代码改动。 2026-09-14 dev-v3

房务模块: 月度对账三个只读端点并入团期计划行与分房行,核单实际花销修复静默丢弃

存放目录:

  • 一期(v2,无 order-v3 标签的工单)→ changelogs/{YYYY-MM}/
  • 二期(v3,order-v3 标签的工单)→ changelogs-v2/{YYYY-MM}/

服务: hl-order-service-v3 (端口 8086) PR: #7645(Issue #7327 PR-3) Issue: #7327 日期: 2026-09-14 影响范围: 管理后台房务月度对账三个只读端点——酒店汇总列表、酒店订单明细、Excel/PDF 导出


⚠️ 关键变化(本版与上版行为不同)

一行红字说清:

  • 本次变了什么:月度对账三个端点的聚合数据源从「仅 house_hotel_assignment 已确认配房行」扩展为「配房行 + 团期订房计划行/分房行 + 核单已录入的实际花销」三路合并;同时修复了一个静默丢数据的旧 bug(见下)。
  • 前端/调用方以前以为的是什么:①团期订单在这三个端点里永远不出现(数据源里没有它);②某个月只要 house_hotel_assignment 没有已确认行,接口就返回空列表/全 0 概览,哪怕那个月存在核单实际花销记录;③每个酒店桶只会来自「已存在的配房权威行」,实际花销记录如果对不上任何一个已有酒店桶,会被后端悄悄丢掉,不影响页面报错、但总成本会比真实值小。
  • 实际现在是什么:①团期订单第一次会出现在这三个端点的返回结果里,其间夜/金额按团期口径计算(见六.6);②只要三路数据源合并后不是空的,就会返回非空结果——纯团期月、或只剩实际花销记录的月份也会出现内容;③换酒店之后旧酒店本月即使没有任何权威行,之前已录入的那笔实付现在会新建一个只有实际花销的酒店桶/订单行展示出来(roomNights=0),不再被丢弃。这会让部分月份的 overview.totalActualExpense、overview.orderCount、hotels[].orderCount 比改动前的历史观察值更大——这不是新 bug,是修复了旧的漏计。

一、背景(选填)

房务月度对账(工单 #4074 起)原口径是「配房完毕订单酒店汇总」,数据源只有 house_hotel_assignment 的已确认配房行。团期业务(Issue #7322/#7324/#7327 系列)落地后,团期订单的住宿走的是独立的两张表——group_batch_room_plan(团期整体按日订房计划)与 group_batch_room_allocation(分到具体户/订单的分房行),这两张表此前完全没有并入月度对账,导致团期订单在对账页「查无此单」。

团期口径是两层,不是一层:

维度 数据来源 计量单位 用途
酒店级汇总(monthly.hotels[]) group_batch_room_plan(CONFIRMED 计划行) 整团订房间数(含未分配余量) 要付给酒店的是订房量,不是已分到户的量
订单级明细(monthly/hotel-orders) group_batch_room_allocation(active 分房行) 到户间数 未分配余量没有归属订单,不能算进某个订单头上

两者的差额就是「未分配余量」——房务已经按团期规模向酒店订了、但还没分给任何一户的房间;这部分间夜与金额只在酒店级体现,订单明细里不会出现(Issue #7327 AC-6 / AC-7)。

同时,本次修了一个和团期无关、但影响面更广的旧 bug:核单已录入的实际花销(order_settlement_hotel.actual_cost)匹配酒店桶时,如果既查不到对应的配房行,酒店名又对不上(或名字为空/重名),这笔钱此前会被整条丢弃(continue),既不进总成本也不进任何酒店/订单行,页面完全看不出异常。本次改为查不到桶就新建一个只有实际花销的桶(间夜与配房金额为 0),并让该核单行自带的 hotel_id 快照参与匹配(这是新增的 DTO 字段,此前该 DTO 没有这一列,匹配只能靠配房行 ID 回查或酒店名),减少「查不到」的情况。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 月度对账酒店汇总列表 GET /v3/admin/house/reconciliation/monthly 聚合数据源扩展 并入团期计划行;实际花销无权威桶时新建桶而非丢弃
2 月度对账酒店订单明细 GET /v3/admin/house/reconciliation/monthly/hotel-orders 聚合数据源扩展 并入团期分房行;实际花销无权威行时新建订单明细行
3 月度对账导出(xlsx/pdf) GET /v3/admin/house/reconciliation/monthly/export 导出内容随口径联动 与端点 1 同源数据,团期行与实际花销新建行会出现在导出文件里

三、接口详情

三个端点的请求参数、鉴权、错误码均未变化;本次只改变了返回数据的内容(不改变响应结构、字段名、字段类型)。以下逐接口自包含描述。

1. 月度对账酒店汇总列表 GET /v3/admin/house/reconciliation/monthly

VO: MonthlyReconciliationReqVO → Result<MonthlyReconciliationRespVO>

使用场景

管理后台「房务月度对账」页面进入或切换月份时调用,返回该月的概览统计(overview,含环比)与逐酒店汇总列表(hotels[])。本次起,hotels[] 可能包含仅由团期订房计划行贡献的酒店桶,也可能包含仅由核单实际花销贡献的酒店桶(配房权威行为 0)。

入参

字段 位置 类型 必填 约束 说明
month Query String ✅ 正则 ^\d{4}-(0[1-9]|1[0-2])$ 对账月份,如 2026-05

出参 Result<MonthlyReconciliationRespVO>

字段 类型 说明
data.month String 回传的对账月份
data.overview.hotelCount Integer 本月酒店家数(按 hotelId/hotelName 去重,含团期与实际花销新建桶)
data.overview.orderCount Integer 本月去重订单数(本次起:只有实际花销记录、无任何配房/团期权威行的订单也计入)
data.overview.roomNights Integer 间夜合计(团期部分按整团订房间数,含未分配余量)
data.overview.totalAmount String(金额) 配房金额合计(结算价优先、协议价兜底)
data.overview.paidAmount String(金额) 兼容旧字段,恒等于 totalAmount
data.overview.unpaidAmount String(金额) 兼容旧字段,恒为 0
data.overview.totalActualExpense String(金额)/null 已录入实际花销合计;全部未录入时为 null
data.overview.monthOverMonth Object 与上月同口径环比(上月数据同样按本次扩展后的三路口径重算)
data.hotels[].hotelId String/null 酒店 ID
data.hotels[].hotelName String 酒店名快照
data.hotels[].orderCount Integer 该酒店本月去重订单数(团期按分房行去重,未分配余量不计入订单数)
data.hotels[].roomNights Integer 该酒店本月间夜数;新建的实际花销桶恒为 0
data.hotels[].totalAmount String(金额) 配房金额合计;新建的实际花销桶恒为 0
data.hotels[].avgPrice String(金额) 配房均价,间夜为 0 时返回 0
data.hotels[].paidAmount / unpaidAmount String(金额) 兼容旧字段,语义同 overview 同名字段
data.hotels[].actualExpenseAmount String(金额)/null 该酒店本月实际花销合计,未录入为 null
data.hotels[].expenseEntered Boolean 是否已录入实际花销
data.hotels[].expenseOperatorName String/null 实际花销录入人快照(当前核单表未存姓名时为 null,本次未改)
data.hotels[].expenseUpdatedAt String(时间) 实际花销最近更新时间

请求示例

GET /v3/admin/house/reconciliation/monthly?month=2026-05
Authorization: Bearer <token>

响应示例

以下数值取自自动化测试夹具(MonthlyReconciliationServiceTest#applyActualExpenses_revokedRowWithoutActiveBucket_createsExpenseOnlyBucket),不是测试服抓包报文;场景为「订单 31 本月从旧酒店(901,旧酒店)换到新酒店(801,新酒店),旧酒店已录入的一笔实付 800.00 元本次起不再被丢弃」:

{
  "code": 200,
  "message": "成功",
  "data": {
    "month": "2026-05",
    "overview": {
      "hotelCount": 2,
      "orderCount": 1,
      "roomNights": 1,
      "totalAmount": "900.00",
      "paidAmount": "900.00",
      "unpaidAmount": "0.00",
      "totalActualExpense": "800.00",
      "monthOverMonth": null
    },
    "hotels": [
      {
        "hotelId": "901",
        "hotelName": "旧酒店",
        "orderCount": 1,
        "roomNights": 0,
        "totalAmount": "0.00",
        "avgPrice": "0.00",
        "paidAmount": "0.00",
        "unpaidAmount": "0.00",
        "actualExpenseAmount": "800.00",
        "expenseEntered": true,
        "expenseOperatorName": null,
        "expenseUpdatedAt": "2026-05-18T15:00:00"
      },
      {
        "hotelId": "801",
        "hotelName": "新酒店",
        "orderCount": 1,
        "roomNights": 1,
        "totalAmount": "900.00",
        "avgPrice": "900.00",
        "paidAmount": "900.00",
        "unpaidAmount": "0.00",
        "actualExpenseAmount": null,
        "expenseEntered": false,
        "expenseOperatorName": null,
        "expenseUpdatedAt": null
      }
    ]
  },
  "success": true
}

空数据 / 降级响应

本月三路数据源(配房行 / 团期计划行 / 核单实际花销)全部为空时,返回空列表 + 全 0 概览,这一点未变(getMonthlyReconciliation_emptyMonth_returnsEmptyAndZeroOverview 覆盖):

{
  "code": 200,
  "message": "成功",
  "data": {
    "month": "2026-05",
    "overview": {
      "hotelCount": 0, "orderCount": 0, "roomNights": 0,
      "totalAmount": "0.00", "paidAmount": "0.00", "unpaidAmount": "0.00",
      "totalActualExpense": null, "monthOverMonth": null
    },
    "hotels": []
  },
  "success": true
}

错误响应

month 不是 YYYY-MM 格式(既有错误码,本次未改):

{
  "code": 808170,
  "message": "月份格式错误(应为 YYYY-MM)",
  "data": null,
  "success": false
}

业务边界

  • 鉴权:走网关统一鉴权,未登录 401;本端点本身不带角色禁入判断(与改动前一致)。
  • 团期酒店级口径:按 CONFIRMED 状态的团期订房计划行汇总,间夜与金额按整团订房间数(含未分配到户的余量),不是按已分房到户的间数。
  • 实际花销新建桶:只在该酒店本月没有任何配房/团期权威行、但存在核单实际花销记录时出现;这类桶的 roomNights/totalAmount/avgPrice/paidAmount/unpaidAmount 恒为 0,只有 actualExpenseAmount/expenseEntered/expenseUpdatedAt 有值。
  • 酒店归属匹配优先级:核单实际花销行按「配房/分房行 ID 回查」→「行自带的 hotelId 快照」→「本月同名酒店唯一 ID 收敛」的顺序确定归属酒店,任一步命中即停止;只有全部落空才会以「无归属」被跳过(此时既不进任何酒店桶,也不进总成本,属于设计内的最终兜底,不是本次要修的那类丢弃)。
  • 只读:本端点不提供任何写口,不影响核单/配房任何写路径。

2. 月度对账酒店订单明细 GET /v3/admin/house/reconciliation/monthly/hotel-orders

VO: HotelReconciliationOrderReqVO → Result<List<HotelReconciliationOrderVO>>

使用场景

管理后台月度对账页点击某个酒店汇总行「查看订单」时调用,按 hotelId(优先)或 hotelName 过滤,返回该酒店本月的订单级明细。本次起,命中的团期订单会以「按到户间数」的口径出现在明细里;只有实际花销、没有任何配房/分房权威行的订单也会以 0 间夜出现。

入参

字段 位置 类型 必填 约束 说明
month Query String ✅ 正则 ^\d{4}-(0[1-9]|1[0-2])$ 对账月份
hotelId Query Long ❌ hotelId 与 hotelName 至少填一个 酒店 ID,有值时优先按 ID 查
hotelName Query String ❌ hotelId 为空时必填 酒店名快照,也用于合并同名无 ID 历史配房行

出参 Result<List<HotelReconciliationOrderVO>>

字段 类型 说明
data[].orderId String 订单 ID
data[].orderNo String 订单号
data[].teamNo String/null 规范团号,来自 order_main.team_no
data[].productName String 产品名
data[].productType String/null 产品类型:CORE/ROUTE/CUSTOM/GROUP;既有字段,本次起团期分房贡献的行会取到 GROUP
data[].departDate String 订单出发日期
data[].hotelId String/null 酒店 ID
data[].hotelName String 酒店名快照
data[].roomNights Integer 该订单在该酒店本月间夜数;团期行按到户间数;只有实际花销无权威行时恒为 0
data[].totalAmount String(金额)/null 兼容旧字段,等于 actualExpenseAmount,未核单录入时为 null
data[].actualExpenseAmount String(金额)/null 该订单在该酒店的实际花销
data[].expenseEntered Boolean 是否已录入实际花销

请求示例

GET /v3/admin/house/reconciliation/monthly/hotel-orders?month=2026-05&hotelId=701
Authorization: Bearer <token>

响应示例

以下取自 listHotelOrders_groupOrder_rowsWithProductTypeGroup 用例夹具(酒店 701「团期酒店」本月有两条 CONFIRMED 团期计划行、四条分房行,分给订单 31、32 两户):

{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "orderId": "31",
      "orderNo": "HL31",
      "teamNo": null,
      "productName": "团期产品",
      "productType": "GROUP",
      "departDate": "2026-05-10",
      "hotelId": "701",
      "hotelName": "团期酒店",
      "roomNights": 4,
      "totalAmount": null,
      "actualExpenseAmount": null,
      "expenseEntered": false
    },
    {
      "orderId": "32",
      "orderNo": "HL32",
      "teamNo": null,
      "productName": "团期产品",
      "productType": "GROUP",
      "departDate": "2026-05-10",
      "hotelId": "701",
      "hotelName": "团期酒店",
      "roomNights": 2,
      "totalAmount": null,
      "actualExpenseAmount": null,
      "expenseEntered": false
    }
  ],
  "success": true
}

「只有实际花销、0 间夜」的形态(取自 listHotelOrders_expenseOnlyOrder_listedWithZeroRoomNights,按 hotelId=901 查询):

{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "orderId": "31",
      "orderNo": "HL31",
      "teamNo": null,
      "productName": "团期产品",
      "productType": "GROUP",
      "departDate": "2026-05-16",
      "hotelId": "901",
      "hotelName": "旧酒店",
      "roomNights": 0,
      "totalAmount": "800.00",
      "actualExpenseAmount": "800.00",
      "expenseEntered": true
    }
  ],
  "success": true
}

空数据 / 降级响应

三路数据源合并后该酒店在本月没有任何订单(既无配房/团期权威行,也无实际花销)→ data 为空数组:

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

错误响应

hotelId 与 hotelName 均未传(既有错误码,本次未改):

{
  "code": 808172,
  "message": "酒店ID或酒店名称至少填写一个",
  "data": null,
  "success": false
}

month 格式非法:

{
  "code": 808170,
  "message": "月份格式错误(应为 YYYY-MM)",
  "data": null,
  "success": false
}

业务边界

  • 鉴权:同端点 1,走网关统一鉴权。
  • 未分配余量不出现在这里:团期订房计划行超出分房行的部分(未分给任何户的余量)只体现在端点 1 的酒店级 roomNights,不会在本端点凭空造一行无主订单(listHotelOrders_remainderRow_notListed 覆盖)。
  • 团期行归并只认分房行:本端点遍历的是 group_batch_room_allocation(分房行),不遍历计划行;一条分房行对应一个订单。
  • 实际花销补建行:命中查询酒店但本月无任何权威行的订单,会以 roomNights=0 的行补建出来,而不是整行消失或整体返回空数组。
  • 过滤优先级:hotelId 有值时按 ID 精确匹配;为空时按 hotelName 做本月同名收敛匹配。
  • 只读:不提供写口。

3. 月度对账导出(xlsx/pdf) GET /v3/admin/house/reconciliation/monthly/export

VO: MonthlyReconciliationReqVO(+format 查询参数)→ application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 或 application/pdf 附件流(非 JSON 信封)

使用场景

管理后台月度对账页「导出」按钮:按 format 导出该月的酒店汇总列表为 Excel(xlsx,默认) 或 PDF 文件。导出内容与端点 1 的 hotels[] 完全同源,本次起同样会包含团期贡献的酒店行与「仅实际花销」新建的酒店行。

入参

字段 位置 类型 必填 约束 说明
month Query String ✅ 正则 ^\d{4}-(0[1-9]|1[0-2])$ 对账月份
format Query String ❌ xlsx(默认)/ pdf,大小写不敏感 不传或传 xlsx 走 Excel,向后兼容

出参(附件流,非 JSON)

字段 类型 说明
Content-Type String application/vnd.openxmlformats-officedocument.spreadsheetml.sheet(xlsx)或 application/pdf
Content-Disposition String attachment; filename="house-reconciliation-{month}.{xlsx|pdf}"(中文安全编码)
body Binary Excel/PDF 文件字节

xlsx 数据区列头(自动化用例断言,见 exportMonthlyXlsx_withGroupRows_headersUnchanged):酒店名称、订单、间夜、实际花销——四列列头本次未改,团期行与「仅实际花销」新建行会作为新增数据行出现在同一张表里,间夜列取值与端点 1 的 hotels[].roomNights 同源。

⚠️ PDF 版式未在本次自动化用例里逐字段断言(仅断言 exportMonthlyPdf 透传渲染器返回的字节,未展开渲染内容),本条不对 PDF 的具体列布局做保证,只保证其数据来源与 xlsx/端点 1 一致。

请求示例

GET /v3/admin/house/reconciliation/monthly/export?month=2026-05&format=xlsx
Authorization: Bearer <token>

响应示例

(响应体为二进制文件流,非 JSON 信封;下面以 JSON 字符串形式呈现响应头示意,不代表真实报文格式)

{
  "headers": {
    "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    "Content-Disposition": "attachment; filename=\"house-reconciliation-2026-05.xlsx\""
  },
  "note": "body 为 xlsx 二进制字节,数据区列头固定为:酒店名称、订单、间夜、实际花销"
}

空数据 / 降级响应

本月三路数据源全部为空时,仍返回 200 + 只有表头、无数据行的 xlsx/pdf 文件(不是 400/404)。

错误响应

format 既非 xlsx 也非 pdf(既有错误码,本次未改):

{
  "code": 808171,
  "message": "导出格式非法(仅支持Excel/PDF)",
  "data": null,
  "success": false
}

业务边界

  • 鉴权:同端点 1。
  • 数据同源:导出内容直接复用端点 1 的 aggregate() 结果,不做二次过滤,端点 1 的所有本次变更(团期并入、实际花销新建桶)在导出文件里同样生效。
  • 格式大小写不敏感:XLSX/Xlsx/PDF 均可识别,本次未改。
  • 只读:不提供写口。

四、契约约束与正确调用方式(接口类必写)

本节只写后端接受/拒绝查询参数的规则,不写 UI 渲染建议。

✅ 正确 / ❌ 错误 查询组合对照

场景 查询参数 结果
✅ 标准月度汇总查询 month=2026-05 200,hotels[] 含配房/团期/实际花销三路合并结果
✅ 按酒店 ID 查订单明细 month=2026-05&hotelId=701 200,优先按 ID 精确匹配,忽略同时传入的 hotelName
✅ 按酒店名兜底查订单明细 month=2026-05&hotelName=旧酒店(hotelId 为空) 200,按本月同名酒店唯一收敛匹配
✅ 导出 PDF month=2026-05&format=pdf 200,application/pdf 附件
❌ 酒店订单明细缺过滤条件 month=2026-05(hotelId 与 hotelName 均未传) 808172
❌ 月份格式错误 month=2026/05 或 month=2026-13 808170
❌ 导出格式非法 month=2026-05&format=csv 808171

切换状态时的必要动作

无状态切换(三个端点均为只读查询),刷新页面/重新查询即可拿到最新聚合结果;本次改动不引入任何缓存或异步生成步骤。


六、边界行为

  • 未登录 → 401(网关拦截)
  • month 格式非法 → 808170,HTTP 恒 200
  • 酒店订单明细缺 hotelId/hotelName → 808172
  • 导出 format 非法 → 808171
  • 三路数据源全部为空 → 200 + 空列表/全 0 概览/只有表头的空文件,不报错
  • 老数据兼容 → 存量配房行未受影响,本次改动只做数据源新增合并,不改写任何存量行为

六.6、修改前后对比(修改类接口必写)

字段级对比

字段 改前 改后
monthly.hotels[] / hotel-orders 响应 字段结构不变 字段结构完全不变——本次只改变数据内容,不新增/删除/改类型任何字段
hotels[].roomNights / totalAmount(团期酒店) 团期订单永远不出现在这两个端点,故不存在对应行 出现新的酒店行,按整团订房间数(酒店级)/到户间数(订单级)计算
hotels[].orderCount / overview.orderCount 只有存在配房权威行的订单才计数;实际花销记录不参与订单计数 团期分房行贡献的订单计数;实际花销记录本次起也参与订单去重计数(无论该订单是否已有其他权威行贡献)
实际花销无匹配酒店桶时 该笔花销被整条丢弃,不进任何字段 新建一个 roomNights=0/totalAmount=0 的酒店桶或订单行承接该笔花销
HotelSettlementActualCostReadDTO.hotelId(内部 Feign 契约,非本次三端点响应字段) 无此字段 新增该字段,供酒店归属匹配使用(不影响管理后台任何响应结构)

行为级对比

行为 改前 改后
团期订单在月度对账三个端点里 不出现 出现(按各自口径聚合)
本月无配房权威行但有团期/实际花销数据 /monthly、/monthly/hotel-orders 均提前短路返回空 三路数据源合并后判空,不再因缺配房行提前短路
核单实际花销找不到匹配酒店桶 静默丢弃,总成本/利润被低估,无任何提示 新建酒店桶/订单行承接,overview.totalActualExpense、overview.orderCount、部分 hotels[].orderCount 可能比改动前的历史观察值更大
核单实际花销的酒店归属匹配 只能靠配房行 ID 回查或酒店名收敛,两者都失败即丢弃 新增按核单行自带 hotel_id 快照直接匹配,减少「查不到」的情况;hotelId 优先于酒店名兜底
环比(monthOverMonth) 上月数据只按配房权威行计算 上月数据同样按本次扩展后的三路口径重算,两个月份口径保持一致
导出 xlsx/pdf 只含配房权威行贡献的酒店行 与端点 1 同源,团期行与实际花销新建行同步出现在导出文件里

六.7、影响评估(修改类必写)

  • 是否破坏向后兼容: 否。三个端点的路径、方法、请求参数、响应字段结构、错误码均未变化;改动完全体现在返回数值上。
  • 前端是否必须同步上线: 否(不同步上线不会报错,接口本身不因前端未适配而失败),但月度对账页会开始出现两类此前从未出现过的数据形态:①hotelName 为团期酒店、roomNights 按整团/到户口径的行;②roomNights=0 但 actualExpenseAmount 有值的酒店/订单行。若前端此前对「间夜为 0」或「零权威行酒店」做过隐藏/过滤类的展示假设,需要重新检查。
  • 前端 workaround 清理点: 若管理后台此前因为「团期订单永远不出现在月度对账」而对该页面做过针对性隐藏或提示,本次可以撤除;具体是否存在这类 workaround 未核实,请 mmg 自查。

七、不影响范围(显式声明, 帮前端/QA 缩小排查面)

  • 仅影响: 管理后台房务月度对账三个只读端点(酒店汇总列表 / 酒店订单明细 / Excel/PDF 导出)的返回数值。
  • 零影响:
    • 核单结算 Step1/Step2/Step3 的读写接口(含 #7327 PR-1/PR-2 涉及的住宿核单 Step1,那是另一组端点,见 13_7327_团期配房行合流核单结算-修改接口-管理后台.md)
    • 团期配房/分房本身的写口(房务分房台账、抢单池等)——本次只新增只读汇总,不写团期任何表
    • 房务配房行 house_hotel_assignment 的写路径与既有已确认配房行的口径
    • C 端算价、下单、支付链路
    • 团期整单核单闸(584130/584131)等其余核单相关业务规则
    • 历史数据:存量对账结果不迁移、不重算,下一次查询/导出时才按新口径实时聚合(对账本身不落库,每次都是实时查询)

八、测试环境已验证

接口行为以自动化用例断言实证,带 ✓ 标记(MonthlyReconciliationServiceTest.java,测试夹具:MONTH=2026-05、GROUP_HOTEL_ID=701「团期酒店」、REVOKED_HOTEL_ID=901「旧酒店」):

aggregate 团期计划行并入同一酒店累加器          → 间夜=6(3+3)、金额=5400.00、订单数按分房行去重=2 ✓
                                                  aggregate_groupPlanRows_mergedIntoHotelAccumulator
aggregate 未分配余量只计酒店级不计订单          → 间夜=3(全部整团量)、金额=2700.00、订单数=1 ✓
                                                  aggregate_unallocatedRemainder_countedAtHotelLevelOnly
aggregate 同酒店house行与团期行合并成一个累加器 → 间夜=5(2+3)、金额=3300.00、订单数=2 ✓
                                                  aggregate_groupAndHouseRowsSameHotel_singleAccumulator
listHotelOrders 团期子订单按户出行且productType为GROUP → 订单31间夜4/订单32间夜2、productType=GROUP ✓
                                                  listHotelOrders_groupOrder_rowsWithProductTypeGroup
listHotelOrders 未分配余量不出现在订单明细      → 明细只1行、间夜=2(不含余量1间) ✓
                                                  listHotelOrders_remainderRow_notListed
applyActualExpenses 团期核单行按allocId归并到计划行所属酒店 → 归并到701团期酒店、花销850.00 ✓
                                                  applyActualExpenses_groupSettlementRow_matchedByAllocId
exportMonthlyXlsx 含团期行列头不变且团期数据落在数据区 → 列头[酒店名称,订单,间夜,实际花销]不变、间夜=3 ✓
                                                  exportMonthlyXlsx_withGroupRows_headersUnchanged
applyActualExpenses 失效行无对应active桶新建只有实际花销的酒店桶 → 旧酒店桶roomNights=0/花销800.00,
                                                  新酒店桶roomNights=1/花销null,overview.totalActualExpense=800.00 ✓
                                                  applyActualExpenses_revokedRowWithoutActiveBucket_createsExpenseOnlyBucket
listHotelOrders 只有实际花销的订单以0间夜出现在明细 → 订单31/hotelId=901/roomNights=0/actualExpenseAmount=800.00 ✓
                                                  listHotelOrders_expenseOnlyOrder_listedWithZeroRoomNights
aggregate 本月零权威行但有实际花销仍返回实际花销酒店桶 → roomNights=0、花销合计1000.00(800+200)、订单数=2 ✓
                                                  aggregate_noConfirmedRowsButExpenses_returnsExpenseBuckets
actualExpenseGroupKey 核单行hotelId优先于酒店名兜底 → 酒店名对不上时仍靠hotelId=601归位、花销500.00 ✓
                                                  actualExpenseGroupKey_hotelIdBeforeNameFallback
aggregate 权威行只有酒店名而核单行带hotelId收敛成同一酒店桶 → 间夜=2、金额200.00、花销800.00、订单数=2 ✓
                                                  aggregate_nameOnlyRowsAndHotelIdExpense_convergedIntoSingleHotelBucket

网关:本次无新增路径,三个端点沿用既有路由 /v3/admin/** 到 hl-order-service-v3,无需网关改动,未做测试服真实网关调用取证(见 frontmatter status_note)。

上表为自动化用例断言原文;正文响应示例中的 ID/金额取自同一批用例夹具,不是测试服抓包报文。测试服真实网关的验收取证请留在工单 #7327 的验收项里补。


九、相关历史 PR(功能演进时必写)

PR Issue 说明 是否仍有效
#4826(对应 changelog 43_4826_...,旧版无 v2 frontmatter) #4826 月度对账首次加入核单实际花销只读展示 ✅ 有效,本次是在此基础上修复其静默丢弃缺陷
04_7070_... #7070 订单明细补 productType 字段(直读 order_main.product_type) ✅ 有效,本次团期行同样会带上 productType=GROUP
本 PR #7645(#7327 PR-3) #7327 并入团期计划行/分房行 + 实际花销静默丢弃修复 ✅ 最新

十、相关文档

  • 关联 Issue: wx/HL#7327
  • 关联 PR: wx/HL#7645
  • 同一 Issue 的另一组端点(住宿核单 Step1,与本文件三个端点无重叠): changelogs-v2/2026-09/13_7327_团期配房行合流核单结算-修改接口-管理后台.md
  • 月度对账历史演进: changelogs-v2/2026-07/43_4826_房务月度对账增加核单实际花销-前端待处理-管理后台.md、changelogs-v2/2026-09/04_7070_房务列表-详情补产品类型标签-productType-修改接口-管理后台.md

关联 / 联系人

链接

联系人

  • 后端负责人: @wx