From a2866a89cf761e9cdad46357b3d6d1159acd2214 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Sat, 20 Jun 2026 10:28:39 +0800 Subject: [PATCH] =?UTF-8?q?docs(v2):=20=E6=88=BF=E5=8A=A1+=E5=AE=9A?= =?UTF-8?q?=E5=88=B6=E5=B8=88=E6=8F=90=E6=88=BF=E9=9C=80=E6=B1=82+?= =?UTF-8?q?=E7=AB=99=E5=86=85=E4=BF=A1=E8=81=8A=E5=A4=A9=20=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E5=AF=B9=E6=8E=A5=E6=80=BB=E8=A7=88(=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=90=8E=E5=8F=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...师提房需求+站内信聊天_前端对接总览-管理后台.md | 361 ++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 changelogs-v2/2026-06/20_房务+定制师提房需求+站内信聊天_前端对接总览-管理后台.md diff --git a/changelogs-v2/2026-06/20_房务+定制师提房需求+站内信聊天_前端对接总览-管理后台.md b/changelogs-v2/2026-06/20_房务+定制师提房需求+站内信聊天_前端对接总览-管理后台.md new file mode 100644 index 0000000..dc80bc8 --- /dev/null +++ b/changelogs-v2/2026-06/20_房务+定制师提房需求+站内信聊天_前端对接总览-管理后台.md @@ -0,0 +1,361 @@ +# 房务模块 + 定制师提房需求 + 站内信聊天 — 前端对接总览(管理后台) + +> 变更类型:📘 对接总览(三域接口契约汇总,供前端排期对接) +> 端类型:管理后台(房务管家工作台 / 订单·定制师提房需求 / 站内信聊天) +> 日期:2026-06-20 +> 服务:hl-order-service-v3(房务 + 定制师提房需求)、hl-user-service(站内信聊天) +> 关联工单:#2728 房务工作台、#4013 房务侧 6 态、#4029/#4039/#4047/#4052/#4066/#4070~#4074/#4101 等 + +--- + +## ⚠️ 关键说明(务必先读) + +1. **统一网关 + 鉴权**:所有 admin 接口走网关 `https://api.test.1814.love:9443`,请求头 `Authorization: Bearer `。登录 `POST /admin/auth/login {username,password}` → `data.token`。`X-Admin-Id` 由网关从 token 注入,**前端不要自己设**("当前操作人/归属/未读"全部据此判定)。 +2. **雪花 ID 一律是字符串**:所有 `id/orderId/requirementId/assignmentId/hotelId/consultantId/...` 等雪花 Long 在 JSON 里都是**字符串**(全局 `ToStringSerializer`)。前端按 string 接收、原样回传,**禁止 `parseInt`/`Number()`** 否则丢精度。 +3. **金额一律是字符串**:所有 `BigDecimal` 金额(`totalAmount/sellPrice/protoPrice/budget/replyPrice/...`)在 JSON 里是**字符串**(如 `"600.00"`)。前端按 string 展示/计算(用 decimal 库),不要当 number。 +4. **统一响应包**:`{ "code": 200, "msg": "success", "data": {...} }`。HTTP 始终 200,业务错误用 `code` + 6 位错误码区分(见各域错误码表)。分页统一 `PageResult{ list/total }`(部分接口为 `{list,total,stats}`)。 +5. **错误码段位**:房务 order-v3 = `808xxx`;定制师提房需求 = `582xxx`;站内信 user-service = `281xxx`。 +6. **本总览=导航 + 核心契约**:高频建屏接口给到字段级 + 示例;其余给端点 + 用途 + 关键参数,**完整字段以 Knife4j/Swagger 在线文档为准**(每个 VO 都有 `@ApiModelProperty` 中文)。 + +--- + +# 模块一 · 定制师提房需求(hl-order-service-v3) + +定制师在订单里提交/修改住宿需求 → 生成 `order_hotel_requirement`(INSERT-only 版本化)→ 进抢单池给房务抢。控制器 `HotelRequirementAdminController`,全部 `/v3/admin/order/**`(网关已覆盖)。 + +## 1.1 端点总览 + +| 方法 | 路径 | 用途 | 调用角色 | +|---|---|---|---| +| **PUT** | `/v3/admin/order/{id}/hotel-requirement` | **提交/修改/调整房型需求(核心入口·三分支自动判断)** | 定制师 | +| GET | `/v3/admin/order/{id}/room` | 查询房间分配列表(家庭维度) | 定制师 | +| POST | `/v3/admin/order/{id}/room` | 新增房间分配(家庭维度) | 定制师 | +| PUT | `/v3/admin/order/{id}/room/{roomAssignmentId}` | 编辑房间分配 | 定制师 | +| POST | `/v3/admin/order/{id}/hotel-requirement/dispatch` | 团期管理员提交房务(PENDING_REVIEW→PENDING) | 团期管理员 | +| POST | `/v3/admin/order/{id}/hotel-requirement/reject` | 团期管理员打回定制师 | 团期管理员 | + +> 另有 `POST /v3/admin/order/hotel-requirement/{requirementId}/assign`(房控老式单次配房)属房务侧,建议房务工作台统一走【模块二 · 2.4 批量配房】。 + +## 1.2 核心接口:提交/修改/调整房型需求 + +**`PUT /v3/admin/order/{id}/hotel-requirement`** —— 定制师提房需求唯一入口。后端按订单当前是否有 active 需求 + 状态**自动判断三分支**,前端无需区分: + +- 无 active 行 → `INIT_SUBMIT`(首次提交) +- active 且 `PENDING` → `PENDING_EDIT`(编辑草稿,不增版本号) +- active 且 `DONE` → `DONE_ADJUST`(已配房后调整,自动回 PENDING/PENDING_REVIEW 并软删旧配房) + +**请求体** + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| days | `List` | 是 | 每晚用房安排,**长度必须 = 订单 tripNights** | +| specialTags | `List` | 否 | 特殊诉求标签(高楼层/景观房/无烟房…,前端自定义多选,后端不做枚举校验) | +| remark | String(≤500) | 否 | 需求备注 | + +`DayHotelReq`: + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| dayNumber | Integer | 是 | 第几晚(1..tripNights,不重复) | +| hotels | `List` | 是 | 同晚酒店列表(≥1) | +| remark | String(≤200) | 否 | 当晚备注 | + +`HotelDetailReq`: + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| hotelId | Long | 是 | 资源域酒店 ID(须存在且已发布) | +| roomCategory | String | 是 | 房型分类字典 `room_category`:STANDARD/SINGLE/TWIN/QUEEN/KING/SUITE/FAMILY/YURT/SPECIAL | +| roomCount | Integer | 是 | 房间数(>0) | +| budget | BigDecimal | 条件 | 预算/晚。**自由行必填、跟团传 null**。⚠️ **工单 #4047:后端按协议价独算覆盖、定制师不可改(预算锁)**,前端可展示协议价做建议值但提交后端会以协议价为准 | +| hotelName | String | 否 | 酒店名快照(前端传,供回显,后端不自动查资源) | +| source | String | 否 | 来源标记,默认 MANUAL | + +> `stayDate`(每晚入住日)**前端不传**,后端按 `departDate + (dayNumber-1)` 反推。 + +**响应体(HotelRequirementRespVO)** + +| 字段 | 类型 | 说明 | +|---|---|---| +| requirementId | Long(字符串) | 需求行 ID | +| version | Integer | 版本号(INSERT-only 单调递增;PENDING 反复改不增) | +| isActive | Boolean | 是否当前生效版本 | +| status | String | 需求状态(见 1.3 状态机) | +| branchTaken | String | 实际走的分支:INIT_SUBMIT / PENDING_EDIT / DONE_ADJUST | +| submittedAt | LocalDateTime | 首提时间(PENDING 改不变,保抢单公平) | +| previousVersion | Integer | DONE_ADJUST 分支:上一版本号 | +| assignmentDeletedCount | Integer | DONE_ADJUST 分支:软删旧配房行数 | +| claimerId/claimerName/claimedAt | — | 抢单房务信息(已被抢时有值) | + +**请求示例** + +```bash +curl -k -X PUT "https://api.test.1814.love:9443/v3/admin/order/60001/hotel-requirement" \ + -H "Authorization: Bearer " -H "Content-Type: application/json" -d '{ + "days": [ + {"dayNumber":1,"remark":"靠近景点","hotels":[ + {"hotelId":80001001,"roomCategory":"TWIN","roomCount":2,"budget":600.00,"hotelName":"拉萨西藏宾馆"} + ]} + ], + "specialTags": ["高楼层","景观房"], + "remark": "夫妻房,要求安静" +}' +``` + +## 1.3 需求状态机(RequirementStatus) + +| 枚举 | 中文 | 说明 | +|---|---|---| +| PENDING | 待房务配 | 已提交进抢单池(核心订单提交后直达此态) | +| PROCESSING | 配房中 | 已被抢,处理中 | +| DONE | 配房完成 | 配房完成 | +| PENDING_REVIEW | 待审核 | **仅团期**:定制师提交后等团期管理员审核 | +| REJECTED_TO_CONSULTANT | 已打回定制师 | 房务/管理员打回,定制师改后重提 | +| REJECTED_TO_ADMIN | 已打回团期管理员 | **仅团期**:房务打回回到管理员重审 | + +- **核心订单**:提交 → PENDING →(DONE / 打回 REJECTED_TO_CONSULTANT)。 +- **团期订单**:提交 → PENDING_REVIEW →(管理员 dispatch → PENDING / 管理员 reject → REJECTED_TO_CONSULTANT)。 + +## 1.4 错误码(582xxx) + +| 码 | 说明 | +|---|---| +| 582001 | 订单不存在 | +| 582011 | days 长度与订单 tripNights 不一致 | +| 582012 | dayNumber 越界或重复 | +| 582013 | hotelId 不存在或已下架 | +| 582014 | 跟团模式 budget 必须为空 | +| 582015 | 自由行模式 budget 必填 | +| 582016 | roomCount 必须 > 0 | +| 582017 | 订单状态不允许提交需求(已结算/已取消) | +| 582018 | roomCategory 不在字典中 | +| 582030 | 当前需求状态不可修改(PROCESSING) | +| 582042 | 已签合同需走调整订单工作流 | + +--- + +# 模块二 · 房务管家工作台(hl-order-service-v3) + +房务工作台全功能。`/v3/admin/order/**`、`/v3/admin/house/**`、`/admin/house/**`、`/v3/admin/hotel-candidates` 网关均已覆盖。 + +## 2.1 端点总览 + +| 分组 | 方法 | 路径 | 用途 | +|---|---|---|---| +| **抢单池** | GET | `/v3/admin/order/grab-pool/hotel-requirements` | 抢单池列表(分页+多维筛选) | +| | POST | `/v3/admin/order/hotel-requirements/{requirementId}/claim` | 抢单(乐观锁先到先得) | +| | POST | `/v3/admin/order/hotel-requirements/{requirementId}/transfer` | 转单 / 超管强制指派 | +| | POST | `/v3/admin/order/hotel-requirements/{requirementId}/release` | 释放回抢单池 | +| | GET | `/v3/admin/order/grab-pool/my-claims/hotel` | 我的接单(分页+stats) | +| | GET | `/v3/admin/house/staff` | 转单可选房务员工 | +| **候选酒店** | GET | `/v3/admin/hotel-candidates` | 候选酒店统一查询(4 场景) | +| **配房** | POST | `/v3/admin/order/hotel-requirements/{requirementId}/assignments` | **批量提交配房方案** | +| | PUT | `/v3/admin/order/assignments/{id}` | 修改单条配房(含**改协议价 #4072**) | +| | DELETE | `/v3/admin/order/assignments/{id}` | 删除配房(软删) | +| | GET | `/v3/admin/order/orders/{orderId}/rooms` | 房间分配查询(按家庭分组) | +| | POST | `/v3/admin/order/assignments/{assignmentId}/rooms` | 家庭维度房间分配(整批覆盖) | +| **订单详情** | GET | `/admin/house/orders/{orderId}` | 房务侧订单详情聚合 | +| | GET | `/admin/house/orders/{orderId}/requirement-history` | 需求历史(含 diff/退回) | +| **最终确认** | POST | `/admin/house/assignments/requirements/{requirementId}/finalize` | 房务点最终确认 | +| | POST | `/admin/house/assignments/requirements/{requirementId}/receipts` | 上传确认回执(multipart) | +| | GET | `/admin/house/assignments/requirements/{requirementId}/receipts` | 查询回执列表 | +| **询房** | POST | `/v3/admin/order/inquiry/send` | 发起询房 | +| | POST | `/v3/admin/order/inquiry/{inquiryId}/reply` | 回填酒店回复(可同步建配房) | +| | GET | `/v3/admin/order/inquiry` | 询房历史(分页) | +| | POST | `/v3/admin/order/inquiry/{originalInquiryId}/resend` | 重发询房 | +| | POST | `/v3/admin/order/inquiry/{inquiryId}/escalate` | 加急/催办 | +| **换酒店** | GET | `/v3/admin/order/swap-hotel/candidates` | 换酒店候选(同城可用) | +| | POST | `/v3/admin/order/swap-hotel/preview` | 换酒店预览差价 | +| | POST | `/v3/admin/order/swap-hotel/commit` | 换酒店提交(触发联动) | +| **待办** | GET | `/v3/admin/order/todos` | 房务待办列表(多筛选+分类stats) | +| **日历** | GET | `/admin/house/calendar` | 房务日历(月历+状态点) | +| **酒店视图** | GET | `/v3/admin/house/hotels` | 房务酒店列表 | +| | GET | `/v3/admin/house/hotels/{hotelId}` | 酒店详情 4Tab 聚合 | +| | POST | `/v3/admin/house/hotels/{hotelId}/check-log` | 新增核房记录 | +| | GET | `/v3/admin/house/hotels/{hotelId}/check-log` | 核房历史 | +| | GET | `/v3/admin/house/hotels/{hotelId}/operation-log` | 酒店维度操作日志 | +| | GET | `/admin/house/orders/{orderId}/operation-log` | 订单维度操作日志 | +| **月度对账** | GET | `/v3/admin/house/reconciliation/monthly` | 月度对账(按酒店现结汇总) | +| | GET | `/v3/admin/house/reconciliation/monthly/export` | 月度对账 Excel 导出 | + +## 2.2 抢单池列表 `GET /v3/admin/order/grab-pool/hotel-requirements` + +**Query**:`keyword`(≤32) / `productType`(CORE/GROUP/CUSTOM) / `productName` / `consultantId` / `guestName` / `departDateFrom` / `departDateTo` / `page`(默认1) / `pageSize`(1-100,默认20) / `sortBy`(默认 createTime,desc,可 departDate,asc)。 + +**响应 `PageResult`**(行卡片,挑关键): + +| 字段 | 类型 | 说明 | +|---|---|---| +| id | Long(串) | 房型需求 ID(抢单用) | +| orderId / orderNo | Long(串)/String | 订单 ID/订单号 | +| guestName / personsDesc | String | 客人姓名 / 人数描述(如 2大1小) | +| productType / productName / productNo | String | 产品类型/名/编号 | +| route / departDate / nights | String/Date/Int | 档位·夜数 / 出行日 / 夜数 | +| cities | `List` | 行程城市(去重中文) | +| totalAmount | BigDecimal(串) | 订单总额 | +| consultantName / **consultantId** | String/Long(串) | 定制师姓名 / **adminId(#4101 新增,开聊天用,见模块三)** | +| requirementNote / special | String/`List` | 需求备注 / 特殊诉求标签 | +| requirementVersion | Integer | 需求版本(>1 时 UI 标"已修订") | +| createTime | LocalDateTime | 创建时间 | + +> **我的接单** `GET .../my-claims/hotel` 响应为 `{list, total, stats}`,`stats={inProgress,pendingConfirm,confirmed,exception}`;行 VO 同样含 `consultantId` + `unreadMessageCount`(聊天未读红点,见模块三)。 + +## 2.3 候选酒店 `GET /v3/admin/hotel-candidates` + +**Query**:`orderId`(必填) / `dayNumber` / `stayDate` / `city` / `keyword`(非空时跨城搜) / `limit`(默认10上限50) / `roomCategory` / `roomCount` / `preferredHotelId` / `requirementId`。 + +**响应 HotelCandidateRespVO**:`stayDate/city/productType/candidates[]`,每个候选含酒店信息、协议价/售价、可用房、运营标签 `tags`(**#4070 复用酒店标签库**:响应快/常合作等)、推荐理由等。 + +## 2.4 批量提交配房 `POST /v3/admin/order/hotel-requirements/{requirementId}/assignments` + +幂等:同 requirementId 3 秒窗口去重 + 30 秒分布式锁。 + +**请求 `{ items: List }`**,每项: + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| dayNumber | Integer | 是 | 第几天(≥1) | +| familyIndex | Integer | 否 | 家庭序号(多家庭团区分,默认1) | +| hotelId / roomTypeId | Long | 是 | 酒店/房型 ID(resource) | +| roomCategory | String | 是 | 房型字典 code | +| roomCount | Integer | 是 | 间数(≥1) | +| sellPrice | BigDecimal | 是 | 售价(元/间·晚,≥0) | +| remark | String | 否 | 备注 | + +**响应 `{successCount,failCount,items[]}`**,item:`dayNumber/familyIndex/assignmentId(串)/arrange(pending/waiting/confirmed/problem)`。 + +## 2.5 修改配房 `PUT /v3/admin/order/assignments/{id}`(含改协议价 #4072) + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| **protoPrice** | BigDecimal | 否 | **新协议价(#4072 房务改协议价)。后端 afterCommit 自动写回 resource 价格日历(协议价单源真相)** | +| sellPrice | BigDecimal | 否 | 新售价 | +| remark | String | 否 | 备注 | +| syncHotelSnapshot | Boolean | 否 | 重新拉酒店主数据覆盖快照 | + +> **改价口径**:房务侧只改**协议价**(本接口 `protoPrice`);**定制师改结算价不在房务、也不在"提需求"那步**,另在"确认订单"环节处理,前端房务工作台无需提供改结算价入口。 + +## 2.6 询房(核心两个) + +**发起 `POST /v3/admin/order/inquiry/send`**:`requirementId/hotelId/dayNumber/stayDate/nights(1-30)/roomCount(1-99)/roomCategory?/messageBody?(≤1000,留空用模板)/isPriority?/timeoutOverride?(默认240min)/contactName?/contactWechat?`。响应含 `id/status(PENDING)/timeoutAt/wechatTemplate(微信复制粘贴模板)`。 + +**回填 `POST /v3/admin/order/inquiry/{inquiryId}/reply`**:`replyBody(必填1-2000)/replyPrice?/replyAvailable?(REJECTED 时必须 0)/replyStatus(REPLIED/REJECTED/CONFIRMED)/convertToAssignment?(true 同步建配房)/isPartialReply?`。`convertToAssignment=true` 时响应回 `convertedAssignmentId`。 + +> 询房=线下微信沟通的**记录/状态机**载体,不是即时通讯;**房务↔定制师的实时沟通走站内信聊天(模块三)**,两者不同。 + +## 2.7 换酒店(三步) + +`GET /swap-hotel/candidates?assignmentId=&level=` → `POST /swap-hotel/preview`(算差价 autoDiff/appliedDiff/totalDiff/diffDirection 升级·降级·持平)→ `POST /swap-hotel/commit`(`assignmentId/newHotelId/newRoomTypeId?/newRoomCategory?/newSellPrice/nights/diffOverride?/swapReason(4-256)`,响应回 `newAssignmentId/totalDiff/diffDirection`,正数=需补差)。 + +## 2.8 日历 `GET /admin/house/calendar` + +**Query**:`month`(YYYY-MM 必填) / `scope`(mine默认/all) / `status`(inProgress/inquiry/pending/exception,逗号多选)。响应 `{month,summary,days[]}`,days 为完整周排版(28-42 天,含上下月补位 `isCurrentMonth=false`),每天 `tourCount + statusDots[]`(status/label/count/color)。 + +## 2.9 月度对账 `GET /v3/admin/house/reconciliation/monthly?month=YYYY-MM`(#4074) + +仅统计**现结**(settlement paymentMethod=CASH_PAID)。响应 `{month,overview,hotels[]}`: + +- `overview`:`hotelCount/orderCount/roomNights/totalAmount/paidAmount/unpaidAmount`(金额均字符串)。 +- `hotels[]`:`hotelId(串)/hotelName/orderCount/roomNights/totalAmount/avgPrice/paidAmount/unpaidAmount`,应付降序。 + +导出 `GET .../monthly/export?month=YYYY-MM` → `.xlsx` 文件流(`Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,文件名 `house-reconciliation-{month}.xlsx`)。空月返空列表 + 全 0 概览。 + +## 2.10 房务错误码(808xxx) + +| 段 | 码 → 说明(节选高频) | +|---|---| +| 抢单 0080xx | 808001 已被他人抢 / 808003 达30单上限 / 808010 非本人需求 / 808013 转单达3次 / 808021 已有confirmed无法释放 / 808090 未登录或非房务 | +| 配房 0081xx | 808100 需求不存在 / 808101 需求未抢 / 808102 dayNumber越界 / 808110 非本人需求 / 808120 配房不存在 / 808130 房间数超配 / 808131 床型不在字典 / 808140 订单不存在 / 808141 订单非房务可见 / 808150 month格式错 / 808170 对账month格式错 / 808180 未全部配房 / 808181 有未完结询房 / 808182 状态不允许最终确认 / 808186 状态不允许上传回执 | +| 询房 0082xx | 808200 非本人需求 / 808201 酒店ID无效 / 808202 该酒店该晚已有PENDING询房 / 808210 询问不存在 / 808211 询问已结束 / 808220 orderId与requirementId至少传一 | +| 换酒店 0084xx | 808400 原配房不存在 / 808401 新酒店今日无可用房 / 808402 无权操作 / 808403 库存冲突 / 808404 新旧酒店相同 / 808405 配房已被换过 | +| 酒店视图 0085xx | 808500 酒店不存在 / 808501 totalAvailable与byRoomType不一致 / 808502 该日已有核房记录 / 808503 byRoomType为空 | + +--- + +# 模块三 · 站内信聊天(hl-user-service) + +站内信已升级为**双向 1:1 会话 + SSE 实时 + 敏感词过滤**。房务↔定制师围绕订单的实时沟通走这里(区别于询房的线下记录)。控制器 `ChatMessageController`,`/admin/message/chat/**`。**只做聊天,不推业务通知**。 + +## 3.1 端点总览 + +| 方法 | 路径 | 用途 | +|---|---|---| +| POST | `/admin/message/chat/open` | 打开/找回 1:1 会话(幂等) | +| GET | `/admin/message/chat/conversations` | 我的会话列表(含订单摘要/待回复 tab) | +| GET | `/admin/message/chat/{conversationKey}/messages` | 拉线程消息(游标分页) | +| POST | `/admin/message/chat/{conversationKey}/messages` | 发一条消息 | +| POST | `/admin/message/chat/{conversationKey}/read` | 标记已读到最新 | +| GET | `/admin/message/chat/unread-total` | 我的聊天未读总数(聊天 Tab 专用) | +| GET | `/ws/admin-msg/stream` | **SSE 实时推送长连接** | +| GET | `/admin/message/unread-count` | 顶部合并角标(聊天+通知,已有,不在本次范围但前端会用) | + +## 3.2 打开会话 `POST /admin/message/chat/open` + +**请求**:`bizModule`(HOUSE/FLEET/DIRECT) / `bizId`(HOUSE=orderId;DIRECT 留空) / `peerAdminId`(对方 adminId)。 +**响应**:`conversationKey / peerAdminId / peerName / peerRole / unreadCount / isNew`。 + +> **房务从订单卡片开会话**:`{bizModule:"HOUSE", bizId:, peerAdminId:}`,`consultantId` 取自抢单池/我的接单列表行(#4101 已补到列表 VO,无需进详情)。同业务对象同两人重复 open 返 `isNew=false`,前端可缓存 conversationKey 避免重复调。 + +## 3.3 会话列表 `GET /admin/message/chat/conversations` + +**Query**:`bizModule?` / `bizId?`(配合实现"围绕某订单的会话")/ `onlyPending?`(true=只看待回复) / `pageNo`(默认1) / `pageSize`(1-50,默认20)。 + +**响应 `PageResult`**:`conversationKey / bizModule / bizId(串) / peerAdminId(串) / peerName / peerRole / unreadCount / lastMessagePreview / lastMessageAt / status(ACTIVE/ARCHIVED) / peerAvatar` + **HOUSE 会话专属**:`orderNo / customerName / destination / tripDays / bizStatusLabel(中文如"待配房") / progressDesc(如"已配 2/4 间") / pendingReply`。按最后活跃倒序。 + +## 3.4 拉消息 / 发消息 / 已读 + +- **拉 `GET /{conversationKey}/messages`**:`beforeId?`(游标,首屏不传=拉最新一页) / `pageSize`(1-50)。响应 `{conversationKey,hasMore,nextCursor,list[]}`,向上翻页用 `nextCursor` 当下次 `beforeId`。消息项:`messageId(串)/senderAdminId(串)/senderName/senderRole/msgType/priority/content/isMine/sentAt`。 +- **发 `POST /{conversationKey}/messages`**:`content`(必填,≤1000) / `priority?`(NORMAL默认/URGENT) / `msgType?`(TEXT默认/IMAGE/SYSTEM)。响应 `{messageId,conversationKey,sentAt}`。**限流 20 条/60 秒/会话**。 +- **已读 `POST /{conversationKey}/read`**:把会话标记已读到最新,`unreadCount` 归 0。响应 `{conversationKey,unreadCount:0,lastReadMessageId}`。**建议发消息后/进入会话时自动调一次**。 +- **未读总数 `GET /admin/message/chat/unread-total`** → `{chatUnreadTotal}`(仅 CHAT,聊天 Tab 红点)。 + +## 3.5 SSE 实时推送 `GET /ws/admin-msg/stream` + +前端用 `EventSource` 连接,token 走 query:`/ws/admin-msg/stream?token=`(网关已放行 /ws/ 前缀、注入 X-Admin-Id;SSE 路由 response-timeout 已加大到 1h 防掐断)。事件: + +| 事件名 | 触发 | 载荷关键字段 | +|---|---|---| +| `connected` | 建连即发 | "ok" | +| `im-chat` | 新聊天消息(type=CHAT) | adminId / unreadCount / conversationKey / senderName / preview / priority | +| `message` | 通知类(type=NOTIFY 或老信令) | adminId / unreadCount / title | +| `ping` | 每 25s 心跳(注释行,不触发 onmessage) | — | + +> 前端按**事件名**分发:`im-chat` 刷新聊天会话/红点,`message` 走原通知逻辑。 + +## 3.6 会话 key 规则 + 枚举 + +- **会话 key**:`{bizModule}:{bizId}:{minAdminId}:{maxAdminId}`,两人 adminId 强制小者在前,保证双向命中同一会话不分叉。DIRECT 纯私聊 bizId 用 `0` 占位。示例 `HOUSE:70123:101:205`。 +- **bizModule**:HOUSE(房务·bizId=orderId) / FLEET(车务·bizId=派车单) / DIRECT(纯私聊·bizId=0) / SYSTEM(系统通知专用)。 +- **msgType**:TEXT(默认) / IMAGE(图片URL) / SYSTEM(系统提示,senderAdminId 空)。 +- **敏感词**:后端发送时本地 DFA 命中替换为等长 `*`、**不报错**。前端**无需自己过滤**,直接展示返回的 `content`。 + +## 3.7 站内信错误码(281xxx) + +| 码 | HTTP语义 | 说明 | +|---|---|---| +| 281001 | 404 | 会话不存在(对未 open 的 key 操作) | +| 281002 | 403 | 无权访问该会话(非成员) | +| 281003 | 409 | 会话正在创建,请稍后重试(open 并发) | +| 281004 | 429 | 发送过于频繁(限流 20/60s) | +| 281005 | 400 | 不能与自己创建会话 | +| 281010 | 400 | 目标员工不存在或已离职 | +| 281011 | 400 | 消息内容不合法(空/超 1000 字) | + +--- + +# 前端联调 Checklist + +1. 登录拿 token → 所有请求带 `Authorization: Bearer`,不要手设 `X-Admin-Id`。 +2. **所有 id/金额按字符串处理**(雪花精度 + 金额精度)。 +3. 定制师提房:`PUT /v3/admin/order/{id}/hotel-requirement`,days 长度=tripNights;budget 自由行必填/跟团 null,**别在前端锁死预算值**(后端按协议价覆盖)。 +4. 房务工作台:抢单池列表 → claim → 候选 `/v3/admin/hotel-candidates` → 批量配房 `/assignments` → (改协议价走 `PUT /assignments/{id}` 的 `protoPrice`)→ finalize + 上传回执。 +5. 聊天:列表卡片 `consultantId` → `open(HOUSE,orderId,consultantId)` → messages/send/read;并发起一条 `EventSource('/ws/admin-msg/stream?token=')` 监听 `im-chat`/`message`。 +6. 完整字段以测试服 Knife4j/Swagger 为准(每 VO 有中文 `@ApiModelProperty`),本总览覆盖端点+核心契约+错误码+枚举。 + +--- + +## 备注 + +- 本总览三域接口均已部署测试服并通过 9443 实测;网关路由全覆盖,无需新增。 +- 站内信"接入房务"= 房务↔定制师 1:1 聊天(已完备);**不做房务事件→站内信通知**(配房完成/待配房等 NOTIFY 推送暂不做)。 +- 月度对账/酒店富表单等后端 schema+API 已就位,对应**表单/列表 UI 由前端实现**。