# hl-ui v2.1 车务接口复核差异告知

- 日期：2026-07-07
- 复核对象：`D:/work2/hl-ui`，分支 `v2.1`，最新提交 `fd7f4392`
- 复核依据：`HL-v3 dev-v3` 当前代码与 `docs/order-v3/api/API-SPEC-FLEET-V1.5.html`
- 结论：车务部分 API 文件已接入真实接口，但仍有若干页面/动作没有完全按真实契约走。请前端优先修下面 P0/P1 项。

## P0 · 必须修

### 1. 车队对账录入实付方法错了

前端文件：`src/api/fleet/reconciliation.js`

当前：

```js
export function updateFleetReconciliationActual(data, config = {}) {
  return http.post(`${BASE}/actual`, data, config)
}
```

后端真实接口：

```http
PUT /admin/fleet/reconciliation/actual
Content-Type: application/json

{
  "periodStart": "2026-07-01",
  "periodEnd": "2026-07-31",
  "fleet": "own",
  "actualAmount": "58000.00",
  "note": "线下结算单号/备注"
}
```

响应示例：

```json
{
  "code": 200,
  "data": {
    "fleet": "own",
    "actualAmount": "58000.00",
    "payableEstimated": "58500.00",
    "diff": "-500.00",
    "closed": false
  }
}
```

前端需要改成 `http.put(`${BASE}/actual`, data, config)`。

### 2. 车队对账期不能继续用本地月份预设

前端文件：

- `src/views/fleet/recon/index.vue`
- `src/views/fleet/recon/components/PeriodBar.vue`
- `src/views/fleet/recon/composables/useReconCompute.js`

当前页面月份来自 `MONTH_PRESETS` 本地生成，未使用已封装的 `getFleetReconciliationPeriods`。这会导致当前 2026-07 仍可能停留在历史月份/本地缓存月份，且拿不到后端关账态。

后端真实接口：

```http
GET /admin/fleet/reconciliation/periods?fromMonth=2026-01
```

响应示例：

```json
[
  {
    "label": "2026-07",
    "periodStart": "2026-07-01",
    "periodEnd": "2026-07-31",
    "closed": false,
    "reopened": false
  },
  {
    "label": "2026-06",
    "periodStart": "2026-06-01",
    "periodEnd": "2026-06-30",
    "closed": true,
    "reopened": false
  }
]
```

前端应以 `GET /fleet/reconciliation/periods` 为对账期下拉数据源；本地月份只能作为接口失败兜底，不应作为主数据源。

### 3. 派单看板列表字段映射错了，已派车信息会读不到

前端文件：`src/views/fleet/board/index.vue`

后端 `GET /admin/fleet/board/orders` 列表行不是 `currentAssignment` 结构，而是平铺字段：

```json
{
  "id": "HL202607010001",
  "orderId": "2074000000000000001",
  "assignmentId": "2074000000000000002",
  "fleetItemIndex": 0,
  "assignmentStatus": "assigned",
  "currentVehiclePlate": "蒙A-88888",
  "currentVehicleModel": "丰田普拉多",
  "currentVehicleSeats": 7,
  "currentVehicleFleet": "own",
  "currentDriverName": "王师傅",
  "currentDriverPhone": "138****1234",
  "protocolPrice": null
}
```

当前 `normalizeBoardOrder()` 主要读 `assignment.plate / row.vehiclePlate / row.driverName`，会漏掉真实字段。应补齐：

```js
vehicle: assignment.vehiclePlate || row.currentVehiclePlate || row.vehiclePlate || '',
driverName: assignment.driverName || row.currentDriverName || row.driverName || '',
driverPhone: assignment.driverPhone || row.currentDriverPhone || row.driverPhone || '',
vehicleModel: row.currentVehicleModel,
vehicleSeats: row.currentVehicleSeats,
vehicleFleet: row.currentVehicleFleet,
assignmentId: assignment.id || row.assignmentId,
protocolPrice: assignment.protocolPrice ?? row.protocolPrice ?? null
```

注意：列表行没有 `vehicleId/driverId`，不要用 mock store 二次 join 假数据。

### 4. 看板详情当前派单字段也映射错了

前端文件：

