# 房务模块 + 定制师提房需求 + 站内信聊天 — 前端对接总览(管理后台) > 变更类型:📘 对接总览(三域接口契约汇总,供前端排期对接) > 端类型:管理后台(房务管家工作台 / 订单·定制师提房需求 / 站内信聊天) > 日期: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. **GET 查询的日期参数格式**(务必遵守,否则报 500 转换异常):`LocalDate` 传 `yyyy-MM-dd`(如 `2026-06-20`);`LocalDateTime`(各种 `*From/*To`、`createTimeFrom`、`claimedAtFrom`、`repliedAtFrom` 等)**必须带 `T` 和时分秒** `yyyy-MM-dd'T'HH:mm:ss`(如 `2026-06-20T00:00:00`),不能只传日期。POST 请求体(`@RequestBody`)里的日期不受此限,传 `yyyy-MM-dd` 即可。详见同日《房务 GET 查询日期参数修复》changelog。 7. **本总览=导航 + 核心契约**:高频建屏接口给到字段级 + 示例;其余给端点 + 用途 + 关键参数,**完整字段以 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 | `/v3/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 同样含 `totalAmount`(#4123 新增,String,与抢单池同口径)+ `consultantId` + `unreadMessageCount`(聊天未读红点,见模块三)。 > **stats 空态说明**:`pendingConfirm`(待确认=PENDING_FINALIZE) / `exception`(异常=EXCEPTION) 计数随真实数据出,库里这两态 0 行时即为 0(空态,非 bug,**勿隐藏 tab**),订单走到配房全确认 / 拒单超时即出数。 > **productType 筛选与徽标**:`productType` 跨服务字段不下推 SQL(当页内存精筛),故筛选时 `total` 仍是池总数(= 待抢徽标语义=池内总数),筛 productType 时前端弱化该徽标即可。 **转单 `POST /v3/admin/order/hotel-requirements/{requirementId}/transfer`** 请求体 `HouseTransferReqVO`: | Key | 类型 | 必填 | 说明 | |---|---|---|---| | toUserId | Long(串) | ✅ | 接收人房务 ID(admin_id) | | reason | String | ✅ | 原因(普通房务 ≥1 字 / 超管 ≥10 字,1~200) | | skipUpperLimit | Boolean | 否 | 跳过 30 单上限(仅超管有效,默认 false) | ## 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 由前端实现**。