# 【前端待处理·管理后台】车务派车看板与矩阵 SSE 实时刷新

## 目标前端

- 端类型：管理后台（Web）
- 目标仓库：`mmg/hl-ui`
- 仓库地址：<https://git.1814.love:8443/mmg/hl-ui.git>
- 联调/验收环境：<http://192.168.100.160:9527>
- 目标角色：当前登录角色为 `VEHICLE_MANAGER`（车务管理员）
- 小程序、司机 H5：无需处理

## 问题与后端结论

2026-07-21 复现：订单详情新增用车需求后，fleet-service 已生成未派车占位，但已打开的派车看板和矩阵派单不会实时刷新；多个车务管理员同时在线时，其他人的页面也无法感知变化。

根因是原管理后台 SSE 只接入消息、聊天、在线状态和房务抢单池信令，没有车务派单数据变更事件，也没有看板/矩阵的刷新订阅。

后端已补充以下链路：

1. fleet-service 在用车需求展开事务真正提交后发布 `REQUIREMENT_EXPANDED` 事件，避免页面刷新早于未派占位落库。
2. fleet-service 调 user-service 内部广播接口。
3. user-service 经 Redis Pub/Sub 把信令分发到所有实例。
4. 每个实例只向当前连接角色为 `VEHICLE_MANAGER` 的 SSE 连接发送 `fleet-dispatch-changed`。
5. 信令不绑定单个 `adminId`，因此多个车务管理员、多个浏览器标签和多个 user-service Pod 均可收到。

> 后端代码和定向测试已完成；测试环境是否已部署须以前后端发布记录为准。前端不得在后端未部署时把“收不到新事件”误判为页面监听实现失败。

## SSE 契约

继续复用现有管理后台 SSE 连接，不新增浏览器请求接口：

```http
GET /ws/admin-msg/stream?token=<accessToken>
Accept: text/event-stream
```

新增具名事件：

```text
event: fleet-dispatch-changed
data: {"type":"FLEET_DISPATCH","targetRoleKey":"VEHICLE_MANAGER","fleetEvent":"REQUIREMENT_EXPANDED","orderId":"2079494135466643457","requirementId":"..."}
```

字段说明：

| 字段 | 类型 | 当前值/说明 |
| --- | --- | --- |
| `type` | String | 固定 `FLEET_DISPATCH` |
| `targetRoleKey` | String | 固定 `VEHICLE_MANAGER` |
| `fleetEvent` | String | 当前为 `REQUIREMENT_EXPANDED` |
| `orderId` | String/Long | 变更涉及的订单 ID；仅作定位提示，按字符串处理 |
| `requirementId` | String/Long | 变更涉及的用车需求 ID；仅作定位提示，按字符串处理 |

- 前端不得对雪花 ID 使用 `Number()`；如需比较，统一 `String(value)` 后比较。
- 本事件是“数据已变化”的轻量信令，不携带看板或矩阵业务正文。
- 收到事件后必须重拉现有权威查询接口，不能根据信令自行拼装订单、派车组或矩阵占用条。
- 后端只向当前角色为 `VEHICLE_MANAGER` 的连接投递。`SUPER_ADMIN` 只有切换并以车务管理员当前角色重新建立 SSE 后才会收到。

## 前端接入要求

### 1. 全局 SSE 接收

在 `src/composables/useAdminMessageSSE.js` 增加具名事件监听：

```js
es.addEventListener('fleet-dispatch-changed', handleFleetDispatchSignal)
```

解析 JSON 后转入独立的车务派单信令总线。不要复用聊天信令或房务 `lastGrabPoolSignal`，避免模块语义互相污染。

建议在现有总线文件中新增：

```js
export const lastFleetDispatchSignal = ref(null)

export function pushFleetDispatchSignal(signal) {
  if (!signal) return
  lastFleetDispatchSignal.value = { ...signal, _seq: Date.now() }
}
```

连续事件必须保证每次都能触发订阅；实现可沿用现有 `_gseq` 自增模式，不强制使用 `Date.now()`。

### 2. 派车看板刷新

目标页面：`src/views/fleet/board/index.vue`

