From e02541943d9cb5455d7db10597660b004ada8e3e Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Sat, 26 Sep 2026 17:13:39 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=2026=5F8375=20=E6=88=BF?= =?UTF-8?q?=E5=8A=A1=E9=85=8D=E6=88=BF=E5=88=97=E8=A1=A8=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E6=8A=A2=E5=8D=95=E6=B1=A0=EF=BC=88=E6=88=B7=E7=BA=A7=20/=20?= =?UTF-8?q?=E5=9B=A2=E6=9C=9F=E4=B8=A4=E5=BC=A0=E5=88=97=E8=A1=A8=EF=BC=89?= =?UTF-8?q?=E4=BA=A4=E6=8E=A5=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 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) --- ...�ˆ并抢单池户级与团期两张列表-新增接口-管理后台.md | 618 ++++++++++++++++++ 1 file changed, 618 insertions(+) create mode 100644 changelogs-v2/2026-09/26_8375_房务配房列表合并抢单池户级与团期两张列表-新增接口-管理后台.md diff --git a/changelogs-v2/2026-09/26_8375_房务配房列表合并抢单池户级与团期两张列表-新增接口-管理后台.md b/changelogs-v2/2026-09/26_8375_房务配房列表合并抢单池户级与团期两张列表-新增接口-管理后台.md new file mode 100644 index 00000000..7f03d22c --- /dev/null +++ b/changelogs-v2/2026-09/26_8375_房务配房列表合并抢单池户级与团期两张列表-新增接口-管理后台.md @@ -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\ | 分页结果行 | +| 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\ | 城市列表 | +| 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\ | 分页结果行 | +| 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