- `src/views/fleet/board/index.vue`
- `src/views/fleet/board/components/OrderDrawer.vue`

后端 `GET /admin/fleet/board/orders/{orderId}` 的 `currentAssignment` 示例：

```json
{
  "currentAssignment": {
    "id": "2074000000000000002",
    "vehiclePlate": "蒙A-88888",
    "driverName": "王师傅",
    "assignmentStatus": "holding",
    "protocolPrice": "1300.00",
    "holdSentAt": "2026-07-07T10:00:00",
    "itineraryUrl": null
  }
}
```

当前前端读 `assignment.plate`，应改读 `assignment.vehiclePlate`。详情接口没有返回 `driverId/vehicleId`，抽屉展示不要从 `useFleetStore` mock 反查司机和车辆。

### 5. 取消派单请求体不满足后端门禁

前端文件：`src/views/fleet/board/index.vue`

当前取消动作传：

```js
await cancelAssignment(o.assignmentId, {
  cancelReason: '车务后台取消',
  driverNotified: false,
})
```

后端真实要求：`driverNotified` 必须为 `true`，否则返回参数错误。

正确请求：

```http
DELETE /admin/fleet/assignments/{assignmentId}
Content-Type: application/json

{
  "cancelReason": "司机拒接，重新派车",
  "driverNotified": true,
  "notifyNote": "已电话告知王师傅",
  "cutoffDate": "2026-07-07"
}
```

同时前端按钮文案不要写“取消订单”。该接口是“取消派单”，不是客户订单退单。客户退单/取消订单不在车务模块处理。

### 6. 司机拒接/请求换车等抽屉动作仍是 mock 本地状态

前端文件：

- `src/views/fleet/board/index.vue`
- `src/stores/fleet.js`

当前：

- `司机拒接` 调 `fleet.rejectAssign(o.id)`，只改前端 mock store。
- `请求更换车辆 / 撤销请求` 调 `fleet.requestChange/cancelChange`，只改前端 mock store。

这些不会落后端，不会触发占用释放、保险、对账 prep、操作日志。前端应先隐藏或改接真实车务接口：

- 司机拒接后退回待派：走 `DELETE /admin/fleet/assignments/{assignmentId}`，且 `driverNotified=true`。
- 已派单不可直接驳回用车需求；要先取消派单并完成司机告知存证。
- 换车请求当前后端文档标为未落地完整闭环，不要用 mock 状态制造假成功。

### 7. 车辆/司机档案里的“派活”订单选择仍从 mock store 取单

前端文件：

- `src/views/fleet/components/OrderPickerDrawer.vue`
- `src/views/fleet/vehicles/index.vue`
- `src/views/fleet/drivers/index.vue`
- `src/stores/fleet.js`

当前 `OrderPickerDrawer` 从 `fleetStore.orders` 取 mock 订单。真实入口应使用后端未派数据：

```http
GET /admin/fleet/matrix/unassigned-orders?year=2026&month=7&typeKeys=suv&typeKeys=mpv
```

或使用看板分页：

```http
GET /admin/fleet/board/orders?page=1&pageSize=20&statuses=unassigned
```

返回行里关键字段：

```json
{
  "assignmentId": "2074000000000000002",
  "orderNumericId": "2074000000000000001",
  "orderNo": "HL202607010001",
  "fleetItemIndex": 0,
  "vehicleCategory": "suv",
  "categoryLabel": "SUV",
  "startDate": "2026-07-10",
  "endDate": "2026-07-12",
  "headcountLabel": "3人"
}
```

派单弹窗必须拿真实 `orderNumericId/assignmentId/fleetItemIndex/requirementId`，不能拿 mock 订单 ID。

### 8. 派单弹窗预选车辆不能传车牌当 vehicleId

前端文件：

- `src/views/fleet/_shared/gantt/composables/useAssignAction.js`
- `src/views/fleet/board/components/AssignModal.vue`
- `src/views/fleet/board/composables/useVehicleDriverPicker.js`

后端创建派单要求：

```json
{
  "vehicleId": "2074000000000000101",
  "driverId": "2074000000000000102",
  "orderId": "2074000000000000001",
  "startDate": "2026-07-10",
  "endDate": "2026-07-12",
  "holdMode": 1,
  "protocolPrice": "1300.00"
}
```