- 订阅 `lastFleetDispatchSignal`。
- 页面处于挂载状态并收到 `REQUIREMENT_EXPANDED` 后调用现有 `fetchBoard()`。
- 保留当前筛选条件、分页/视图模式和搜索输入，不得重置用户工作区。
- 多条短时间信令可做 100～300ms 合并刷新，避免重复并发请求。
- 沿用现有请求序号/取消机制，迟到响应不得覆盖较新的看板数据。

### 3. 矩阵派单刷新

目标页面：

- `src/views/fleet/matrix/index.vue`
- `src/views/fleet/matrix/solo/index.vue`（如独立挂载数据上下文）
- `src/views/fleet/matrix/composables/useFleetMatrixData.js`

处理要求：

- 收到信令后调用现有 `fetchMatrix(requestFilters.value)` 或等价的当前筛选刷新入口。
- 同步刷新未派订单池、顶部统计和矩阵占用数据；不能只刷新车辆行而保留旧未派数量。
- 保留当前年月、车队、车型和其他筛选条件。
- 若派单弹窗正在提交，不得关闭弹窗或清空用户输入；提交结束后以最后一次权威查询结果收敛页面。
- 矩阵分窗复用主页面数据组件时只订阅一次，避免同一事件发起重复请求。

### 4. 断线重连对账

Redis Pub/Sub 和 SSE 均不提供历史事件重放。断线期间可能漏过 `fleet-dispatch-changed`，因此：

- SSE 重新收到 `connected` 后，若派车看板或矩阵当前已打开，应主动重拉一次当前页面数据。
- 不要仅依赖实时事件维持页面正确性。
- 仍按现有 SSE 生命周期要求保证全局只有一条连接，不得为看板和矩阵各自新建 `EventSource`。

## 不影响范围

- 不修改派车看板、矩阵派单现有查询接口及响应结构。
- 不修改现有 `message`、`unread-count`、`im-chat`、`im-chat-read`、`presence`、`grab-pool-changed` 事件。
- 不要求前端调用 `/internal/sse/fleet-dispatch/broadcast`；该路径仅供服务间 Feign 使用，管理后台不得直接访问。
- 本次只覆盖用车需求展开后实时刷新。后续其他派单动作如扩展新的 `fleetEvent`，将另行补充契约。

## 验收清单

- [ ] 以车务管理员 A 打开 `/fleet/board`，车务管理员 B 或定制师新增用车需求后，A 的看板无需手动刷新即可出现新未派订单。
- [ ] 以车务管理员 A 打开 `/fleet/matrix`，新增用车需求后未派订单池、顶部统计和矩阵数据自动更新。
- [ ] 两个不同车务管理员同时在线并分别打开看板/矩阵，两边均收到同一变更并刷新。
- [ ] 同一车务管理员两个浏览器标签同时在线，两个标签均能刷新且互不关闭 SSE。
- [ ] 当前角色为 `SUPER_ADMIN` 且未切换车务角色时不接收本事件。
- [ ] `SUPER_ADMIN` 切换为 `VEHICLE_MANAGER` 并重建 SSE 后可以接收。
- [ ] 收到信令时保留看板和矩阵当前筛选条件，不跳回默认月份或清空搜索项。
- [ ] 短时间连续提交用车需求不会产生请求风暴或旧响应覆盖新数据。
- [ ] SSE 断线期间新增需求，连接恢复并收到 `connected` 后页面主动对账并显示最新数据。
- [ ] Network 中只存在一条 `/ws/admin-msg/stream` 长连接，没有为车务页面新增独立 SSE。

## 后端验证记录

- fleet-service：`AssignmentServiceTest` 239 项通过。
- fleet-service：车务派单 AFTER_COMMIT 通知测试 2 项通过。
- user-service：`AdminSseServiceTest` 37 项通过，覆盖两个车务同时接收、非车务角色隔离。
- user-service：车务广播与内部接口测试 3 项通过。
- `hl-fleet-service`、`hl-user-service` 模块级 `mvn -DskipTests package` 均通过。

## 发布说明

- 本文是前端接入与联调通知，不代表已修改或发布 `mmg/hl-ui`。
- 前端完成后应在 `mmg/hl-ui` 走自身 Issue、分支、PR、测试和发布流程。
- 联调材料不得包含 access token、带 token 的完整 SSE URL、Cookie 或真实管理员身份信息。
