docs: clarify fleet frontend contracts

这个提交包含在:
API Changelog Bot 2026-07-06 10:40:45 +08:00
父节点 31e2bc4fec
当前提交 c46f388641
共有 2 个文件被更改,包括 293 次插入17 次删除

查看文件

@ -22,6 +22,7 @@
| 场景 | 前端处理 |
|------|----------|
| 当前角色 | 车务菜单只能由 `VEHICLE_MANAGER``SUPER_ADMIN` 访问;`wx/CUSTOMIZER` 访问 `/admin/fleet/**` 返回 `403`,提示切换车务角色。 |
| 车务工作台 | `GET /admin/profile/dashboard?period=today``VEHICLE_MANAGER` 角色下由 user-service 代理 fleet 真实看板数据;不要再读旧订单统计或本地 mock。 |
| 司机结算方式 | 司机新增/编辑/导入使用字典 `driver_settlement_mode``direct`=直接结算,`captain`=车队长结算。 |
| 待审核详情 ID | `GET /admin/fleet/drivers/pending/{pendingId}` 响应主键字段是 `id`,不是 `pendingId`;路径变量仍叫 `pendingId`。 |
| 派单重复确认 | `POST /admin/fleet/assignments/{assignmentId}/confirm` 只允许 `holding -> assigned`;非 `holding` 返回 `605020`。 |
@ -41,6 +42,7 @@
| 菜单 | 关键接口 |
|------|----------|
| 车务工作台 | `/admin/profile/dashboard?period=today` |
| 派单看板 | `/admin/fleet/board/summary`, `/orders`, `/orders/{orderId}`, `/timeline`, `/expiry`, `/assignments/**` |
| 矩阵派单 | `/admin/fleet/matrix/grid`, `/unassigned-orders`, `/day-orders` |
| 车队对账 | `/admin/fleet/reconciliation/cars`, `/insurance`, `/periods`, `/actual`, `/close`, `/reopen`, `/pending-compensations/**`, `/export/cars` |
@ -94,6 +96,80 @@ Content-Type: application/json
> 说明:下面示例来自本次测试服真实 API 回归结构,手机号、token、部分 ID 做了脱敏或占位。前端处理长整型 ID 时建议按字符串保存,避免 JS 精度问题。
### 4.4 分页与全量接口约定
- 公共分页对象 `page/pageSize` 的上限仍为 `100`,车务模块不绕过公共上限。
- 页面列表、弹窗选择、远程搜索列表继续调用分页接口,前端分页选项不要超过 `100`
- 下拉、筛选、树形选择等“需要全量选项”的场景不要用 `pageSize=200/1000` 拉分页接口,应改用对应的非分页轻量端点。
- 当前可用非分页端点:
```http
GET /admin/fleet/vehicle-types
GET /admin/fleet/vehicle-types/list
GET /admin/fleet/drivers/options?keyword=王&limit=20
GET /admin/fleet/message-templates
GET /admin/fleet/reconciliation/periods?fromMonth=2026-01
```
| 场景 | 正确接口 | 不要这么调 |
|------|----------|------------|
| 车辆列表页/车辆档案分页 | `GET /admin/fleet/vehicles/page?page=1&pageSize=24` | 不要 `pageSize>100` |
| 派单弹窗选择车辆 | `GET /admin/fleet/vehicles/page?page=1&pageSize=10&vehicleStatus=idle` | 不要用一个下拉一次拉全部车辆 |
| 车型大类筛选下拉 | `GET /admin/fleet/vehicle-types/list` | 不要 `/vehicle-types/page?pageSize=1000` |
| 大类+型号树选择 | `GET /admin/fleet/vehicle-types` | 不要按每个大类再批量扫 `/models/page` |
| 司机远程搜索下拉 | `GET /admin/fleet/drivers/options?keyword=王&limit=20` | 不要 `/drivers/page?pageSize=1000` |
| 车管模板列表 | `GET /admin/fleet/message-templates` | 该接口本身不分页,不要拼 page/pageSize |
| 车队对账主数据 | `GET /admin/fleet/reconciliation/cars` / `insurance` | 主数据不分页,不要自己从车辆/司机分页接口拼 mock |
如果后续前端出现新的“确实需要全量车辆选项”的页面,不要扩大 `/vehicles/page` 上限;先提需求补一个窄字段的车辆 options 接口,限定字段和筛选条件。
### 4.5 车务工作台
车务首页入口仍走用户服务的角色分发接口;当前登录角色必须是 `VEHICLE_MANAGER`
```http
GET /admin/profile/dashboard?period=today
```
响应:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"pendingArrangeVehicle": 3,
"upcomingTrips": [
{
"orderId": "70123456789",
"assignmentId": "80123456789",
"orderNo": "HL202607010001",
"productName": "呼伦贝尔 6 日",
"customerName": "张先生",
"departureDate": "2026-07-10",
"endDate": "2026-07-15",
"headcount": 4,
"status": "unassigned_urgent",
"statusLabel": "待派",
"urgentBadge": "T-1"
}
]
}
}
```
字段口径:
| 字段 | 说明 |
|------|------|
| `pendingArrangeVehicle` | 来源 fleet 派单看板 `pendingCount`,即当前待派单数,含紧急待派。 |
| `upcomingTrips` | 来源 fleet 派单看板列表,近 7 天游程,最多 5 条。 |
| `status` | 车务派单态:`unassigned``unassigned_urgent``holding``holding_urgent``assigned`。 |
| `orderId` / `assignmentId` | 长整型按字符串处理,前端不要转 JS Number。 |
这不是分页不足问题;工作台不应从 `/admin/fleet/vehicles/page` 或旧 `DashboardStats` 拼数据,也不应使用本地 `useFleetStore` mock。
---
## 5. 订单前置:新建订单、出行人、大交通、用车需求
@ -1405,6 +1481,8 @@ POST /admin/fleet/drivers/pending/{pendingId}/reject
## 13. 车队对账
前端当前若仍用本地 `useFleetStore` / mock 计算车队对账,需要切到本节接口;后端主数据不是分页接口,返回结构也不是旧的 `records/total`
### 13.1 查询车费、保险、对账期
```http
@ -1419,21 +1497,149 @@ GET /admin/fleet/reconciliation/export/cars?periodStart=2026-07-01&periodEnd=202
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"records": [
"periodLabel": "2026-07",
"periodStart": "2026-07-01",
"periodEnd": "2026-07-31",
"fleets": [
{
"fleet": "own",
"fleetName": "自有车队",
"assignmentCount": 1,
"estimated": "888.00",
"payableEstimated": "888.00",
"actualAmount": "123.45",
"diff": "-764.55"
"orderCount": 3,
"estimatedTotal": "27000.00",
"payableTotal": "27000.00",
"actualTotal": "26000.00",
"settleMode": "OWN_COST",
"diff": "-1000.00",
"closed": false,
"vehicles": [
{
"vehicleId": "2064991856994758657",
"plate": "蒙A-88888",
"modelName": "丰田汉兰达",
"seats": 7,
"orderCount": 3,
"days": 18,
"avgPerDay": "1500.00",
"amount": "27000.00",
"payableAmount": "27000.00"
}
]
}
],
"total": 1
"grandTotal": {
"estimated": "27000.00",
"payable": "27000.00",
"actual": "26000.00",
"diff": "-1000.00",
"totalDays": 18,
"totalOrderCount": 3
}
}
}
```
字段口径:
| 字段 | 说明 |
|------|------|
| `fleets[].orderCount` | 车队内订单数,后端已按 `orderNo` 去重 |
| `fleets[].estimatedTotal` | 对客报价合计,利润分析用 |
| `fleets[].payableTotal` | 对车队应付合计,车队成本基准 |
| `fleets[].actualTotal` | 车务/财务手工录入实付,未录为 `null` |
| `fleets[].diff` | `actualTotal - payableTotal`,未录实付为 `null` |
| `fleets[].closed` | 整月关账状态;非自然月查询恒为 `false` |
| `vehicles[].days` | 车天,来自 active prep 行数 |
| `vehicles[].avgPerDay` | `payableAmount / days` |
| `grandTotal.totalOrderCount` | 全车队订单数,后端跨车队全局去重,前端不要用明细求和 |
保险响应:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"periodLabel": "2026-07",
"grandTotal": "250.00",
"fleets": [
{
"fleet": "own",
"sourceSubtotals": {
"manual": "120.00",
"baoyou": "130.00"
},
"typeCounts": {
"annual": 1,
"perTrip": 1,
"none": 0
},
"drivers": [
{
"driverId": "188800000000000001",
"name": "王师傅",
"phone": "138****1234",
"insuranceType": "annual",
"insuranceSource": "MANUAL",
"billingMethod": "年保险按日摊销",
"annualPremium": "3650.00",
"perDayRate": null,
"totalPremium": null,
"orderCount": 2,
"days": 12,
"insuranceAmount": "120.00"
},
{
"driverId": "188800000000000002",
"name": "李师傅",
"phone": "139****5678",
"insuranceType": "",
"insuranceSource": "BAOYOU",
"billingMethod": "保游网实际出单",
"annualPremium": null,
"perDayRate": null,
"totalPremium": "130.00",
"orderCount": 1,
"days": 3,
"insuranceAmount": "130.00"
}
]
}
]
}
}
```
保险字段口径:
| 字段 | 说明 |
|------|------|
| `insuranceSource` | 分组主键,取 `MANUAL/BAOYOU/NONE`;不要用 `insuranceType` 分组 |
| `insuranceType` | 司机配置类型,`annual/perTrip/none`;保游实际出单行可能为空字符串 |
| `sourceSubtotals.manual` | 手填年保费/行程险摊销小计 |
| `sourceSubtotals.baoyou` | 保游实际出单摊销小计 |
| `typeCounts` | 车队内司机保险类型计数 |
| `grandTotal` | 全车队保险成本合计,不含 `NONE` |
对账期响应:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": [
{
"label": "2026-07",
"periodStart": "2026-07-01",
"periodEnd": "2026-07-31",
"closed": false,
"reopened": false
}
]
}
```
@ -1454,7 +1660,7 @@ POST /admin/fleet/reconciliation/pending-compensations/{id}/resolve
"periodStart": "2026-07-01",
"periodEnd": "2026-07-31",
"fleet": "own",
"actualAmount": 123.45,
"actualAmount": "26000.00",
"note": "菜单验收录入"
}
```
@ -1464,13 +1670,70 @@ POST /admin/fleet/reconciliation/pending-compensations/{id}/resolve
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"id": "2026-07-own",
"estimated": "888.00",
"payableEstimated": "888.00",
"actualAmount": "123.45",
"diff": "-764.55"
"id": "2073749573041356801",
"diff": "-1000.00",
"payableEstimated": "27000.00",
"estimated": "27000.00"
}
}
```
清空实付:`actualAmount``null` 或省略,后端软删该 `(periodStart, fleet)` 实付行,响应中 `diff``null`
补偿列表响应:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"page": 1,
"pageSize": 5,
"total": 1,
"records": [
{
"compId": "2073749573041356802",
"assignmentId": "2073749573041356803",
"orderId": "2073749536638992386",
"periodLabel": "2026-07",
"opType": "TRUNCATE",
"serviceDateFrom": "2026-07-20",
"reason": "提前完结截断触及已关账期 2026-07",
"status": "PENDING",
"createTime": "2026-07-06T10:00:00",
"resolvedAt": null,
"resolvedBy": null,
"resolveRemark": null
}
]
}
}
```
处置补偿请求:
```json
{
"remark": "已线下补对账,差额计入 2026-08 调整"
}
```
处置补偿响应:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"compId": "2073749573041356802",
"status": "RESOLVED",
"resolvedAt": "2026-07-06T10:30:00",
"resolvedBy": "1001"
}
}
```
@ -1509,6 +1772,8 @@ POST /admin/fleet/reconciliation/pending-compensations/{id}/resolve
## 14. 车管模板
模板列表是非分页接口;前端页面不要传 `page/pageSize`。测试服如果返回空数组,表示当前库没有配置模板,不是分页问题。
```http
GET /admin/fleet/message-templates
GET /admin/fleet/message-templates?templateType=hold_notify
@ -1552,9 +1817,9 @@ DELETE /admin/fleet/message-templates/{templateId}
```json
{
"orderNo": "HL20260705204312543",
"driverName": "API师傅",
"vehiclePlate": "蒙A-69665"
"orderId": "2073749536638992386",
"driverId": "2073749573041356803",
"vehicleId": "2073749573041356804"
}
```
@ -1565,12 +1830,14 @@ DELETE /admin/fleet/message-templates/{templateId}
"code": 200,
"success": true,
"data": {
"renderedBody": "您好 API师傅,订单 HL20260705204312543 已派车,车牌 蒙A-69665",
"renderedBody": "您好 API师傅,订单 已派车,车牌 蒙A-69665",
"variablesUsed": ["driver.name", "order.no", "vehicle.plate"]
}
}
```
说明:当前 `driver.*``vehicle.*` 可按 ID 渲染;`order.*` / `itinerary.*` 本期仍走占位端口,缺值会替换为空串,不报错。前端不要传 `orderNo/driverName/vehiclePlate`,这些不是该接口入参字段。
变量缺失:
```json

