修改原因:complete_batch 三方核账要求 frontend_ref 与账本完整业务 SHA 精确一致,仓库前缀格式会被旧门禁误判。 修改内容:保持五项 implemented 状态与原业务提交不变,仅将 frontend_ref 规范为对应 origin/v2.1 可达的 40 位完整 SHA,并刷新核账时间。 实际验证:source npm test 46 项、check:path-aliases 与 git diff --check 通过;所有业务 SHA 已在 mmg/hl-ui origin/v2.1 可达。Changelog:#5301、#5295、#5299、#5264、#5292。
17 KiB
schema, ticket, title, consumer, 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 | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 5292 | 车务按服务日逐车配置用车、接机与价格 | admin | 修改接口 | deployed | verified | implemented | hl-ui-pi | 0a35da1e3b88f9c375d3e79516780e5a66f2b8ad | 2026-07-28T14:38:26+08:00 | hl-admin 已修复可编辑 UNPLANNED/新槽位默认用车、ARRIVAL 参与状态保持及新增槽位候选有界加载;最终提交 0a35da1e 已在 origin/v2.1 可达,全量 checkpoint 通过 | 2026-07-28 | dev-v3 |
Fleet:按服务日逐车配置用车、接机与价格
服务:
hl-fleet-service后端 PR:wx/HL#5296 Issue:#5292 日期:2026-07-27 影响范围:管理后台车务看板派车弹窗和订单派车详情
⚠️ 关键变化
新派车页面不再提交“收取车费日期”。派车保存改为提交完整的“服务日期 × 稳定车辆槽位”矩阵,每个单元格独立说明是否用车、车辆、司机、ARRIVAL 接机参与和当天实际价格。
旧 items 输入暂时保留用于滚动发布兼容;旧收费日期字段只能随旧 items 使用,dailyPlan 模式提交这些字段会被拒绝。
2026-07-28 产品口径补充:默认全部行程用车
新建派车草稿进入“排车”步骤时,当前需求的全部可编辑服务日、全部新建稳定车辆槽位均默认勾选“当天用车”。车务只需要选择车辆、司机和逐日价格;只有主动取消某日勾选或点击“明确不用车”,才表示该日明确不用车。
初始化规则
- 后端
planState=UNPLANNED表示尚未形成最终方案,不等于NOT_USED。前端首次把可编辑UNPLANNED日格转换为草稿时应初始化used=true,复选框默认选中,并显示“待选车辆/司机”或等价规划中状态。 - 新增车辆槽位时,该槽位覆盖的全部可编辑服务日同样默认
used=true,不得全部初始化为未勾选。 - 已有
planFinalized=true的USED/NOT_USED、只读日格、已关账日格和已有有效派车必须按服务端事实保留;已经明确保存为NOT_USED的日期重新打开时仍保持未选中,不得重新默认用车。 - 默认选中只是前端草稿口径,不代表已经完成派车。未选择车辆、司机或逐日价格时仍不得提交,也不得伪造
planFinalized=true。 - 用户主动取消“当天用车”时才写入草稿
used=false/planState=NOT_USED;某日所有槽位均不用车时继续执行confirmNoVehicleServiceDates=true二次确认。
当前前端偏差
前端提交 7b99fe7d35530073176af1a0376cba7b33503319 中,createDailyVehiclePlan() 在详情提供 dailyVehiclePlan 时把所有未最终确认日格按 UNPLANNED + used=false 直接用于草稿;appendDailyVehiclePlanSlot() 也把新槽位日格初始化为 used=false。因此页面出现整段行程“尚未规划”、所有“当天用车”均未选中的状态,与本次明确口径不符。
前端应区分“服务端基线状态”和“当前编辑草稿默认值”,不能通过把 UNPLANNED 改成服务端 USED 来伪造最终事实;仅在可编辑的新草稿层默认选中。
前端验收清单
- 4 天行程首次进入排车步骤时,4 个可编辑日格的“当天用车”全部默认选中。
- 候选尚未选定时显示待选车辆/司机而非“明确不用车”,且下一步仍因车辆、司机或价格缺失而受阻。
- 新增车辆槽位后,该槽位全部可编辑服务日也默认选中。
- 车务取消其中一天后,仅该日变为明确不用车,其余日期保持选中和已编辑草稿。
- 已保存的
NOT_USED、已有有效派车及只读日期重新打开后保持服务端事实,不被默认逻辑覆盖。 - 补充
createDailyVehiclePlan()、appendDailyVehiclePlanSlot()和真实挂载FleetAssignModal的回归测试,覆盖首次进入、增加槽位、重新打开以及主动取消用车。 - 提交请求仍满足:每个
used=true日格具备车辆、司机和价格;全天不用车时带明确二次确认。
2026-07-28 页面阻断:ARRIVAL 接机参与无法选中
真实页面复验中,服务日 2026-08-04 已标记“要求接机”,该日格已勾选“当天用车”、已选车辆和司机、无只读或锁定提示,但点击“参与 ARRIVAL 接机 / 接站”后复选框无法保持选中。该操作发生在派车草稿提交前,属于前端日格交互与状态同步阻断,不是后端保存接口拒绝。
当前 origin/v2.1@e744c909 中:
DailyVehiclePlanMatrix.vue的复选框仅发出update:pickup(key, checked);AssignModal.vue的handleDailyPlanPickupUpdate()只将结果写入本地dailyPlan;- 现有
daily-vehicle-plan-matrix.spec.js只断言子组件已发出事件,没有挂载FleetAssignModal验证父层接收后、候选状态 watcher 运行后以及切换日格后的值是否仍为true。
因此,子组件事件测试通过不能作为页面可用证据。前端需要从浏览器事件开始逐段核对 NCheckbox → update:pickup → handleDailyPlanPickupUpdate → dailyPlan → 提交 payload,找出勾选值被丢弃或覆盖的位置;不得通过跳过逐日方案校验或伪造后端字段规避。
前端验收清单
- 对
pickupRequired=true、used=true且可编辑的日格,点击后复选框立即选中,并在多轮nextTick、候选状态刷新以及价格编辑后保持选中。 - 切换到其他服务日再返回,接机参与状态仍保留;再次点击可以明确取消。
- 更新只作用于当前
serviceDate + fleetItemIndex,不得串改同日其他车辆或其他服务日。 - 将该日改为不用车时自动清除
pickupParticipant;重新用车后由车务再次明确选择,不沿用陈旧值。 - 提交前的
dailyPlan以及最终请求体均包含该日格pickupParticipant=true;后端返回成功后详情回显一致。 - 要求接机的服务日至少一辆实际用车标记参与接机;未选择时继续显示既有校验提示,选择后提示收敛。
- 新增
FleetAssignModal父子联动回归测试,覆盖事件接收、watcher 稳定、日格切换和 payload;不能只断言DailyVehiclePlanMatrix发出了事件。 - 页面 Console 无
Maximum recursive updates exceeded或unhandledrejection,复选框操作不得重新触发无界候选请求。
在该页面交互修复并完成真实浏览器复验前,frontend_status 保持 claimed,不得流转为 implemented/released/verified。
变更接口
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 原子提交最终派车方案 | POST | /admin/fleet/assignments/batch |
请求体扩展与校验调整 | 新增完整 dailyPlan,保留旧 items 兼容 |
| 2 | 车务看板订单详情 | GET | /admin/fleet/board/orders/<orderId> |
响应字段新增 | 返回逐日逐车计划、每车小计和订单车辆总计 |
二、原子提交最终派车方案
POST /admin/fleet/assignments/batch
请求 VO:BatchCreateAssignmentReqVO
新增顶层字段
| 字段 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|
dailyPlan |
DailyPlanItem[] |
新页面必填 | 与旧 items 二选一;最多 4000 项 |
完整“服务日期 × 稳定车辆槽位”矩阵 |
confirmNoVehicleServiceDates |
Boolean |
条件必填 | 某日全部槽位 used=false 时必须为 true |
全天无需用车二次确认 |
orderId、requirementId 和所有 Long ID 继续按 JSON 字符串传输。
DailyPlanItem
| 字段 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|
fleetItemIndex |
Integer |
是 | >= 0 |
稳定车辆槽位序号 |
serviceDate |
String(date) |
是 | yyyy-MM-dd,必须属于当前需求服务日 |
服务日期 |
used |
Boolean |
是 | - | 当天是否实际用车 |
vehicleId |
String(Long) |
条件必填 | used=true 必填;不用车必须为空 |
当天车辆 |
driverId |
String(Long) |
条件必填 | used=true 必填;不用车必须为空 |
当天司机 |
pickupParticipant |
Boolean |
否 | 仅表示 ARRIVAL 接机/接站;不用车不得为 true |
当天是否参与接机 |
assignmentPrice |
String(decimal) |
条件必填 | used=true 必填;非负、整数最多 10 位、小数最多 2 位 |
本车当天实际价格,可为 0.00 |
priceAdjustmentReason |
String |
条件必填 | 最多 256 字;实际价偏离价格日历时必填 | 改价原因 |
confirmCrossResident |
Boolean |
否 | 跨常驻车时按既有规则确认 | 跨常驻车确认 |
正确请求示例
{
"orderId": "789",
"orderNo": "26-4165",
"requirementId": "790",
"startDate": "2026-08-04",
"endDate": "2026-08-05",
"headcount": 3,
"holdMode": 0,
"fromEntry": "from-board",
"requestId": "daily-plan-26-4165-v1",
"confirmNoVehicleServiceDates": false,
"dailyPlan": [
{
"fleetItemIndex": 0,
"serviceDate": "2026-08-04",
"used": true,
"vehicleId": "701",
"driverId": "801",
"pickupParticipant": true,
"assignmentPrice": "800.00",
"priceAdjustmentReason": "首日短途优惠"
},
{
"fleetItemIndex": 1,
"serviceDate": "2026-08-04",
"used": true,
"vehicleId": "702",
"driverId": "802",
"pickupParticipant": false,
"assignmentPrice": "900.00",
"priceAdjustmentReason": null
},
{
"fleetItemIndex": 0,
"serviceDate": "2026-08-05",
"used": false,
"vehicleId": null,
"driverId": null,
"pickupParticipant": false,
"assignmentPrice": null,
"priceAdjustmentReason": null
},
{
"fleetItemIndex": 1,
"serviceDate": "2026-08-05",
"used": true,
"vehicleId": "702",
"driverId": "802",
"pickupParticipant": false,
"assignmentPrice": "900.00",
"priceAdjustmentReason": null
}
]
}
提交规则
- 每个保留槽位必须覆盖当前需求的全部服务日;同一
fleetItemIndex + serviceDate不能重复。 - 同一服务日不能重复使用同一车辆或同一司机。
used=false不占用车辆/司机、不投保、不计费;车辆、司机、接机和价格必须为空或 false。- 某日所有槽位均不用车时允许提交,但必须设置
confirmNoVehicleServiceDates=true。 - 大交通 ARRIVAL 要求平台接机时,当日至少一辆
used=true的车辆必须pickupParticipant=true。 - 大交通未要求接机时,仍允许人工标记一辆或多辆使用中的车辆参与接机。
- 连续日期使用相同车辆和司机时后端自动合并派车组;逐日价格仍分别冻结。
- 过去日期、已完结日期或已关账对账期不能修改。
- 新
dailyPlan不得提交chargeableServiceDates、vehicleFeeWaiverReason、confirmAllServiceDatesFree。
参数错误响应
本项目参数校验失败沿用 HTTP 200 + 业务 code=400:
{
"code": 400,
"message": "dailyPlan 与旧 items 必须二选一,dailyPlan 不得提交旧收费日期字段",
"data": null,
"success": false
}
三、车务看板订单详情
GET /admin/fleet/board/orders/<orderId>
响应 VO:Result<BoardOrderDetailVO>
新增响应字段
| 字段 | 类型 | 空值规则 | 说明 |
|---|---|---|---|
dailyVehiclePlan |
DailyVehiclePlanVO[] |
无记录返回 [] |
按服务日期、槽位序号稳定排序 |
vehicleFeeSummaries |
VehicleFeeSummaryVO[] |
无实际用车返回 [] |
按实际车辆汇总小计 |
vehicleFeeTotal |
String(decimal) |
无费用返回 "0.00" |
当前需求全部实际用车日总计 |
DailyVehiclePlanVO
| 字段 | 类型 | 说明 |
|---|---|---|
serviceDate |
String(date) |
服务日期 |
fleetItemIndex |
Integer |
稳定槽位序号 |
assignmentSlotId |
String(Long) |
稳定槽位 ID |
assignmentId |
String(Long) |
每日切片 ID;显式不用车也返回占位 ID |
assignmentGroupId |
String(Long) |
当前连续派车组 ID |
planFinalized |
Boolean |
该日格是否已经业务最终确认 |
planState |
String |
UNPLANNED(建议占位)/ NOT_USED(明确不用车)/ USED(实际用车) |
used |
Boolean |
当天是否实际用车;必须结合 planFinalized/planState 区分未规划与明确不用车 |
pickupParticipant |
Boolean |
当天车辆是否参与 ARRIVAL 接机 |
pickupRequired |
Boolean |
大交通当天是否要求接机 |
vehicleId / driverId |
String(Long) |
不用车时为 null |
vehiclePlate / vehicleModel / driverName |
String |
不用车时为 null |
driverPhone |
String |
脱敏手机号;不用车时为 null |
calendarPrice |
String(decimal) |
价格日历参考价;缺价或不用车时为 null |
assignmentPrice |
String(decimal) |
本车当天实际价;不用车时为 null |
priceSource |
String |
CALENDAR / OVERRIDE / MISSING / NOT_USED |
priceAdjustmentReason |
String |
改价原因,无则 null |
assignmentStatus |
String |
基础派单状态;显式不用车为 unassigned |
readOnly |
Boolean |
过去、已完结或已关账日期为 true |
readOnlyReason |
String |
服务日期已过去 / 派单已完结 / 对账期已关账,可编辑时为 null |
VehicleFeeSummaryVO
| 字段 | 类型 | 说明 |
|---|---|---|
vehicleId |
String(Long) |
实际车辆 ID |
vehiclePlate |
String |
车牌 |
vehicleModel |
String |
车型 |
amount |
String(decimal) |
该车辆全部实际用车日小计 |
金额守恒:vehicleFeeTotal = sum(vehicleFeeSummaries[].amount) = sum(dailyVehiclePlan[used=true].assignmentPrice)。
四、前端改造清单
- 删除派车弹窗中的“收取车费日期”和免费日期提交逻辑。
- 按服务日渲染稳定车辆槽位矩阵;每格维护
used、车辆、司机、ARRIVAL 接机参与和当天实际价格。 - 某日全不用车时显示二次确认,并提交
confirmNoVehicleServiceDates=true。 - 默认价格使用详情/报价返回的日历价;修改价格时强制填写原因,允许
0.00。 - 详情展示每辆车小计和订单车辆总计;金额以字符串解析,不能用浮点累计。
readOnly=true的日格禁止编辑,并展示readOnlyReason。- 新页面只提交
dailyPlan,不得同时提交旧items或收费日期字段。
五、数据库和历史数据
fleet_assignment.daily_vehicle_used:1表示当天实际用车,0表示明确不用车;滚动发布期间允许NULL,读取侧按车辆/司机事实回退,避免旧节点新写入被误判。fleet_assignment.pickup_participant:1表示当天该车参与 ARRIVAL 接机/接站;滚动发布期间允许NULL并按false兼容。- 历史已派日迁移为实际用车;原免费日实际价格迁为
0.00;未派占位迁为不用车;历史接机参与默认false。 - 全程明确不用车可由 Fleet 以
vehicleCount=0完成需求;Order 端车辆和司机快照均为空。
六、不影响范围
- 不修改
hl-ui仓库,由本 changelog 交接前端。 - 不改变单派接口及旧
items滚动兼容输入。 - 不把 DEPARTURE 送机/送站映射到
pickupParticipant。 - 不改变订单或产品价格日历接口。
验证证据
- 后端 PR #5296 已 squash 合并至
dev-v3,hl-order-service-v3与hl-fleet-service已滚动部署测试环境,双实例健康检查通过。 - Fleet 最新
dev-v3reactor verify:2452 项测试,0 failures,0 errors,1 skipped;Spotless、Jar、JaCoCo 均成功。 - Order 全量:6759 项测试,0 failures,0 errors,29 skipped;零车辆回调生产者/消费者及迁移定向回归通过。
- 真实测试网关
GET /admin/fleet/board/orders/<orderId>:HTTP 200、业务 code 200;返回 3 条dailyVehiclePlan,包含planFinalized、planState、used、接机、价格和只读字段;逐车汇总数组及字符串总计存在。 - 真实测试网关
POST /admin/fleet/assignments/batch安全负例:dailyPlan携带旧收费日期字段时 HTTP 200、业务 code 400,确认新旧模式互斥;该探针不产生业务写入。 - OpenAPI diff 与 Spring Cloud Contract 未配置;已通过源码字段对比、Controller/Service 及 Fleet → Order 双端普通测试提供人工回退证据,未冒充工具通过。
当前状态:
backend_status: deployedgateway_status: verifiedfrontend_status: claimed
关联 Issue:wx/HL#5292