文件
hl-api-changelog/changelogs-v2/2026-09/01_6905_团期看板-4-接口对齐补全-GB-ADM-000-001-002-003-修改接口-管理后台.md
T
API Changelog Bot和Claude Opus 5 41c0f752f9
changelog-filename-gate / validate (push) Failing after 2s
docs(changelog): #7972 接送机的免车闸与结算闸改为非对称判据,809114 放宽 / 584131 新增拒绝
PR #7976(572dc4037)改了两个既有错误码的抛出条件,零新增错误码:

- 809114(整团免车被派车阻塞)改为只看 TRAVEL——「只订接送机、没有团车」
  是合法的在团户,不该被上游拒绝免车,属于放宽。
- 584131(团期用车未就绪)对 TRANSFER 补独立的非对称判据:不存在放行 /
  DONE 放行 / 其余拒。原 TRAVEL 的抛出与放行结论逐字不变。
  整团免车现在只短路 TRAVEL 一支,两类互不豁免。

🔴 运维注意已写进正文:存量里停在 PENDING 的 TRANSFER 需求行,其所在户的
finalize 会从 200 变 584131——那是本次改动的预期行为,不是回归。

backend_status=deployed 依据 572dc4037 是测试服 order-v3 所在提交的祖先;
gateway_status=not_required 依据本次不涉及网关路由变更(仅后端判定条件)。
校验器 PASS,校验对象 4 个端点。

Refs #7972

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 17:35:19 +08:00

20 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 6905 团期看板 4 接口对齐补全 GB-ADM-000/001/002/003 admin wx(GIT) 修改接口 deployed verified verified mmg f6b849eb hl-ui@f6b849eb 2026-09-02 regionText 由上游 product-v2 提供后透传,当前测试环境返回 null 属预期;001 排序保持 create_time DESC 未变(depart_date ASC 变更待 wx 确认);#6929 已实现:003 totalPrice 应收字段 + birthdayInTrip 跨年修正(详见 §十一) 2026-09-20 dev-v3

团期看板 4 接口对齐补全 GB-ADM-000/001/002/003

服务: hl-order-service-v3 PR: #6923(网关拦截 PUT 无法 API 合并,采用等价 squash 推送落地 dev-v3,PR 已 closed 留痕) Issue: #6905 日期: 2026-09-01 影响范围: 管理后台团期看板 4 个查询接口(产品列表/团期分页/团期详情/团期订单列表)


⚠️ 关键变化

  • 本单只做查询接口的字段/筛选/性能对齐,未新建任何表/列,未新增写操作。
  • 运营阶段映射为团期八态(opsStage 桶+八态并集),本单不新增 ops_stage 字段/表。
  • 证件号与游客手机号永不返回(编译期字段裁剪 + 运行期实测零返回),前端不得依赖。
  • contactPhone 一期全量掩码(保前 3 后 4,如 138****0001)。
  • 001 排序保持 create_time DESC(将 depart_date 升序的变更留待 wx 确认,PR 与进度中已注明)。
  • regionText 由上游 product-v2 提供(order 侧仅透传),当前上游未提供时返回 null,产品域交接后由前端组验收展示。

一、背景

