# 房务模块 + 定制师提房需求 + 站内信聊天 — 前端对接总览（管理后台）

> 变更类型：📘 对接总览（三域接口契约汇总，供前端排期对接）
> 端类型：管理后台（房务管家工作台 / 订单·定制师提房需求 / 站内信聊天）
> 日期：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 <token>`。登录 `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<DayHotelReq>` | 是 | 每晚用房安排，**长度必须 = 订单 tripNights** |
| specialTags | `List<String>` | 否 | 特殊诉求标签（高楼层/景观房/无烟房…，前端自定义多选，后端不做枚举校验） |
| remark | String(≤500) | 否 | 需求备注 |

`DayHotelReq`：

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dayNumber | Integer | 是 | 第几晚（1..tripNights，不重复） |
| hotels | `List<HotelDetailReq>` | 是 | 同晚酒店列表（≥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 <token>" -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<HouseGrabPageItemRespVO>`**（行卡片，挑关键）：

| 字段 | 类型 | 说明 |
|---|---|---|
| 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<String>` | 行程城市（去重中文） |
| totalAmount | BigDecimal(串) | 订单总额 |
| consultantName / **consultantId** | String/Long(串) | 定制师姓名 / **adminId（#4101 新增，开聊天用，见模块三）** |
| requirementNote / special | String/`List<String>` | 需求备注 / 特殊诉求标签 |
| 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<AssignmentItemReqVO> }`**，每项：

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| 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:<orderId>, peerAdminId:<consultantId>}`，`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<ChatConversationRespVO>`**：`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=<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 由前端实现**。
