docs(changelog): 26_8375 房务配房列表合并抢单池(户级 / 团期两张列表)交接件
changelog-filename-gate / validate (push) Failing after 2s

新增 GET /v3/admin/order/house-allocation/households 与 /group-batches,
五个旧抢单池读口标 @Deprecated(行为不变),菜单 101/102 下线、团期站内信链接改指订单列表。
第八节只列测试服 dev-v3 12707c57c 上的实测读数;808612/808613 与 productNo 降级标明未触发。

Refs wx/HL#8375

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
这个提交包含在:
API Changelog Bot
2026-09-26 17:13:39 +08:00
共同撰写人 Claude Opus 5.5
父节点 982c9b1f71
当前提交 e02541943d
@@ -0,0 +1,618 @@
---
schema: "hl-changelog/v2"
ticket: "8375"
title: "房务配房列表改版:并入订单列表,新增两个读端点(户级/团期),旧池端点标废弃,菜单与通知链接改向"
consumer: "admin"
author: "wx(GIT)"
change_type: "新增接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: "2026-09-26"
status_note: "订单列表新增两个读端点替代房务抢单池;五个旧读端点标 @Deprecated;三个错误码文案改动(去「抢单池」);菜单 101/102 下线;团期通知链接改为订单列表。后端三个 PR(product-v2/#8381、order-v3/#8382、user-service/#8379)已合入 dev-v3 并部署测试服,逐条实测验证通过。"
updated_at: "2026-09-26"
base: "dev-v3"
---
# 房务订单列表改版:新读端点替代抢单池、旧端点标废弃、菜单下线、通知改向(管理后台)
> **服务**: hl-order-service-v3(端口 8086/8186)/ hl-user-service(端口 8089/8189)
> **PR**: #8381(product-v2)/ #8382(order-v3)/ #8379(user-service)
> **Issue**: #8375
> **日期**: 2026-09-26
> **影响范围**: 房务管家 › 订单列表的页签切换、筛选项恢复、「开始配房」入口改向、所有房务角色的可见范围放开、旧页面与菜单下线
---
## ⚠️ 关键变化
1. **新增两个读端点**,替代房务抢单池两个旧页面:
- `GET /v3/admin/order/house-allocation/households`:户级(核心/定制订单)
- `GET /v3/admin/order/house-allocation/group-batches`:团期订单
2. **五个旧读端点标 `@Deprecated`**,行为不变,但前端改调新端点:
- `GET /v3/admin/order/grab-pool/hotel-requirements`
- `GET /v3/admin/order/grab-pool/group-batches`
- `GET /v3/admin/order/grab-pool/my-claims/hotel`
- `GET /v3/admin/order/grab-pool/my-claims/group-batches`
- `GET /v3/admin/order/grab-pool/all-claims/hotel`
3. **权限视图放开**:两个新端点都挂读门 `HouseReadPermission`,不限 `scope=all`;普通房务也能看全员认领情况(旧的 `all-claims/hotel` 仍返回 808092)。
4. **认领写口不变**:保持现有逻辑,只是入口从池页改到列表行:
- 户级:`POST /v3/admin/order/hotel-requirements/{requirementId}/claim`
- 团期:`POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/claim`
5. **三个错误码文案改动**,去掉"抢单池"页面名:
- 808650:改为「团期订单须整团认领后配房,不支持逐户认领 / 转单」
- 808612:改为「该团期尚未被房务整团认领」
- 808001:改为「该需求已被其他房务认领或状态已变化,请刷新后重试」
6. **菜单下线**:房务抢单池菜单(菜单 101 与 102)已在 user-service 侧置为 INACTIVE,部署后生效。
7. **团期通知链接改向**:站内链接由 `/housekeeper/grab-pool-group?groupBatchId=` 改为 `/housekeeper/orders?groupBatchId=`(订单列表团期页签)。
---
## 一、背景
房务原来要在"订单列表"和"房务抢单池"两个页面之间来回切,体验割裂。本次改版将**待配房**的单并入**订单列表**,整合三类订单视图:
- 待配房:所有房务都能点「开始配房」认领
- 配房中:谁在做,全员可见
- 已认领:可按「我的」或「全部」筛选
新端点按以下规则返回行:
- **户级行**(order_main 表,group_batch_id 与 product_batch_id 都为空)
- 待配房:`status=PENDING` 且 `claimer_id IS NULL`
- 已认领:`status=PENDING` 且 `claimer_id IS NOT NULL`
- **团期行**(order_group_batch 表,整团一行)
- 待配房:`house_claimer_id IS NULL` 且 `requirement_confirmed=1` 且 `batch_status=RESOURCE_PREPARING`
- 已认领:`house_claimer_id IS NOT NULL`(任意阶段)
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 房务配房列表·户级 | GET | `/v3/admin/order/house-allocation/households` | **新增接口** | 户级页签(核心/定制订单),分页返回 |
| 2 | 房务配房列表·团期 | GET | `/v3/admin/order/house-allocation/group-batches` | **新增接口** | 团期页签,整团一行 |
**旧端点标废弃(行为不变)**:`GET /v3/admin/order/grab-pool/hotel-requirements`、`/grab-pool/group-batches`、`/grab-pool/my-claims/hotel`、`/grab-pool/my-claims/group-batches`、`/grab-pool/all-claims/hotel` 加 `@Deprecated`,详见第五章节。
网关无改动(均在既有 `/v3/admin/order/` 前缀下);新路径已验证可达。
---
## 三、接口详情
### 1. 房务配房列表·户级 `GET /v3/admin/order/house-allocation/households`
**VO**: `HouseAllocationHouseholdPageReqVO` → `HouseAllocationHouseholdPageRespVO`
#### 使用场景
房务管家 › 订单列表的**核心**和**定制**页签,以及**全部**页签的户级数据部分。返回户级订单(order_main),按待配房/已认领分行展示,支持 `scope` 筛选当前用户或全员,按多种条件排序和分页。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| scope | Query | String | 否 | `all` \| `mine`,小写 | 默认 `all`;`mine` 则只返回当前用户认领的行,`pendingClaim` 桶恒为 0 |
| status | Query | String | 否 | `pendingClaim` \| `unfinished` \| `claiming` \| `pendingConfirm` \| `confirmed` \| `exception`,或空 | 默认 `unfinished`;空串表示全部;返回对应 house_status 的行 |
| productType | Query | String | 否 | `CORE` \| `CUSTOM`,或空 | 空或不传表示全部户级行;不接受 `productType=GROUP`(团期订单走团期端点) |
| keyword | Query | String | 否 | `@Size(max=32)` | 模糊匹配:订单号 / 团号 / 客人姓名 / 产品名(OR) |
| productName | Query | String | 否 | — | 模糊匹配产品名 |
| consultantId | Query | Long | 否 | — | 定制师 ID,精确匹配 |
| guestName | Query | String | 否 | — | 客人姓名,模糊匹配 |
| departDateFrom | Query | LocalDate | 否 | `yyyy-MM-dd` | 出发日期起 |
| departDateTo | Query | LocalDate | 否 | `yyyy-MM-dd` | 出发日期止(含) |
| claimedAtFrom | Query | LocalDateTime | 否 | `yyyy-MM-dd'T'HH:mm:ss` | 认领时间起;传了就等于只看已认领的行 |
| claimedAtTo | Query | LocalDateTime | 否 | `yyyy-MM-dd'T'HH:mm:ss` | 认领时间止 |
| page | Query | Integer | 否 | `@Min(1)` | 默认 1 |
| pageSize | Query | Integer | 否 | `@Min(1) @Max(100)` | 默认 20 |
| sortBy | Query | String | 否 | `departDate,asc` \| `createTime,desc` \| `claimedAt,desc` | 默认 `departDate,asc`;**无论选哪个,都先按手动加急(manual_urgent DESC, manual_urgent_at DESC)排** |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| list | List\<HouseAllocationHouseholdRespVO\> | 分页结果行 |
| total | Long | 满足条件的总行数 |
| stats | HouseAllocationHouseholdStatsVO | 各页签的计数(与列表共用筛选条件,忽略 status) |
**行字段详情(HouseAllocationHouseholdRespVO)**:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | String | requirementId,前端「开始配房」时用此值 |
| orderId | String | 订单 ID,进户级详情时用 |
| orderNo / teamNo | String | 订单号 / 团号 |
| guestName | String | 客人姓名 |
| personsDesc | String | 人数描述(如「2成人1儿童」) |
| productType | String | 产品类型(CORE/CUSTOM/ROUTE/…) |
| productName | String | 产品名称 |
| productNo | String | 产品编号;来自 product-v2 批量查询,**取不到时为 null**(见降级响应) |
| route | String | 行程描述 |
| cities | List\<String\> | 城市列表 |
| departDate | LocalDate | 出发日期 |
| nights | Integer | 晚数 |
| totalAmount | String | 订单金额(后端计算,已序列化为字符串) |
| consultantId | String | 定制师 ID |
| consultantName | String | 定制师姓名 |
| consultantRemark | String | 定制师备注 |
| requirementNote | String | 需求备注 |
| dispatchRemark | String | 派车备注 |
| special | String | 特殊需求 |
| requirementVersion | Integer | 需求版本号 |
| requirementStatus | String | 对外状态(PENDING/PROCESSING/DONE/…) |
| houseStatus | String | **枚举码**:PENDING_CLAIM / CLAIMING / PENDING_FINALIZE / CONFIRMED / EXCEPTION;与旧 VO 的中文名不同 |
| houseStatusLabel | String | 中文显示名(「待配房」/「配房中」/「待确认」/「已确认」/「异常」) |
| claimerId | String | 当前认领人 ID;待配房行为 null |
| claimerName | String | 当前认领人名字;待配房行为 null |
| claimedAt | LocalDateTime | 认领时间;待配房行为 null |
| isMine | Boolean | 认领人是否当前用户 |
| canStartAllocation | Boolean | 前端「开始配房」按钮的**唯一依据**(true 时显示,false 时隐藏);条件:`status=PENDING` 且 `claimerId IS NULL` 且当前用户有写权限 |
| manualUrgent | Boolean | 手动加急标记 |
| manualUrgentAt | LocalDateTime | 加急时间 |
| urgencyLevel | String | 紧急度标签(LOW/MEDIUM/HIGH) |
| urgencyLabel | String | 紧急度中文名(「低」/「中」/「高」) |
| daysToDepart | Integer | 距出发还有几天(负数表示已出发) |
| isRework | Boolean | 是否返工 |
| reworkPrevClaimerName | String | 上次认领人名字 |
| exceptionCount | Integer | 已认领行:该户有几个未处理异常待办;待配房行为 0 |
| todoCount | Integer | 已认领行:该户有几个待办;待配房行为 0 |
| primaryAction | HousePrimaryActionVO | 只在 `isMine=true` 时返回;包含转单、释放等操作信息 |
| createTime | LocalDateTime | 需求创建时间 |
**stats 分桶计数(HouseAllocationHouseholdStatsVO)**:
| 字段 | 类型 | 说明 |
|------|------|------|
| pendingClaim | Long | 待配房行数;`scope=mine` 时恒为 0 |
| claiming | Long | house_status=CLAIMING 的行数 |
| pendingConfirm | Long | house_status=PENDING_FINALIZE 的行数 |
| confirmed | Long | house_status=CONFIRMED 的行数 |
| exception | Long | house_status=EXCEPTION 或订单有未处理异常待办的行数 |
| unfinished | Long | 待配房 + 配房中 + 待确认 + 异常 的行数;与 `status=unfinished` 的查询结果同源 |
| all | Long | 待配房 + 已认领 的总行数;与不传 status 的查询结果同源 |
#### 请求示例
```http
GET /v3/admin/order/house-allocation/households?scope=all&status=pendingClaim&productType=CORE&page=1&pageSize=20&sortBy=departDate,asc HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer ***
```
无请求体。
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"list": [
{
"id": "2097250563497385985",
"orderId": "770145",
"orderNo": "O20260926001",
"teamNo": null,
"guestName": "张三",
"personsDesc": "2成人1儿童",
"productType": "CORE",
"productName": "日本东京 6 日游",
"productNo": "P002301",
"route": "东京→富士山→京都",
"cities": ["东京", "富士山", "京都"],
"departDate": "2026-10-01",
"nights": 6,
"totalAmount": "8500.00",
"consultantId": "1001",
"consultantName": "李四",
"consultantRemark": "客户有特殊需求",
"requirementNote": "希望升级酒店",
"dispatchRemark": "已预留",
"special": "客户晕车,安排靠窗座位",
"requirementVersion": 1,
"requirementStatus": "PROCESSING",
"houseStatus": "PENDING_CLAIM",
"houseStatusLabel": "待配房",
"claimerId": null,
"claimerName": null,
"claimedAt": null,
"isMine": false,
"canStartAllocation": true,
"manualUrgent": true,
"manualUrgentAt": "2026-09-25T14:30:00",
"urgencyLevel": "HIGH",
"urgencyLabel": "高",
"daysToDepart": 5,
"isRework": false,
"reworkPrevClaimerName": null,
"exceptionCount": 0,
"todoCount": 0,
"primaryAction": null,
"createTime": "2026-09-20T09:00:00"
}
],
"total": 12,
"stats": {
"pendingClaim": 12,
"claiming": 5,
"pendingConfirm": 3,
"confirmed": 8,
"exception": 1,
"unfinished": 21,
"all": 28
}
}
}
```
#### 空数据 / 降级响应
- 分页无结果时,`list=[]`,`total=0`,`stats` 各字段为 0,`code=200` 不报错:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"list": [],
"total": 0,
"stats": { "pendingClaim": 0, "claiming": 0, "pendingConfirm": 0, "confirmed": 0, "exception": 0, "unfinished": 0, "all": 0 }
}
}
```
- **productNo 取不到时**(product-v2 服务降级或响应缺字段),该字段为 null,其余字段正常返回,不报错。
- **参数不合法时**(status、productType、sortBy 等与规则不符),HTTP 200,`code=400`(全局 BindException 处理),`message` 是具体字段的校验文案。下面是传 `productType=GROUP` 的实测原文:
```json
{"code":400,"message":"productType 只能是 CORE 或 CUSTOM","data":null,"traceId":null,"success":false}
```
#### 错误响应
- **未登录或无房务读权限**(808090):
```json
{
"code": 808090,
"message": "未登录或无该操作权限",
"success": false,
"data": null
}
```
#### 业务边界
- 查询条件 `claimedAtFrom/claimedAtTo` 同时传递时,等于隐含过滤 `claimerId IS NOT NULL`(只看已认领的行);与 `status=pendingClaim` 同传时查询结果为空、`code=200`、`stats.pendingClaim=0`,不报错。
- 手动加急 `manual_urgent=true` 的行在任何 `status` 页签里都会被提前排列(加急 DESC,加急时间 DESC),无论选何种 sortBy。
- **并发认领**:前端的「开始配房」按钮直接调 `POST /v3/admin/order/hotel-requirements/{id}/claim`,若二人同时点同一行,先到者返回 200,后到者返回 808001 并附加新文案「该需求已被其他房务认领或状态已变化,请刷新后重试」,前端刷新列表即可看到当前认领人。
- **异常数据(存量)**:列表里个别行的 `orderNo` 或 `productName` 可能为 null(对应主订单已软删除),前端需容忍空值、不做特殊提示,只显示已有字段。
- 转单、释放等操作只在 `isMine=true` 的行显示(`primaryAction` 非 null);他人认领的行为只读。
- `requirementStatus` 是订单对外状态,`houseStatus` 是该户配房阶段(两个维度独立)。
---
### 2. 房务配房列表·团期 `GET /v3/admin/order/house-allocation/group-batches`
**VO**: `HouseAllocationGroupPageReqVO` → `HouseAllocationGroupPageRespVO`
#### 使用场景
订单列表的**团期**页签,整团一行。返回团期订单(order_group_batch),按待配房/已认领分行展示。前端的通知链接 `?groupBatchId=<团ID>` 也以此端点定位落地。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| scope | Query | String | 否 | `all` \| `mine` | 默认 `all`;`mine` 时只返回当前用户认领的团 |
| status | Query | String | 否 | `pendingClaim` \| `claimed`,或空 | 空或不传表示两者都要 |
| groupBatchId | Query | Long | 否 | — | 精确定位一个团(通知链接用);传了则 1 条或 0 条结果 |
| keyword | Query | String | 否 | `@Size(max=32)` | 模糊匹配:团期号 / 产品名(OR) |
| productId | Query | Long | 否 | — | 产品 ID,精确匹配 |
| batchStatus | Query | String | 否 | `RECRUITING` \| `RESOURCE_PREPARING` \| `MATERIAL_PREPARING` \| `PENDING_DEPARTURE` \| `TRAVELLING` \| `TRIP_FINISHED` \| `REVIEWING` \| `SETTLED` \| `CANCELLED` | 团期阶段,精确匹配 |
| departDateFrom | Query | LocalDate | 否 | `yyyy-MM-dd` | 出发日期起 |
| departDateTo | Query | LocalDate | 否 | `yyyy-MM-dd` | 出发日期止 |
| claimedAtFrom | Query | LocalDateTime | 否 | `yyyy-MM-dd'T'HH:mm:ss` | 认领时间起 |
| claimedAtTo | Query | LocalDateTime | 否 | `yyyy-MM-dd'T'HH:mm:ss` | 认领时间止 |
| page | Query | Integer | 否 | `@Min(1)` | 默认 1 |
| pageSize | Query | Integer | 否 | `@Min(1) @Max(100)` | 默认 20 |
| sortBy | Query | String | 否 | `departDate,asc` \| `claimedAt,desc` \| `createTime,desc` | 默认 `departDate,asc` |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| list | List\<HouseAllocationGroupRespVO\> | 分页结果行 |
| total | Long | 满足条件的总行数 |
| stats | HouseAllocationGroupStatsVO | 三个页签的计数:pendingClaim / claimed / all |
**行字段详情(HouseAllocationGroupRespVO)**:
| 字段 | 类型 | 说明 |
|------|------|------|
| groupBatchId | String | 团期 ID,前端「开始配房」时用此值 |
| batchNo | String | 团期号 |
| batchName | String | 团期名称 |
| batchLabel | String | 团期标签 |
| productId | String | 产品 ID |
| productName | String | 产品名称 |
| batchStatus | String | 团期阶段码(RECRUITING/RESOURCE_PREPARING/…) |
| batchStatusLabel | String | 中文显示名(「招募中」/「资源准备中」/…) |
| departDate | LocalDate | 出发日期 |
| endDate | LocalDate | 结束日期 |
| enrollDeadline | LocalDate | 报名截止日期 |
| enrolledRooms | Integer | 已报名房间数 |
| enrolledPeople | Integer | 已报名人数 |
| activeOrderCount | Integer | 活跃订单数 |
| hotelOrderCount | Integer | 需要配房的订单数 |
| hotelReady | Integer | 已配房的订单数 |
| daysToDepart | Integer | 距出发还有几天 |
| urgencyLevel | String | 紧急度标签(LOW/MEDIUM/HIGH) |
| urgencyLabel | String | 紧急度中文名 |
| requirementConfirmed | Boolean | 团期需求是否已整体确认(仅信息展示,不影响认领) |
| houseClaimerId | String | 团级认领人 ID;待配房时为 null |
| houseClaimerName | String | 团级认领人名字;待配房时为 null |
| houseClaimedAt | LocalDateTime | 认领时间;待配房时为 null |
| isMine | Boolean | 认领人是否当前用户 |
| canStartAllocation | Boolean | 前端「开始配房」按钮的**唯一依据**;条件:`houseClaimerId IS NULL` 且当前用户有写权限 |
| createTime | LocalDateTime | 创建时间 |
**stats 分桶计数(HouseAllocationGroupStatsVO)**:
| 字段 | 类型 | 说明 |
|------|------|------|
| pendingClaim | Long | 待配房团数(`houseClaimerId IS NULL` 且 `requirementConfirmed=1` 且 `batchStatus=RESOURCE_PREPARING`) |
| claimed | Long | 已认领团数(`houseClaimerId IS NOT NULL`,任意阶段) |
| all | Long | pendingClaim + claimed 的总数 |
#### 请求示例
```http
GET /v3/admin/order/house-allocation/group-batches?scope=all&status=pendingClaim&page=1&pageSize=20 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer ***
```
无请求体。
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"list": [
{
"groupBatchId": "2097250563497385985",
"batchNo": "G20260926001",
"batchName": "日本 10 月团·东京-京都-大阪",
"batchLabel": "标准",
"productId": "5001",
"productName": "日本东京-京都-大阪 8 日游",
"batchStatus": "RESOURCE_PREPARING",
"batchStatusLabel": "资源准备中",
"departDate": "2026-10-05",
"endDate": "2026-10-12",
"enrollDeadline": "2026-09-28",
"enrolledRooms": 8,
"enrolledPeople": 16,
"activeOrderCount": 8,
"hotelOrderCount": 8,
"hotelReady": 0,
"daysToDepart": 9,
"urgencyLevel": "HIGH",
"urgencyLabel": "高",
"requirementConfirmed": false,
"houseClaimerId": null,
"houseClaimerName": null,
"houseClaimedAt": null,
"isMine": false,
"canStartAllocation": true,
"createTime": "2026-09-15T10:00:00"
}
],
"total": 5,
"stats": {
"pendingClaim": 5,
"claimed": 12,
"all": 17
}
}
}
```
#### 空数据 / 降级响应
- 分页无结果:`list=[]`,`total=0`,`stats` 各字段为 0,`code=200`:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"list": [],
"total": 0,
"stats": { "pendingClaim": 0, "claimed": 0, "all": 0 }
}
}
```
- 参数不合法时,HTTP 200,`code=400`,`message` 是具体字段的校验文案(结构同户级端点):
```json
{"code":400,"message":"<该字段的校验文案>","data":null,"traceId":null,"success":false}
```
#### 错误响应
- **未登录或无房务读权限**(808090):
```json
{
"code": 808090,
"message": "未登录或无该操作权限",
"success": false,
"data": null
}
```
#### 业务边界
- **整团一行**:团期阶段任意时刻只要被认领一次,就会在列表持续出现(`claimed` 页签或混合页签),不会因为进入后续阶段(待出发、出行中等)而消失。
- **并发认领**:前端的「开始配房」按钮直接调 `POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/claim`。并发场景下:
- 团期 claim 端点挂了 `@Idempotent`(5 秒窗口)与 `@Lock4j` 两重防护
- 同时提交会被幂等或锁层拦下,返回 **100502**(「整团认领处理中,请勿重复提交」,幂等窗口内),前端提示并刷新列表
- 窗口过后别人已抢到会返回 **808652**(「该团期已被其他房务认领」)
- **他人认领的团不提供看板入口**:前端对 `isMine=false` 的行不显示「查看看板」入口(若用户直接访问详情页,会得到 808612 或 808613 拒绝);详情页权限不变。
- 订单列表的通知参数 `?groupBatchId=<团ID>` 须在团期页签按此参数定位(可配合 filter 或直接查询定位)。
---
## 四、契约约束与正确调用方式
| 场景 | 调用方法 | 说明 |
|------|---------|------|
| ✅ 户级「开始配房」| `POST /v3/admin/order/hotel-requirements/{id}/claim` | 直接调现有写口,行为不变;`canStartAllocation=true` 时显示按钮 |
| ✅ 团期「开始配房」| `POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/claim` | 同上;路径保留 grab-pool,因为是现有写口 |
| ✅ 查看全员认领情况 | `GET /v3/admin/order/house-allocation/households?scope=all` 或 `/group-batches?scope=all` | **普通房务首次可见**;旧端点 `all-claims/` 仍返回 808092 |
| ✅ 刷新列表信号 | 监听 SSE 事件 `grab-pool-changed` | 现有 SSE 通道保留,不新增事件 |
| ❌ 调旧端点继续写代码 | `GET /v3/admin/order/grab-pool/hotel-requirements` | @Deprecated,禁用 |
| ❌ 团期已被他人认领后再点「开始配房」 | — | 返回 808652「该团期已被其他房务认领」,刷新列表即可看到认领人,不要重试 |
---
### 配套变更说明
#### 错误码文案更新
随 PR-1 部署,三个错误码文案改动(去掉对「抢单池」页面的引用):
| 码 | 旧文案 | 新文案 | 触发场景 |
|----|--------|--------|---------|
| **808650** | "团期订单须整团认领,不支持逐户认领。请到团期抢单池整团认领" | "团期订单须整团认领后配房,不支持逐户认领 / 转单" | 试图逐户认领 / 转单一个团期订单 |
| **808612** | "该团期尚未被房务认领,请先到团期抢单池认领" | "该团期尚未被房务整团认领" | 进入他人未认领的团期看板,或操作不满足条件的团期 |
| **808001** | "需求已被其他房务抢到" | "该需求已被其他房务认领或状态已变化,请刷新后重试" | 户级并发认领冲突,或状态变化后重复调用 |
#### 房务抢单池菜单下线
随 PR-2 部署(Flyway `V20260926_001`):
- 菜单 101(「抢单池·普通」,路径 `/housekeeper/grab-pool`)置为 INACTIVE
- 菜单 102(「抢单池·团期」,路径 `/housekeeper/grab-pool-group`)置为 INACTIVE
菜单对所有角色同步生效(按 ACTIVE 状态过滤)。缓存于 user-service 启动完成时自动刷新;若启动日志出现"应用启动后刷新菜单缓存失败",需手工 Redis DEL 对应 key(1 小时 TTL 后自动过期)。
#### 团期通知链接改向
随 PR-2 部署(SQL Flyway 更新通知配置),三条团期站内链接由抢单池改指订单列表:
| 通知事件 | 原链接 | 新链接 | 内容变化 |
|---------|--------|--------|---------|
| GROUP_BATCH_HOUSE_CLAIMED | `/housekeeper/grab-pool-group?groupBatchId=` | `/housekeeper/orders?groupBatchId=` | 链接改向;内容不再含「抢单池」词语 |
| GROUP_BATCH_HOUSE_RELEASED | `/housekeeper/grab-pool-group?groupBatchId=` | `/housekeeper/orders?groupBatchId=` | 同上 |
| GROUP_BATCH_REQUIREMENT_CONFIRMED | `/housekeeper/grab-pool-group?groupBatchId=` | `/housekeeper/orders?groupBatchId=` | 同上 |
#### 旧读端点标废弃
五个旧端点的 Controller 方法加 `@Deprecated` 注解;Swagger 操作描述末尾补充「(已废弃,改用 /v3/admin/order/house-allocation/…)」。**行为与字段完全不变**,现有消费方在迁移完毕前仍可继续调用,但新代码禁止接入。
迁移完毕后(todos 与作废页签移至新端点或别处)方开工单删除。
---
## 五、数据库行为
无表结构变更,无 H2 迁移脚本改动。user-service Flyway `V20260926_001` 更新系统菜单与通知配置(非业务表)。
---
## 六、边界行为
- 两个新端点都挂读门 `HouseReadPermission`,非房务角色(或未登录)返回 808090;普通房务、组长、超管均可调(不限 `scope=all`),这是与旧 `all-claims/` 最大的权限差异。
- 写口(认领、转单、释放)权限不变:写门只放行 ROOM_MANAGER 与 SUPER_ADMIN;房务组长(house_keeper_lead)调写口返回 808091,其他角色返回 808090。所以组长在两个新端点上所有行 `canStartAllocation=false`。
- SSE 广播现有四类事件 ADDED / CLAIMED / RELEASED / URGENT 保留,团期认领、释放、接管不补新事件(现状事实:旧池亦不发,属既知设计)。
- 他人认领的团期行只读:行操作(转单、释放等)只在 `isMine=true` 时展示;若直接访问团期看板详情,会得 808612/808613 拒绝。
- 参数校验失败(如 status 值非法)走全局 `BindException`,HTTP 200,`code=400`(非 400 HTTP 状态)。
---
## 七、不影响范围
- **认领、转单、释放、接管的写逻辑**:完全不变,只改入口
- **订单详情读权限**:不变
- **小程序端**(`/v3/mp/`):无改动
- **旧池的五个读端点在存量消费方(todos、作废页签)迁移完毕前**仍可继续调用
- **SSE 通道与事件**:保留现有四类,不新增
- **子订单状态机、配房流程**:不变
- **身份权限种子与角色**:不变(仅房务菜单下线,不涉及角色、权限位、权限码)
- **网关配置**:无改动;新路径在现有通配规则 `Path=/v3/admin/**` 下已生效
---
## 八、测试环境已验证
部署:`hl-gateway` / `hl-user-service` / `hl-product-service-v2` / `hl-order-service-v3` 四个服务均为 dev-v3 `12707c57c`(`deploy-status.sh` 四行 ok,2026-09-26 15:47~16:06 滚完)。以下读数都是经网关 `api.test.1814.love`、用真实账号鉴权的实测结果(2026-09-26)。
| # | 场景 | 实测结果 |
|---|---|---|
| 1 | 户级「开始配房」:两个普通房务并发认领同一行 | 一个 200,另一个 808001「已被认领或状态已变化」。随后另一人在列表里看到该行:`claimerName` = 认领人,`isMine=false`,`canStartAllocation=false` |
| 2 | 普通房务对他人已认领的户级行调认领 / 转交 / 释放 | 分别返回 808001 / 808010 / 808020,行状态不变 |
| 3 | 普通房务调户级列表 `scope=all` | code=200;旧端点 `GET /grab-pool/all-claims/hotel` 仍返回 808092(旧权限策略不变) |
| 4 | 团期「开始配房」:两个普通房务同时点击 | 5 秒幂等窗口内,后到者 100502;窗口外重试 808652。列表中该团 `houseClaimerName` = 先到者 |
| 5 | 房务主管(`house_keeper_lead`)调两个列表 | 均 code=200。全量翻页:户级 107 行、团期 109 行,`canStartAllocation` 全部为 false |
| 6 | 房务主管调「开始配房」 | 808091「房务组长为只读监督角色,无权执行该操作」 |
| 7 | 非房务角色调两个列表 | 808090「未登录或非房务角色,无权操作」 |
| 8 | 户级列表传 `productType=GROUP` | HTTP 200,`{"code":400,"message":"productType 只能是 CORE 或 CUSTOM"}` |
| 9 | 团期订单不进户级列表 | 全量翻页户级 107 行,与团期的需求 ID、订单 ID 交集均为 0;阳性对照(已知户级行)在列 |
| 10 | 加急行排序 | 把原在第 2 页第 9 位的样本标为加急后,它移到第 1 页第 1 位,第 2 页没有加急行(样本已还原) |
| 11 | `stats` 与列表同源 | 户级:不筛选 total=107,`productType=CORE` total=105,7 个状态分桶的 `stats` 与按该状态查询的 `total` 逐一相等。团期:不筛选 `stats` 为 45 / 64 / 109(pendingClaim / claimed / all),带关键字为 24 / 53 / 77,均与对应 `total` 相等 |
| 12 | `scope=mine` 与 `status=pendingClaim` 同传 | code=200,空页 |
| 13 | 整团认领后的站内信 | 6 条 CLAIMED 记录的链接为 `/housekeeper/orders?groupBatchId=<团期ID>`,正文不含「抢单池」;用该 `groupBatchId` 查团期列表,total=1 |
| 14 | 5 个旧端点 | 均 code=200,响应结构不变;Swagger 中 `deprecated=true` |
| 15 | 菜单 | 普通房务 ×2、房务主管、超管共 4 个账号,当前菜单树里都没有菜单 101 / 102 |
| 16 | 户级列表 `productNo` | 抽 3 行,与库中产品编号一致 |
| 17 | 关联订单已软删的存量需求行 | 该行 `orderNo` / `productName` / `productNo` 为 null,列表 code=200 照常返回 |
**本次未在测试服触发、只按代码契约给出的**:808612 / 808613(看他人认领的团期看板);`productNo` 取数失败时的降级(Feign fallback 置 null)。
---
## 十、相关文档
- **Issue**: [#8375](https://git.1814.love/wx/HL/issues/8375)
- **PR**:
- [#8381](https://git.1814.love/wx/HL/pulls/8381)(product-v2)
- [#8382](https://git.1814.love/wx/HL/pulls/8382)(order-v3)
- [#8379](https://git.1814.love/wx/HL/pulls/8379)(user-service)
- **涉及表结构**: 无
- **数据库变更**: user-service Flyway `V20260926_001`(菜单下线 + 通知链接改向)
## 关联 / 联系人
### 链接
- **Issue**: [#8375](https://git.1814.love/wx/HL/issues/8375)
- **合并提交**:
- product-v2: [#8381](https://git.1814.love/wx/HL/pulls/8381) @ commit d7a98f13f
- order-v3: [#8382](https://git.1814.love/wx/HL/pulls/8382) @ commit 72d8fe310
- user-service: [#8379](https://git.1814.love/wx/HL/pulls/8379) @ commit 12707c57c
### 联系人
- **后端负责人**: @wx
- **前端负责人(hl-ui)**: @mmg