当前从矩阵/车辆档案预选时常传 `vehicle.plate`，但 `useVehicleDriverPicker` 内部选项 `valueKey` 优先是车辆 `id`。应统一：

- 选择值使用 `vehicleId`。
- 展示字段使用 `plate`。
- 如果入口只能拿到车牌，必须先从 `/fleet/vehicles/page` 结果中找到对应 `id`，否则不要预选。

## P1 · 需要尽快对齐

### 9. 车务工作台数据入口对了，但“配车”按钮仍打开旧订单配车组件

前端文件：

- `src/views/dashboard/components/VehicleDashboard.vue`
- `src/views/order/components/VehicleInfoModal.vue`

工作台数据源 `GET /admin/profile/dashboard?period=today` 是对的；但“配车”按钮打开的是旧 `VehicleInfoModal`，里面调用旧订单接口 `assignVehicle/checkVehicleInfoDiff`，不是车务派单闭环。

建议：点击工作台“配车”应跳转/打开车务看板详情或复用 `FleetAssignModal`，走：

- `GET /admin/fleet/board/orders/{orderId}`
- `POST /admin/fleet/assignments`
- `POST /admin/fleet/assignments/{assignmentId}/confirm`

### 10. 矩阵订单详情抽屉 action 事件参数不匹配

前端文件：`src/views/fleet/matrix/index.vue`

`OrderDrawer` 发的是字符串：

```js
emit('action', 'assign')
```

矩阵页接收写成：

```js
function onDrawerAction({ type }) {
  if (type === 'assign') ...
}
```

这会导致矩阵详情抽屉内派车/重派动作不触发。应改成接字符串，或统一事件对象格式。

### 11. 消息模板 delete 的 config 传参位置不一致

前端文件：`src/api/fleet/message-template.js`

当前：

```js
return http.delete(`${BASE}/${String(templateId)}`, config)
```

`http.delete(url, data, config)` 签名下，这会把 config 当请求 body。建议统一为：

```js
return http.delete(`${BASE}/${String(templateId)}`, null, config)
```

无 config 时影响不大；需要 `silentError/cancelDuplicate` 时会出问题。

### 12. 车队对账导出目前是前端本地 CSV，不是后端文件流

前端文件：`src/views/fleet/recon/index.vue`

后端已有：

```http
GET /admin/fleet/reconciliation/export/cars?periodStart=2026-07-01&periodEnd=2026-07-31
```

如果前端要保证和后端对账口径、BOM、文件名完全一致，车费对账导出建议改用后端文件流。本地 CSV 可保留作兜底。

## 可以保留的部分

- `src/api/fleet/board.js` 路径整体正确：summary/orders/detail/timeline/precheck/create/confirm/cancel/reject/restore/early-complete 都能对上后端。
- `src/api/fleet/matrix.js` 三个接口路径正确：grid/unassigned-orders/day-orders。
- `src/api/fleet/vehicles.js`、`vehicle-types.js`、`drivers.js`、`pricing.js`、`insurance.js` 主路径基本正确。
- `@/mock/fleet` 中仅作为展示常量/文案 helper 使用可以暂留；但不能作为车务真实数据源，尤其不能作为订单、车辆、司机、派单状态的写入源。

## 前端自测建议

1. 用车务角色登录，不用 admin/wx。
2. 打开车务工作台，确认数据来自 `GET /admin/profile/dashboard?period=today`，点击“配车”不要进入旧 `VehicleInfoModal`。
3. 车务看板列表检查已派行：车牌、司机、协议价、派单状态都来自 `current*` 或 `currentAssignment` 真实字段。
4. 打开订单详情后再派单，确认 `POST /admin/fleet/assignments` 请求体里 `vehicleId/driverId/orderId` 都是雪花字符串，不是车牌/订单号。
5. 取消派单必须先勾选/确认“已告知司机”，请求体 `driverNotified=true`。
6. 车队对账切到当前月份 2026-07，月份来源应为 `/fleet/reconciliation/periods`；录入实付必须发 `PUT /fleet/reconciliation/actual`。