团期看板已实现 4 个查询接口(GB-ADM-000/001/002/003,见 #6902 共享件),本单按验收清单补齐全量字段、真实来源取值、批量聚合与筛选参数,消除 N+1。依赖 #6902(已合并 dev-v3)。

二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 产品团期看板(000) GET /v3/admin/order/group-batch/products 新增响应字段+入参 batchCount/regionText + productType 筛选
2 团期分页列表(001) GET /v3/admin/order/group-batch 新增筛选+响应字段 opsStage/month/keyword + chips/金额/orderCount
3 团期详情(002) GET /v3/admin/order/group-batch/<groupBatchId> 金额实时聚合+reporter totalReceivable/totalReceived 实时、primaryReporter 批量
4 团期订单列表(003) GET /v3/admin/order/group-batch/<groupBatchId>/orders 新增批量派生字段 成本/tier/人数/房车需求/掩码手机/游客(证件零返回)

三、接口详情

1. 产品团期看板 GET /v3/admin/order/group-batch/products

VO: GroupBatchProductItemRespVO

使用场景

管理后台看板"产品列表"页:按产品展示其团期批量数。

入参

字段 位置 类型 必填 约束 说明
productType Query String 否 - 产品类型筛选;不传=只看团期产品(上游恒定 GROUP 范围,详见「十一、复审补充知会」第 2 条勘误)
keyword Query String 否 - 产品名关键词(原有行为不变)

出参 Result<GroupBatchProductItemRespVO>

字段 类型 说明
batchCount Integer 该产品下活跃团期数(一次性 in 投影 product_id 后内存分组计数,无逐产品 count)
regionText String 产品地域文本(上游 product-v2 提供后透传;当前上游未提供时 null)

请求示例

GET /v3/admin/order/group-batch/products?productType=&keyword=

响应示例

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

空数据 / 降级响应

{
  "code": 200,
  "message": "成功",
  "data": []
}
  • 无数据 → 空数组,不报错。
  • 未登录 → 401(网关拦截)。

错误响应

{
  "code": 401,
  "message": "未登录或登录已过期",
  "data": null
}

业务边界

  • 未传筛选 = 不过滤,等价旧行为;regionText 为 null 时不阻断列表。

2. 团期分页列表 GET /v3/admin/order/group-batch

VO: GroupBatchPageItemRespVO

使用场景

管理后台看板"团期列表"页:分页 + 筛选(阶段桶/月份/关键词)。

入参(新增,均可选)

字段 位置 类型 必填 约束 说明
opsStage Query String 否 RECRUIT/FORMED/PENDING_TRIP/TRAVELLING/AUDITING/CHECKED/DISBANDED 七桶折叠;FORMED=RRESOURCE_PREPARING+MATERIAL_PREPARING 复合桶;未知/空=不过滤
month Query String 否 yyyy-MM 按出发日期所在自然月区间过滤
keyword Query String 否 - 匹配团期编号/名称,服务端 trim+转义 % _ \(防通配符扩匹配)

出参 Result<PageResult<GroupBatchPageItemRespVO>>

字段 类型 说明
receivableAmount BigDecimal 应收(批量 sum 实时聚合,非快照)
receivedAmount BigDecimal 实收(批量 sum 实时聚合,非快照)
chips Object 六芯片键:hotel/vehicle/guide/photo/contract/insurance(#6902 GroupBatchChipResolver 聚合)
orderCount Integer 该团期子订单数(批量 in 投影计数,无 N+1)

请求示例

GET /v3/admin/order/group-batch?pageNo=1&pageSize=5&opsStage=FORMED&month=2026-09&keyword=x

响应示例

{
  "code": 200,
  "message": "成功",
  "data": {
    "records": [],
    "total": 0
  }
}

空数据 / 降级响应

{
  "code": 200,
  "message": "成功",
  "data": {
    "records": [],
    "total": 0
  }
}
  • 无匹配 → records 空数组 total=0。
  • 排序保持 create_time DESC(向后兼容,未做排序变更)。

错误响应

{
  "code": 401,
  "message": "未登录或登录已过期",
  "data": null
}

业务边界

  • opsStage 非法/未知 = 不过滤(保守向后兼容);month 格式非法 → 业务 400。

3. 团期详情 GET /v3/admin/order/group-batch/<groupBatchId>

VO: GroupBatchDetailRespVO

使用场景

看板点击团期看详情:实时金额 + 主报道人。

入参

字段 位置 类型 必填 约束 说明
groupBatchId Path Long ✅ - 团期 ID(路径参数)

出参 Result<GroupBatchDetailRespVO>

字段 类型 说明
totalReceivable BigDecimal 应收实时聚合(sumBatchAmountsByProductBatchIds),非恒 0
totalReceived BigDecimal 实收实时聚合,非恒 0
primaryReporterId/primaryReporterName Long/String order_batch_staff 中 reporter_rank=PRIMARY 的首个;无 PRIMARY 配置时 null
secondaryReporterId/Name Long/String 副报道人(可 null)
advanceAmount BigDecimal 预付款(原字段,语义不变)

请求示例

GET /v3/admin/order/group-batch/<groupBatchId>

响应示例

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

空数据 / 降级响应

{
  "code": 200,
  "message": "成功",
  "data": null
}
  • 团期不存在 → 业务 404。

错误响应

{
  "code": 404,
  "message": "团期不存在或已被删除",
  "data": null
}

业务边界

  • PRIMARY 报道人未配置时两 reporter 字段为 null,不降级不报错。

4. 团期订单列表 GET /v3/admin/order/group-batch/<groupBatchId>/orders

VO: GroupBatchOrderItemRespVO

使用场景

看板展开订单列表:成本/tier/人数/房车需求/游客(含旅行内生日、无证件号)。

入参(新增,均可选)

字段 位置 类型 必填 约束 说明
includeTravelers Query Boolean 否 - 是否加载游客数组(不传=不加载,零开销)
includeNeeds Query Boolean 否 - 是否加载房车需求做派生聚合
includeCancelled Query Boolean 否 - 是否包含已取消子订单

出参 Result<List<GroupBatchOrderItemRespVO>>

字段 类型 说明
estimatedCost BigDecimal 🔴 2026-09-20 订正:该字段恒为 null,且已于 #7536 从 003 出参删除。原说明「估算成本(无成本数据可 null)」会让人以为只是暂时没数据——实际是从未有过数据。实测 origin/dev-v3:setEstimatedCost 与 getEstimatedCost 在 main 代码里各 0 命中,字段名在整个 Java 侧只出现在实体 OrderInfo.java 自身的声明里,其余全是 docs 与建表 DDL (V20260511_001__init_core_tables.sql:83 等)⇒ order_main.estimated_cost 全仓零写入点。逐户毛利的现行宿主见下方订正条。
totalPrice BigDecimal(字符串) 本户应收 = orderAmount + surchargeAmount − discountAmount(下限 0,OrderAmountUtil.payableForDisplay 口径;取消单返 "0.00",#7097 已补两位小数);预计毛利 = totalPrice − estimatedCost(#6929) 🔴 2026-09-20 订正:此式不成立,estimatedCost 恒 null(见上行),任何时点都算不出来。毛利改用核团接口,见「联调口径」第 4 条。
tierCode/tierName String tier 组合(成人A/儿童C/幼童Y/婴儿B,如 2A1C→"2成人1儿童";全零→null,映射表待 wx 确认)
participantCount Integer 人数聚合(adult+child+youngChild+baby)
youngChildCount/babyCount Integer 幼童/婴儿数
roomCount Integer 房数=逐晚需求 segment 房间数最大值;无需求行缺省 ceil(人数/2)
roomType String 最大房数晚的 roomCategory "、" 连接;无 → null
specialNeeds String 需求 special_tags ";" 连接 → 需求行 remark → 缺需求行 customer_remark
contactPhone String 全量掩码(保前 3 后 4)
travelerInfoComplete Boolean 游客信息完整(校验服务批量聚合)
travelers Array name/type/age/birthdayInTrip;不含 idNo/phone(编译期字段裁剪 + 运行期零返回)

请求示例

GET /v3/admin/order/group-batch/<groupBatchId>/orders?includeTravelers=true&includeNeeds=true&includeCancelled=true

响应示例

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

空数据 / 降级响应

{
  "code": 200,
  "message": "成功",
  "data": []
}
  • 无子订单 → 空数组。
  • 证件号/游客手机号永不返回(数据安全边界)。

错误响应

{
  "code": 401,
  "message": "未登录或登录已过期",
  "data": null
}

业务边界

  • include* 参数不传 = 不加载对应数据(零开销,向后兼容)。

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

  • 全部新增参数可选;不传 = 旧行为(向后兼容)。
  • opsStage=FORMED 代表"资源筹备中+物料筹备中"两态并集(复合桶),不是单一状态值。
  • 金额一律 BigDecimal 字符串(ToStringSerializer 序列化),前端按字符串处理,避免精度丢失。
  • 001 keyword 的 %_ 会被转义为字面匹配;需要模糊查询请用 % 以外的普通字符。
  • 003 的证件号/手机号不存在于任何响应,前端不得引用(编译期保证)。

五、数据库行为

  • 本单零写操作、零表/列变更、零迁移;全部为查询层字段/聚合/筛选对齐。

六、边界行为

  • 未登录 → 401(网关拦截)
  • 资源不存在 → 业务 404
  • 下游(order-stats/requirement)异常降级 → 金额/需求字段 null,不 500 不阻断页面
  • 老数据无新列 → 新增字段 null,不异常
  • 身份证号/手机号 → 永不返回(安全边界)

六.5、枚举 / 数据字典

opsStage(GroupBatchStageBuckets 七桶)

值 中文 折叠状态
RECRUIT 招募中 (created)
FORMED 已成团(复合桶) RESOURCE_PREPARING + MATERIAL_PREPARING
PENDING_TRIP 待出行 -
TRAVELLING 出行中 -
AUDITING 待审核 -
CHECKED 已审核 -
DISBANDED 已解散 -

tierCode 组合(成人A/儿童C/幼童Y/婴儿B)

值 说明
2A1C 2 成人 1 儿童(tierName="2成人1儿童")
全零 null(不输出)

六.6、修改前后对比

字段级对比

接口 字段 改前 改后
000 batchCount 无 批量 in 投影计数
001 receivableAmount/receivedAmount 无(或空) 实时聚合(2943.00/3270.00 测试实证)
001 chips/orderCount 无 六芯片聚合/批量计数
002 totalReceivable/totalReceived 实体快照 sumBatchAmounts 实时聚合(非恒 0)
002 primaryReporter 无 reporter_rank=PRIMARY 批量取值
003 estimatedCost/tier/人数 无 批量派生(🔴 estimatedCost 部分已于 2026-09-20 订正作废:它虽在出参里出现过,但从未被赋值,且已由 #7536 删除;tier 与人数不受影响)
003 contactPhone 原文 全量掩码
003 totalPrice 无 本户应收(payableForDisplay 口径,取消单返 "0.00",#6929/#7097)
003 travelers 无 name/type/age/birthdayInTrip(无证件号,跨年修正 #6929)

六.7、影响评估

  • 是否破坏向后兼容: 否(全部新增可选字段/参数)
  • 前端是否必须同步上线: 否
  • 前端 workaround 清理点: 无

七、不影响范围

  • 仅影响: 管理后台团期看板 4 个查询接口(响应新增字段 + 筛选参数)
  • 零影响:
    • C 端 / MP 端接口
    • 下单/支付/退款链路
    • 数据库表结构(零迁移)
    • #6902/#6903/#6904 已合并接口(本单未改其文件,仅顺带合并 dev-v3 时解决 GroupBatchMapper 尾部冲突取并集)

八、测试环境已验证

网关: https://api.test.1814.love:9443(/v3/admin/** → hl-order-service-v3,dev-v3 @ 47aaff0be,双实例 8086/8186 UP,BUILD SUCCESS 24.6s)

  • GET /v3/admin/order/group-batch?pageNo=1&pageSize=5 → 200 records=5 total=14,receivableAmount=2943.00 receivedAmount=3270.00,chips=hotel/vehicle/guide/photo/contract/insurance,orderCount=1 ✓
  • 筛选实证: base_total=14 → keyword=0 / opsStage(FORMED)=13 / month(2026-09)=12 ✓
  • GET /v3/admin/order/group-batch/products → 200 list=6,batchCount>0 产品 4/6(3/3/3/5),regionText=null(上游未提供,预期)✓
  • GET /v3/admin/order/group-batch/<groupBatchId> → 200 keys(34),totalReceivable=2943.00 totalReceived=3270.00(非恒 0)✓,primaryReporterId/Name 字段在位(无 PRIMARY 配置时 null)✓
  • GET /v3/admin/order/group-batch/<groupBatchId>/orders?includeTravelers=true&includeNeeds=true&includeCancelled=true → 200 orders=1,tierCode=2A tierName=2成人,roomCount=1,specialNeeds=脚本造单·…,contactPhone=138****0001(掩码)✓,travelerInfoComplete=true ✓,travelers len=2 keys=age,birthdayInTrip,name,type(无 idNo/phone)✓,样本: name=张伟 type=ADULT age=41 birthdayInTrip=false ✓

本地: 定向单测 19+15+27+8+3 全绿;模块全量 7832 用例仅 3 个 refund 401 用例失败且为 dev-v3 基线固有(git stash 对照同结果),与本单无关。

九、相关历史 PR

PR Issue 说明 是否仍有效
#6902 #6902 共享件(chip/stats/buckets) ✅ 依赖
#6917 #6904 看板统计条+导出(顺带合并冲突并集) ✅ 有效
#6923 #6905 本单 4 接口对齐(squash 等价落地 47aaff0be,PR 因网关禁 PUT 已 closed 留痕) ✅ 最新

十、相关文档

十一、复审补充知会(2026-09-01)

复审轮对 #6903/#6904/#6905 已上线行为的口径确认与勘误,无新接口、无字段结构变更;其中 2 项已开返工(见文末表)。

联调口径(现行行为,按此开发)

  1. 【001】无活跃子订单的团期 chips 整体为 null(不是六键全「待办」)。有单团期六键全在(聚合态四值:待办/处理中/已完成/异常;个别键可为 null)。渲染芯片前判 chips != null,null 时按「未开始」占位。
  2. 【000】productType 实际只能看团期产品:上游产品域只返回 GROUP 产品,传非 GROUP 值得空列表;「不限类型查普通产品」是面向隐式团的规划能力,当前不可达。§三.1 入参表原「不传=不限」描述有误,已就地勘误,以本条为准。
  3. 【003】demandStatus(本户需求态 SUBMITTED/CONFIRMED/REJECTED)不下发:契约卡 GB-ADM-003 有该字段但本期未实现,响应中不存在;原型名单表「打回 / 已重提」列暂无数据源,请先隐藏或恒占位,勿依赖。
  4. 【003】totalPrice(本户应收)已实现(#6929):「预计毛利 = totalPrice − estimatedCost」现可直接算(estimatedCost 已可用)。 🔴 2026-09-20 订正:上面这句是错误交接,请勿据此开发。estimatedCost 从 #6905(47aaff0be)透出那天起就恒为 null,不是后来才失效的——实测 origin/dev-v3:setEstimatedCost / getEstimatedCost 在 main 代码里各 0 命中(后者尤其关键:MyBatis-Plus 的 LambdaUpdateWrapper.set(Entity::getXxx, v) 用的是 getter 引用,只 grep setter 会整类漏掉),且该字段名在整个 Java 侧只出现在实体自身的声明里,没有任何别的 DTO/VO 带这个属性名 ⇒ 连 BeanUtil 那种反射拷贝也无从填它。该字段已于 #7536(6182d566d)从 003 出参删除。 ⇒ 「预计毛利」列在本接口上任何时点都算不出来。逐户毛利的现行宿主是核团接口 GET /v3/admin/order/group-batch/{groupBatchId}/audit 的 allocs[].grossProfit / allocs[].costAmount(GroupBatchAuditRespVO.java:204-208 声明,GroupBatchAuditService.java:742-743 真实填值;见 changelog 18_7932,亦即 14_7536:97「毛利改核单页」所指)。 📌 顺带订正出处:estimatedCost 由 #6905(47aaff0be) 引入,不是 df8dbea0c——后者只加了 totalPrice。totalPrice 为 JSON 字符串(ToStringSerializer),活跃单 = orderAmount + surchargeAmount − discountAmount(下限 0),取消单返 "0.00"(#7097 已补两位小数;JSON 为字符串,勿数值化)。请勿用 paidAmount + balanceAmount 自算应收(含退款场景口径不对)。
  5. 【003】include*=false 时扩展字段「键在、值为 null」:travelers / roomCount / roomType / specialNeeds 键仍存在、值为 null,判 null 即可,勿用 key in obj 判断。
  6. 【003】birthdayInTrip 跨年已修复(#6929):行程跨年(12 月1 月)时,出团年与返团年分别年化比较,任一落在行程闭区间即 true(如 12-2801-03 行程内 01-02 生日 → true);2-29 生日在非闰年落 2-28 不抛异常。

在途返工(上线后另行同步)

工单 内容 前端影响
#6926 008 导出补 opsStage;009 自校验修正;month 非法值容错 见 01_6904_* 第十一节
#6929 003 补 totalPrice;birthdayInTrip 跨年修正;内部双包装收敛 ✅ 已实现(PR #7057,部署验收后生效):totalPrice − estimatedCost 可直接算预计毛利 🔴 该半句 2026-09-20 订正作废(estimatedCost 恒 null,见「联调口径」第 4 条);totalPrice 本身已实现属实;跨年生日不再漏报;其余无感

关联 / 联系人

链接

  • Issue: #6905
  • PR: #6923(closed;网关拦截 PUT,采用等价 squash 推送落地)
  • Merge commit: 47aaff0be(dev-v3 落点)

联系人

  • 后端负责人: @wx