查看文件

@ -11,6 +11,7 @@
- 车务保险菜单只展示司机险任务,不展示游客保险、订单保险、客户退款保险明细。
- `insurance.type=perTrip` 的司机在派单确认进入 `assigned` 后,后端按 `driverId + serviceDate` 自动一日一保投保。
- `insurance.type=annual` 的司机不会自动按单买险,但派单确认时后端会逐服务日校验年保覆盖;年保到期或覆盖不足会生成车务保险待办。
- 同一司机同一服务日只能有一份司机险;前后订单相接同日不重复买。
- 取消/提前完结后,只退该司机该服务日已无其它 active 派单占用的单日险。
- 司机险只进入车队/司机成本,不进入客户退款、订单收款、游客保险或合同保险。
@ -275,6 +276,9 @@ POST /admin/fleet/insurance/tasks/{taskId}/ignore
| 场景 | 后端行为 | 前端关注 |
|------|----------|----------|
| 派单确认 `holding -> assigned` | perTrip 司机按服务日逐日投保 | 确认派单成功不代表保险一定成功,需看保险任务 |
| 派单确认时司机配置年保且服务日全部被 `annualStart~annualEnd` 覆盖 | 不买按单险,不生成待办 | 保险成本按年保摊销进车队成本 |
| 派单确认时司机配置年保但部分服务日不被覆盖 | 对未覆盖服务日生成 `PENDING` 投保待办,错误码 `540033` | 提示车务续保/修正年保起止,或改按行程险后重试 |
| 司机年保为手工/线下来源且与线上保单不一致 | 生成 `PENDING` 核对待办,错误码 `540034` | 提示上传线下凭证或修正保单信息 |
| 已有线上保单覆盖同司机同日 | 不重复购买,写 `SUCCESS` 幂等流水 | 不要提示重复投保 |
| 已有手工/年保覆盖同司机同日 | 不买按单险,写 `PENDING` 提醒核对 | 展示 `suggestedAction` |
| 派单取消 | 只退该司机该服务日无其它 active 派单占用的单日险 | 同日仍有其它订单时不会退 |
@ -291,6 +295,7 @@ POST /admin/fleet/insurance/tasks/{taskId}/ignore
- 不要在前端拼重试业务参数,重试只传 `taskId`
- 不要在车务保险菜单展示订单游客险。
- 不要把 `SUCCESS` 自动流水当成待办提示;待办只看 `PENDING/PROCESSING`
- 不要只看司机保险类型是 `annual` 就认为无需处理;必须以后端任务状态为准。年保到期、行程增加一天、换司机后都会重新按服务日校验。
---
@ -302,6 +307,8 @@ POST /admin/fleet/insurance/tasks/{taskId}/ignore
| `100502` | 幂等保护,重复提交 | “正在处理,请勿重复提交” |
| `540007` | 保险计划缺费率 | “请维护保险计划费率后重试” |
| `540032` | 同司机服务日已有保单覆盖 | “已有保单覆盖,请核对后处理” |
| `540033` | 司机配置年保但服务日不在年保起止内 | “司机年保未覆盖当前服务日,请续保/修正年保后重试,或改按行程险处理” |
| `540034` | 手工年保/线下保单与线上覆盖信息不一致 | “请核对线下保单并上传凭证,确认后标记线下完成” |
| `605601` | 保险服务不可用 | “保险服务暂不可用,请稍后重试或线下处理” |
---
@ -313,6 +320,8 @@ POST /admin/fleet/insurance/tasks/{taskId}/ignore
- 新建订单 -> 补出行人 -> 补大交通 -> 提用车需求 -> 车务派单确认 -> 保险任务生成。
- 同一司机同一服务日多订单,不重复买保险。
- 取消派单时,同司机同日还有其它 active 派单则不退保。
- 年保司机在服务期内不买按单险;服务日超过年保到期日时生成 `540033` 待办。
- 年保续保后重试 `540033` 任务,应变为 `SUCCESS`,且不能产生重复按单险。
- 投保失败后重试成功。
- 线下完成必须上传凭证。
- 定制师角色不可见车务保险菜单。