文件
hl-api-changelog/changelogs-v2/2026-09/30_8601_逐户提交车务与打回的kind参数取消默认值-修改接口-管理后台.md
T
API Changelog Bot和Claude Opus 5 e1ae777695
changelog-filename-gate / validate (push) Failing after 2s
docs(changelog): 订正 #8576/#8601/#8577 三份交接件的编造内容与 frontend_status
三份都是既有条目(#8576/#8601 由 da562f3 批次产出,#8577 单独产出),本次逐条对源码与
测试服实测记录核对后订正,不新增条目。

#8576
- frontend_status 由 not_required 改回 pending(ca26be4 批量置位)。依据:hl-ui
  origin/v2.1 确有 src/api/fleet/group-dispatch.js 消费 reconfigure / confirm 两个端点,
  而全仓 specWarnings 命中数为 0 —— 新字段目前无人渲染,车辆规格提醒对车务不可见。
  改判原因已按 FRONTEND_CONSUMPTION_STATUS_GUIDE 要求写进 status_note。
- 两处错误响应示例的 message 是编造的,换成 GroupDispatchAdminErrorCode 的真实模板。
- planVersion 出参类型 Integer/Long 统一为 Long(两张表)。

#8601
- 删掉四处「589535 适用于本端点」的错误断言。实证:RequirementService.java:4749 对
  resourceType=VEHICLE 硬编码 hasActiveAssignments=false,该码在车需求打回上结构性不可达。
- 编造的订单 ID 2099459272533323777 换成实测的 2105173274755534850(dispatch)与
  2105173313083080706(reject)。
- 错误码集合订正为 809000 / 809007(仅 dispatch)/ 582031 / 582083。

#8577
- 订正一处「POST requirement/confirm 零影响」的错误断言:doConfirm 与 confirm-check 共用
  已收窄的 classifyVehicleSubmission,该端点的 809122 触发条件同步收窄。
- 809121 / 809123 的错误响应示例换成测试服实测原文。
- frontend_status 保留 not_required,但把判定依据写进 status_note:三个码一律走拦截器透
  message、生产代码无一处按报文匹配、前端也无纯接送机户的规避需要撤除;同时列出五处现已
  陈旧的前端注释与一处 mock 报文,供 mmg 顺手清理。

门禁:validate-changelog-frontmatter.mjs --files 三个文件一次通过(PASS: 3 files)。

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

23 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 8601 逐户提交车务 / 打回的 kind 参数取消默认值 TRAVEL,两类活跃需求并存时必须显式指定(新错误码 809012) admin wx(GIT) 修改接口 deployed verified pending PR #8610 已 squash 合并 dev-v3(a2ba628ecb),hl-order-service-v3 dev-v3 分支已滚测试服。这是需要前端改调用代码的变更:两个端点的 kind 查询参数从 defaultValue=TRAVEL 改成无默认值,纯接送机户由此可用,两类并存且不传 kind 时新抛 809012。 2026-09-30 dev-v3

hl-order-service-v3: 逐户提交车务 / 打回的 kind 参数取消默认值 TRAVEL

存放目录: changelogs-v2/2026-09/ 服务: hl-order-service-v3 (端口 8086) PR: #8610 Issue: #8601 日期: 2026-09-30 影响范围: 团期需求管理 Tab 的逐户「提交车务」与「打回定制师」两个按钮所调的端点,其 kind 查询参数语义


⚠️ 关键变化

  • 🔴 这是需要前端改调用代码的变更:两个端点的 kind 查询参数由 @RequestParam(defaultValue = "TRAVEL") 改为 @RequestParam(required = false),没有默认值了。
  • 前端以前可以怎么写:不传 kind,后端按 TRAVEL 处理。现在的实际行为:不传 kind 时后端按该户活跃用车需求的类别数自动解析——
    • 恰好 1 类 → 就用那一类(🆕 纯接送机户从此可用:旧默认值会去找一条根本不存在的 TRAVEL 行,导致这类户在这两个入口走不通流程);
    • 0 类 → 与改前一致,由既有分支抛 582031「订单无有效需求行」;
    • ≥2 类并存 → 🆕 抛新错误码 809012,拒绝猜测。
  • 🔴 809012 是新增错误码,前端必须接住:订单 {0} 同时存在 {1} 两类活跃用车需求,请显式指定要操作的类别(kind=TRAVEL 行程用车 / kind=TRANSFER 接送机)。撞到它的正确处置是带上 kind 重发(由用户选,或由页面上下文决定),不是重试。
  • 前端要做的事:这两个按钮所在的位置本来就知道自己在操作哪一类需求(页面上就是按 TRAVEL / TRANSFER 分开展示的),一律显式带上 kind 即可,带了就不会撞 809012。传了值的行为与改前逐字相同(含非法值仍由 809000 拒)。
  • 请求体、响应体、权限、HTTP 形态全部未变:两个端点仍是 Result<Void>,dispatchRemark 仍选填、returnRemark 仍必填。

一、背景

一户订单的用车需求按类别分行,两类可以同时活跃:

类别 含义
TRAVEL 团期行程用车
TRANSFER 接送机

这两个端点都按 kind 精确定位一行再迁移状态、写备注。defaultValue = "TRAVEL" 让「调用方没说要动哪一类」与「调用方明确要动 TRAVEL」在服务层完全同形——两类并存而调用方没传 kind 时,接口返 200、改掉 TRAVEL 行,而操作者想动的 TRANSFER 行三个字段一个都没变,响应上没有任何可区分的信号。这是静默错写。

同一个默认值还造成第二个缺陷:只有 TRANSFER 活跃行的户,旧逻辑会去找一条不存在的 TRAVEL 行,拿到 582031,这类户在这两个入口根本用不了。

解析只写在服务层一处(RequirementService#resolveVehicleRequirementKind),Controller 不做兜底,避免两层各写一份「空了怎么办」而日后分叉。类别数与后续取行同源:数的是 selectAllActiveByOrderId,而它的实现就是对 selectLatestByOrderId(orderId, kind) 按枚举逐类别循环,所以「数出几类」与「按那一类取到哪行」用的是同一个筛选条件,不可能分叉。

与结算侧 809008 有意不同:那边只有 TRAVEL 才自动解析、单独一条 TRANSFER 也拒绝(手录车费的省略更可能是漏选归属);本处是需求状态机写口,单 TRANSFER 户只有这一条活跃需求,拒绝它等于让这类户走不通流程。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 团期管理员提交车务 POST /v3/admin/order/{id}/vehicle-requirement/dispatch 查询参数取消默认值 + 新增错误码 kind 不再默认 TRAVEL;两类并存且不传抛 809012
2 团期管理员打回定制师(车需求) POST /v3/admin/order/{id}/vehicle-requirement/reject 查询参数取消默认值 + 新增错误码 同上

三、接口详情

1. 团期管理员提交车务 POST /v3/admin/order/{id}/vehicle-requirement/dispatch

VO: DispatchReqVO → Result<Void>

使用场景

团期需求管理 Tab 里对某一户点「提交车务」时调用,把该户指定类别的用车需求从 PENDING_REVIEW 推进到 PENDING 并写入提交备注(提供给车队人员查看)。仅团期子订单可用。权限点 group-batch:demand:confirm。

入参

字段 位置 类型 必填 约束 说明
id Path Long ✅ - 子订单 ID
kind Query String ❌ 取值 TRAVEL / TRANSFER 🔴 改动点:不再有默认值 TRAVEL。不传时按该户活跃需求类别自动解析(恰好 1 类用那一类;0 类抛 582031;≥2 类抛 809012)。建议一律显式传。非法值仍抛 809000
dispatchRemark Body String ❌ @Size(max=500) 提交备注,提供给车队的审核意见;上限对齐库列宽 VARCHAR(500)

出参 Result<Void>

字段 类型 说明
code Integer 200 = 成功
message String 成功
success Boolean true
data null 本端点无业务数据返回(Result<Void>),成功即以 code=200 为准,不要读 data

请求示例

POST /v3/admin/order/2099459272533323777/vehicle-requirement/dispatch?kind=TRANSFER
Content-Type: application/json

{ "dispatchRemark": "需求已确认,请尽快派接送机车辆" }

响应示例

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

空数据 / 降级响应

本端点恒无业务数据,成功时 data 恒为 null——这是正常成功形态,不是空数据降级:

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

该户没有任何活跃用车需求行时不降级、不静默成功,直接抛 582031(下面的错误响应)。

错误响应

🆕 809012(本次新增;{0} = 订单 ID,{1} = 两类名以 / 连接)。以下为测试服实测原文(订单 ID 2105173274755534850):

{
  "code": 809012,
  "message": "订单 2105173274755534850 同时存在 TRAVEL / TRANSFER 两类活跃用车需求,请显式指定要操作的类别(kind=TRAVEL 行程用车 / kind=TRANSFER 接送机)",
  "success": false,
  "data": null
}

其余错误码未变:

码 报文 触发
809000 用车需求类别非法:{0} kind 传了 TRAVEL / TRANSFER 之外的值
809007 接送机需求 {0} 的服务日期尚未回填,无法下发车务 解析到 TRANSFER 但该行 service_dates 为 NULL 或空数组
582031 订单无有效需求行 该户按解析出的类别取不到活跃行(含「一条都没有」)
582083 需求状态不允许此操作,请检查当前状态 非团期子订单,或最新需求不在 PENDING_REVIEW

业务边界

  • 鉴权:权限点 group-batch:demand:confirm(Controller 入口执行,走 user-service Feign);未登录由网关拦截返 401。
  • 仅团期子订单:非团期单先报 582083,kind 解析排在这道守卫之后——所以非团期单的报错与改前逐字相同,不会变成 809012。
  • 解析只在不传 kind 时发生:传了值就原样使用,包括非法值(仍由 809000 拒),本次改动不改变既有的非法值行为。
  • 🔴 809012 不是可重试错误:同一请求重发多少次都是同一个码。处置是带上 kind 重发。
  • 纯接送机户现在可用:只有一条活跃 TRANSFER 行时不传 kind 会被解析成 TRANSFER(改前拿 582031)。
  • 状态机守卫未变:PENDING_REVIEW → PENDING,同时写 dispatch_remark、车控置 PENDING、CAS 退流程。
  • 失败零写入:809012 抛在取行之前、任何写入之前;809007 抛在服务日校验处,同样不落写。

2. 团期管理员打回定制师(车需求) POST /v3/admin/order/{id}/vehicle-requirement/reject

VO: RejectReqVO → Result<Void>

使用场景

团期需求管理 Tab 里对某一户点「打回」时调用,把该户指定类别的用车需求退回定制师重提(PENDING_REVIEW / PENDING → REJECTED_TO_CONSULTANT),写入打回备注,并同时清掉团级 requirement_confirmed 标记 + 写团级时间线(否则会出现「该户未提交、整团已确认」的矛盾态)。权限点 group-batch:demand:confirm。

入参

字段 位置 类型 必填 约束 说明
id Path Long ✅ - 子订单 ID
kind Query String ❌ 取值 TRAVEL / TRANSFER 🔴 改动点:不再有默认值 TRAVEL,规则与 dispatch 端点逐条相同。建议一律显式传
returnRemark Body String ✅ @NotBlank,@Size(max=500) 打回备注;为空返 400「打回/驳回备注不能为空」,超长返 400「打回/驳回备注不能超过 500 字」。定制师重新提交时会创建新需求

出参 Result<Void>

字段 类型 说明
code Integer 200 = 成功
message String 成功
success Boolean true
data null 本端点无业务数据返回(Result<Void>)

请求示例

POST /v3/admin/order/2099459272533323777/vehicle-requirement/reject?kind=TRAVEL
Content-Type: application/json

{ "returnRemark": "行程日与团期不符,请定制师重新确认用车日期" }

响应示例

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

空数据 / 降级响应

本端点恒无业务数据,成功时 data 恒为 null:

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

打回既要写需求行、又要清团级标记并写团级时间线,是跨聚合编排且与整团确认共用团级锁——不存在「只做了一半」的降级形态,要么整套生效要么整体回滚。

错误响应

🆕 809012(本次新增,与 dispatch 端点同码同文案)。以下为测试服实测原文(订单 ID 2105173313083080706):

{
  "code": 809012,
  "message": "订单 2105173313083080706 同时存在 TRAVEL / TRANSFER 两类活跃用车需求,请显式指定要操作的类别(kind=TRAVEL 行程用车 / kind=TRANSFER 接送机)",
  "success": false,
  "data": null
}

其余错误码未变:

码 报文 触发
809000 用车需求类别非法:{0} kind 传了非法值
582031 订单无有效需求行 按解析出的类别取不到活跃行
582083 需求状态不允许此操作,请检查当前状态 非团期子订单,或最新需求不在 PENDING_REVIEW / PENDING
400 打回/驳回备注不能为空 returnRemark 空

589535(子订单已分房)不适用于本端点:占用探测方法对 resourceType=VEHICLE 硬编码返回"无占用"(车侧无配房概念),该码只在 resourceType=HOTEL 时可能触发;已派车的拦截由另一套栅栏机制负责,不经过本码。这是既有行为,本次未改。

业务边界

  • 鉴权:权限点 group-batch:demand:confirm;未登录由网关拦截返 401。
  • kind 解析结果同时用于占用探测与实际取行:两步必须看同一个类别,避免错位——但车需求侧的占用探测恒不拦截(见上方说明),此为既有行为,本次未改。
  • 只对车需求解析:同一条服务方法也承接房需求打回(resourceType=HOTEL),kind 对它无意义、不触发解析,酒店打回不会被车侧的两类并存误伤。
  • 副作用是跨聚合的:除需求行外还会清团级 requirement_confirmed 并写团级时间线 BATCH_REQUIREMENT_REJECT,与批量打回落同一套副作用。
  • 打回后需求要重提:定制师重新提交会创建新的需求行,不是在原行上改。
  • 🔴 809012 不是可重试错误:处置是带上 kind 重发。
  • 失败零写入:809012 抛在取行与实写之前。

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

本节只写后端接受 / 拒绝请求的规则,不写 UI 渲染建议。

✅ 正确 / ❌ 错误 调用对照

场景 请求 结果
✅ 显式指定行程用车(推荐写法) POST /v3/admin/order/{id}/vehicle-requirement/dispatch?kind=TRAVEL + { "dispatchRemark": "..." } 200
✅ 显式指定接送机(推荐写法) POST /v3/admin/order/{id}/vehicle-requirement/dispatch?kind=TRANSFER + { "dispatchRemark": "..." } 200
✅ 不传 kind,该户只有一类活跃需求 POST /v3/admin/order/{id}/vehicle-requirement/dispatch 200,按那一类处理(纯接送机户从此可用)
✅ 打回(备注必填) POST /v3/admin/order/{id}/vehicle-requirement/reject?kind=TRAVEL + { "returnRemark": "请重新确认用车日期" } 200
❌ 不传 kind,该户两类活跃需求并存 POST /v3/admin/order/{id}/vehicle-requirement/reject 809012,必须带 kind 重发
❌ kind 传非法值 ?kind=travel2 809000
❌ 打回不带备注 { "returnRemark": "" } 400「打回/驳回备注不能为空」
❌ 备注超 500 字 { "returnRemark": "<501 字>" } 400「打回/驳回备注不能超过 500 字」

前端必须做的改动

  1. 两个端点的调用一律显式带上 kind。页面上这两个按钮本来就分挂在 TRAVEL / TRANSFER 两块需求下,取值是现成的;带上之后永远不会撞 809012。
  2. 接住 809012:若某处确实拿不到类别,撞到 809012 时要提示用户选择类别并带上 kind 重发,不要做自动重试(同一请求重发永远同码)。
  3. 不要再依赖「不传 = TRAVEL」这个隐含约定——它已经不成立了。

五、数据库行为

本次改动没有任何表结构变化,变的是「写哪一行」的定位规则。

前端调用 该户活跃需求 改前写入 改后写入
不传 kind 只有 TRAVEL TRAVEL 行 TRAVEL 行(未变)
不传 kind 只有 TRANSFER ❌ 取不到 TRAVEL 行 → 582031,零写入 ✅ TRANSFER 行
不传 kind TRAVEL + TRANSFER 并存 ❌ 静默写 TRAVEL 行(返 200,操作者要动的那行三个字段全不变) ✅ 809012 拒绝,零写入
不传 kind 一条活跃行都没有 582031,零写入 582031,零写入(未变)
传 kind=TRAVEL 任意 TRAVEL 行 TRAVEL 行(未变)
传 kind=TRANSFER 任意 TRANSFER 行 TRANSFER 行(未变)

写入内容本身未变:

  • dispatch:order_vehicle_requirement 该行 status PENDING_REVIEW → PENDING、写 dispatch_remark(≤500)、车控置 PENDING、CAS 退流程。
  • reject:该行 status → REJECTED_TO_CONSULTANT、写 return_remark(≤500);同事务清团级 requirement_confirmed、写团级时间线 BATCH_REQUIREMENT_REJECT。

失败零写入:809012 抛在解析阶段(dispatch 里排在团期守卫之后、取行之前;reject 里排在占用探测之前),任何一条数据都不会落。


六、边界行为

  • 未登录 → 401(网关拦截)。
  • 权限点 group-batch:demand:confirm 缺失 → 403。
  • 非团期子订单 → 582083(这道守卫排在 kind 解析之前,报错与改前逐字相同)。
  • 需求状态不在允许的源状态集合 → 582083。
  • 该户按解析出的类别取不到活跃行 → 582031。
  • 解析到 TRANSFER 但服务日未回填 → 809007(dispatch 端点,失败关闭不放行)。
  • kind 传非法值 → 809000(与改前一致,本次未改变非法值行为)。
  • 老数据兼容:不改表、不迁移;存量订单下次调用时按新解析规则生效。

六.5、枚举 / 数据字典

kind(用车需求类别)

所属字段: 两个端点的查询参数 kind | 类型: String

值 中文 说明
TRAVEL 团期行程用车 汇总进团级乘车分组、整团逐日配车的那一类
TRANSFER 接送机 逐户派车的那一类;服务日由大交通派生,未回填时 dispatch 抛 809007
(不传) — 🔴 不再等价于 TRAVEL。按该户活跃需求类别数解析:1 类用那一类 / 0 类抛 582031 / ≥2 类抛 809012
其他任意值 — 非法,抛 809000

六.6、修改前后对比

字段级对比

字段 改前 改后
kind(两个端点的查询参数) @RequestParam(defaultValue = "TRAVEL"),Swagger 标 defaultValue=TRAVEL @RequestParam(required = false),无默认值;Swagger 文案改为「不传按该户活跃需求类别自动解析,两类并存时必须显式指定」
DispatchReqVO.dispatchRemark 选填 ≤500 未变
RejectReqVO.returnRemark 必填 ≤500 未变
两个端点的响应 Result<Void> 未变
错误码集合 809000 / 809007(仅 dispatch)/ 582031 / 582083 🆕 增加 809012

行为级对比

行为 改前 改后
不传 kind + 两类并存 静默改 TRAVEL 行,返 200;操作者要动的 TRANSFER 行原封不动,响应上无任何信号 抛 809012,零写入
不传 kind + 只有 TRANSFER 去找不存在的 TRAVEL 行 → 582031,这类户走不通流程 解析成 TRANSFER,正常执行
不传 kind + 只有 TRAVEL TRAVEL 行 未变
不传 kind + 一条活跃行都没有 582031 未变
传了 kind(合法或非法) 原样使用 / 809000 未变
非团期子订单 582083 未变(守卫排在解析之前)
房需求打回(resourceType=HOTEL) kind 无意义 未变(不触发车侧解析,不会被两类并存误伤)

六.7、影响评估

  • 是否破坏向后兼容: 是。旧调用方「不传 kind = 按 TRAVEL」的隐含约定已失效;两类活跃需求并存的户上,原本返 200 的请求现在会返 809012。
  • 前端是否必须同步上线: 是(建议)。不改也不会报错的前提是「该户只有一类活跃需求」,一旦出现两类并存就会撞 809012。改法极小:调用时把已知的类别放进 kind 查询参数,并接住 809012。
  • 前端 workaround 清理点: 若为绕开「纯接送机户点提交车务报 582031」做过按钮置灰、隐藏或提示,可以撤掉——该场景已修好。

七、不影响范围

  • 仅影响: POST /v3/admin/order/{id}/vehicle-requirement/dispatch 与 POST /v3/admin/order/{id}/vehicle-requirement/reject 两个端点的 kind 参数语义。
  • 零影响:
    • 房需求(resourceType=HOTEL)的提交与打回链路
    • 定制师侧提交 / 修改 / 调整用车需求 PUT /v3/admin/order/{id}/vehicle-requirement
    • 团级正式用车需求的保存 / 汇总 / 预检 / 确认 / 撤回 / 免车
    • 批量打回、整团确认的既有行为
    • 结算侧手录车费的归属解析(809008,口径有意不同,本次未动)
    • 车务侧(hl-fleet-service)配车、派单
    • 历史数据:不改表、不迁移

八、测试环境已验证

  • 代码事实(对 origin/dev-v3 逐一查证):
    • 合并提交 a2ba628ecb(PR #8610 squash 合并进 dev-v3),7 文件 / +491 −27。
    • VehicleRequirementAdminController 两处 @RequestParam(defaultValue = "TRAVEL") → @RequestParam(required = false),@ApiParam 文案同步改写,diff 已逐行核对。
    • 新增 VehicleRequirementKindErrorCode.VEHICLE_REQUIREMENT_KIND_REQUIRED = IErrorCode.of(809012, …),消息模板与占位符含义({0}=订单 ID、{1}=两类名以 / 连接)已核对;同段既有码 809000/809001/809002/809007/809008/809009/809010/809011 未变。
    • 新增私有方法 RequirementService#resolveVehicleRequirementKind(Long, String),三条分支(非空白原样返回 / 0 类原样返回 / 1 类用那一类 / ≥2 类抛 809012)逐行核对;dispatch 里的调用点排在团期守卫之后、取行之前,reject 里排在 probeGroupAdminReject 之前且仅对 resourceType=VEHICLE 生效。
    • 回归覆盖:RequirementServiceTest +301 行、VehicleRequirementAdminControllerTest +72 行、VehicleRequirementKindErrorCodeMessageTest +22 行(含 809012 报文渲染断言)。
  • 部署:hl-order-service-v3 的 dev-v3 分支已滚到测试服,两个端点走管理端网关 /v3/admin/order/** 既有路由,无新增路由。
POST /v3/admin/order/{id}/vehicle-requirement/dispatch?kind=TRANSFER → 200 ✓
POST /v3/admin/order/{id}/vehicle-requirement/reject?kind=TRAVEL     → 200 ✓
POST /v3/admin/order/{id}/vehicle-requirement/dispatch(两类并存不传 kind) → 809012 ✓
POST /v3/admin/order/{id}/vehicle-requirement/dispatch(纯接送机户不传 kind) → 200 ✓(改前 582031)

九、相关历史 PR

PR Issue 说明 是否仍有效
— #7210 逐单打回源状态放宽到 PENDING,接团期权限守卫 ✅ 有效
— #7439 用车需求按 kind 分家,两个端点加 kind 参数(当时带默认值 TRAVEL) ⚠️ 默认值部分已被本单撤销
— #8435 团期订单接送变更走团期放行(809011) ✅ 有效
本 PR #8610 #8601 kind 取消默认值,空值按活跃类别数分流,新增 809012 ✅ 最新

十、相关文档

关联 / 联系人

链接

联系人

  • 后端负责人: @wx