8.2 KiB
订单详情「联系车务」独立未读红点
日期:2026-07-23 工单:HL #5180 影响范围:管理后台订单详情 → 行程安排 → 用车安排;聊天 SSE 实时状态 状态:前端待处理
1. 问题与口径
车务通过 FLEET:{orderId} 会话给定制师发送消息后,顶部全局铃铛能显示未读,但当前订单「联系车务」按钮没有订单维度红点。房务按钮已有同款能力,本次车务必须复用相同的角标样式、实时刷新和已读清零交互。
房务与车务未读是两个独立业务会话,禁止继续共用一个字段:
unreadMessageCount:当前登录定制师在本订单HOUSE:{orderId}会话的未读数,只供「联系房务」使用。fleetUnreadMessageCount:当前登录定制师在本订单FLEET:{orderId}会话的未读数,只供「联系车务」使用。- 顶部铃铛全局未读只能说明“存在未读”,不能作为当前订单按钮是否亮红点的判断依据。
2. 接口变更
GET /v3/admin/order/{id}/itinerary
响应新增:
{
"unreadMessageCount": 0,
"fleetUnreadMessageCount": 2,
"canContactFleet": true,
"contactFleetDisabledReason": null
}
| 字段 | 类型 | 说明 |
|---|---|---|
unreadMessageCount |
Integer | HOUSE 订单会话未读;既有字段,语义不变 |
fleetUnreadMessageCount |
Integer | FLEET 订单会话未读;新增字段;无会话、未登录或软依赖降级时为 0 |
两个字段必须分别消费,不能用 fleetUnreadMessageCount || unreadMessageCount 一类兜底混用,否则房务消息会错误点亮车务按钮。
后端内部取数同时增加可选 unreadScope:
- FLEET 不传或传
TEAM:保持车务看板既有团队共享未读口径。 - FLEET 传
PERSONAL:按指定adminId返回车务发给该定制师的个人未读;订单详情的fleetUnreadMessageCount使用此口径。 - HOUSE:仍按成员行个人未读统计,行为不变。
该字段属于 order-v3 → user-service 内部契约,管理后台无需直接传递。
3. 前端实现要求
3.1 按钮角标
VehicleArrangeCard.vue 的「联系车务」按钮按 RoomArrangeCard.vue 原样复用 NBadge:
fleetUnreadMessageCount > 0时显示红色数字角标。max=99,0自动隐藏。- 按钮禁用时仍可保留未读提示,不能因为
canContactFleet=false静默吞掉既有会话未读;是否允许重新开会话继续遵守后端门控。 - 样式、偏移、尺寸与「联系房务」保持一致,不新增另一套红点 CSS。
v3Adapter.js 需把行程接口的 fleetUnreadMessageCount 映射为独立本地字段(建议 itineraryFleetUnreadCount),不要覆盖现有 itineraryUnreadCount。
3.2 SSE 实时刷新
订单详情现有 lastChatSignal 监听只匹配 HOUSE:{orderId}。需要同时支持:
HOUSE:{orderId} -> 刷新联系房务角标
FLEET:{orderId} -> 刷新联系车务角标
收到当前订单的 FLEET:{orderId} im-chat / im-chat-read 信令后,轻量重拉行程接口并只合并 fleetUnreadMessageCount;不能整页闪骨架屏,也不能把其他订单的全局未读数套到当前订单。
3.3 已读清零与竞态
- 打开「联系车务」并收到聊天抽屉
read事件后,立即把当前订单车务角标本地清零。 - 房务已读只清 HOUSE 字段,车务已读只清 FLEET 字段,互不影响。
- 复用房务现有
chatReadEpoch(或等价版本号)防竞态:已读期间较早发出的刷新请求返回时,不得把旧未读数重新覆盖成红点。 - 切换订单时按新
orderId重算,不能沿用上一个订单角标。
4. 验收场景
- 当前订单无未读时,「联系车务」不显示角标。
- 车务给本单定制师发送 1 条消息后,不刷新页面,顶部铃铛和本单「联系车务」都立即显示红点/数字
1。 - 当前订单无车务未读、其他订单有车务未读时,顶部铃铛可亮,但当前订单「联系车务」不亮。
- 当前订单只有房务未读时,只点亮「联系房务」,不得点亮「联系车务」。
- 打开本单车务会话并读完后,「联系车务」角标立即消失,顶部铃铛与站内信列表同步收敛。
- 已读操作与 SSE 刷新并发时,旧请求不会让已清零红点复现。
- 角标样式、最大数字和按钮布局与房务模块一致。
5. 兼容性
新增响应字段为加性变更。未接入新字段的旧前端行为不变;前端接入后仍使用既有 POST /admin/message/chat/open-fleet 打开会话,orderId 必须使用数字雪花字符串,不能使用 HL... 展示号。
6. 2026-07-23 车务看板回归补充
本节针对车务管理员在「派单看板」点击「联系定制师」的反向会话入口。它使用车务团队
TEAM 未读口径,不得复用定制师订单详情的 PERSONAL 字段。
6.1 首次点击必须立即打开真实会话
当前 FleetBoard 在首次点击时同一轮设置 chatOrder 和 chatOpen=true,随后通过
v-if="chatOrder" 首次挂载 ChatDrawer。ChatDrawer 对 props.show 的 watcher 没有
立即执行,因此组件以 show=true 首次挂载时不会调用 open-fleet,只显示默认「对端」
和空线程;关闭后第二次发生 false -> true 才会正常调用接口。
前端需要修复该生命周期缺口:
ChatDrawer首次挂载且show=true时必须执行一次openFlow();建议在现有合并 watcher 保留flush: 'post'并增加immediate: true,或采用等价的挂载处理。- 首次点击只能调用一次
POST /admin/message/chat/open-fleet,不能因show/bizId同轮变化 重复打开或让后一个请求取消前一个请求。 - 首次接口响应后立即展示真实
peerName/peerRoleLabel/thread;加载完成前保持 loading, 不能先落成可交互的「对端」空会话。 show=false首次挂载不得调用打开接口;之后每次false -> true仍只调用一次。
6.2 车务看板未读角标必须实时刷新
车务看板列表、密集视图和详情抽屉虽然已经消费 unreadMessageCount,但当前只订阅
useFleetDispatchRefresh 的派车业务信令,没有订阅聊天总线 lastChatSignal。因此定制师
发来 FLEET:{orderId} 新消息后只能整页刷新才出现角标。
前端需要按房务模块的方式补齐:
- 监听全局
lastChatSignal,仅处理当前页订单的FLEET:{orderId}im-chat/im-chat-read信令;其他模块和其他订单不得误刷新角标。 - 信令只表示“数据变化”,不能把顶部铃铛的合并未读数直接写进订单。应轻量重拉当前筛选/
分页的车务看板订单接口,并只合并对应行的
unreadMessageCount。 - 轻量刷新不得切换全页 loading、闪白、重置筛选、分页或滚动位置;短时间连续信令需要合并。
- 列表
orders、当前activeOrder、当前chatOrder的同一订单计数必须一起收敛。 - 打开会话收到
read后立即本地清零,并使用读态纪元/刷新序号防止较早发出的异步刷新把 旧未读数重新覆盖回来。 - 聊天抽屉正在打开当前会话时由
ChatDrawer实时拉消息并标已读;看板 watcher 不得与其 抢写非零计数。
6.3 回归验收
- 清缓存后首次点击任一订单「联系定制师」,只发起一次
open-fleet,直接显示真实定制师及历史消息,不出现「对端」空会话。 - 关闭再打开同一订单,行为与首次一致,不依赖“点第二次才正常”。
- 定制师给该订单发送 1 条新消息后,车务不刷新页面即可在对应订单按钮看到房务同款红色数字角标。
- 新消息只更新对应订单;其他订单、HOUSE 会话和顶部全局未读不得误点亮该按钮。
- 实时更新角标时页面不闪 loading,筛选、分页、滚动位置保持不变。
- 打开会话后角标立即清零;异步刷新晚返回也不会让红点复现。
- 列表视图、密集视图、订单详情抽屉三处计数一致。
- 增加组件测试:
ChatDrawer(show=true)首挂打开一次、show=false首挂不打开、聊天信令轻量更新/已读竞态不复亮。