文件
hl-api-changelog/changelogs-v2/2026-09/26_8388_房务最终确认回执上传加认领校验列表加读门-修改接口-管理后台.md
T
API Changelog Bot和Claude Opus 5.5 88caa74d34
changelog-filename-gate / validate (push) Failing after 3s
docs(changelog): 房务接口审计批次 #8385~#8390 交接件
- #8385 团期订房计划建守卫(未认领团 808612)与 808660 释放文案
- #8386 住宿 supplier-reject 加角色门与认领校验,车务 supplier-reject 下线
- #8387 下线订单侧房间分配三口 /v3/admin/order/{id}/room
- #8388 最终确认回执上传加认领校验、列表加读门、808184 带具体原因
- #8389 下线 POST /v3/admin/order/assignments/{assignmentId}/rooms
- #8390 房务 16 个只读端点加角色读门,ADMIN 房务菜单撤授

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 23:52:47 +08:00

376 行
15 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
schema: "hl-changelog/v2"
ticket: "8388"
title: "回执接口守卫:上传加认领人校验(808110/808186,超管不豁免)、列表加房务读门(808090)、缺分片改 808184"
consumer: "admin"
author: "wx(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: "房务回执上传新增认领人校验(超管不豁免)与可选分片处理;列表新增房务读权限门(不受灰度开关控制,常开);错误码 808184 消息补五种原因的具体文案。测试环境网关已实测各分支,含缺分片 808184 新文案。"
updated_at: "2026-09-26"
base: "dev-v3"
---
# order-v3:房务最终确认回执接口守卫加固(管理后台)
**服务**: hl-order-service-v3
**PR**: #8394(原始改动) + #8399(808184 补 {0} 占位追加)
**Issue**: #8388
---
## ⚠️ 关键变化
1. **上传回执** `POST /admin/house/assignments/requirements/{requirementId}/receipts`:
- 新增认领人校验:需求必须被当前操作人认领,否则返回 **808110**(非本人);**SUPER_ADMIN 在本端点不豁免**,与 #8386 驳回需求口径相反。
- 需求处于 `PENDING_CLAIM`(未认领)时先被状态闸口拦下,返回 **808186**;错误码 808116 仅作理论防御(正常调用路径不可达)。
- 缺 file 分片 / 0 字节 file 分片改返 **808184**「回执上传失败:上传文件为空或缺少 file 分片」(改前返 HTTP 500),共 5 种原因文案。
- 顺序:角色门(808090/808091)→ 需求存在(808100)→ 状态闸口(808186)→ 认领人校验(808110)→ 文件校验(808184)。
2. **回执列表** `GET /admin/house/assignments/requirements/{requirementId}/receipts`:
- 新增房务读权限门:非房务角色(定制师、车控等)返回 **808090**。房务全角色(含组长)可见;超管可见。
- 无认领限制(组长可看全部、他人接手前查历史回执)。
---
## 一、背景
回执两个端点(上传、列表)无任何权限校验,导致:
1. 非认领人房务可为他人上传回执,破坏追责链。
2. 定制师、车控等非房务角色可读回执列表含 OSS 直链与酒店联系信息。
同一配房模块的 10 个写操作都带认领人校验;回执缺失属遗漏。缺 file 分片时返回 500 属参数处理缺陷(hl-common-log 共性坑,本单仅止血)。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 上传最终确认回执 | POST | `/admin/house/assignments/requirements/{requirementId}/receipts` | 修改接口 | 加认领人校验、缺分片改 808184 |
| 2 | 回执列表 | GET | `/admin/house/assignments/requirements/{requirementId}/receipts` | 修改接口 | 加房务读权限门(808090) |
---
## 三、接口详情
### 1. 上传最终确认回执 `POST /admin/house/assignments/requirements/{requirementId}/receipts`
**VO**: `multipart/form-data: file → Result<HouseFinalizeReceiptRespVO>`
#### 使用场景
房务在配房面板「最终确认」区上传回执照片/PDF,证明已与酒店确认房间。仅**该需求的认领人本人**可上传;非房务角色、房务组长、非认领人(含 SUPER_ADMIN)均返回权限错误——超管在本端点**不豁免**认领归属校验,这一点与 #8386(驳回需求超管豁免)口径不同。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| requirementId | Path | Long | 是 | — | 酒店需求 ID |
| file | Form | MultipartFile | 是(业务校验) | ≤50MB,类型自动识别 | 回执文件;缺失返 808184 |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| receiptId | String | 回执 ID(雪花 ID,序列化为字符串) |
| requirementId | String | 需求 ID(雪花 ID,序列化为字符串) |
| orderId | String | 订单 ID(雪花 ID,序列化为字符串) |
| fileName | String | 原始文件名 |
| ossUrl | String | OSS 公网 URL,`domain + "/" + ossKey` 拼接,不签名 |
| ossKey | String | OSS Object Key |
| fileSize | Long | 文件大小(字节) |
| fileType | String | 附件类型:`PDF` / `IMAGE` / `OTHER`(按扩展名识别) |
| createTime | LocalDateTime | 上传时间,序列化格式 `yyyy-MM-dd HH:mm:ss` |
#### 请求示例
```http
POST /admin/house/assignments/requirements/2103126783048245250/receipts HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
Content-Type: multipart/form-data; boundary=----Boundary
------Boundary
Content-Disposition: form-data; name="file"; filename="receipt.jpg"
Content-Type: image/jpeg
[binary jpeg data]
------Boundary--
```
#### 响应示例
**成功**:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"receiptId": "2103801203844689921",
"requirementId": "2103126783048245250",
"orderId": "2100542287908892650",
"fileName": "receipt.jpg",
"ossUrl": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/house/finalize-receipt/...",
"ossKey": "house/finalize-receipt/2026-09/2103126783048245250_receipt.jpg",
"fileSize": 512000,
"fileType": "IMAGE",
"createTime": "2026-09-26 14:30:00"
}
}
```
**失败(非认领人)**:
```json
{
"code": 808110,
"message": "需求不属于当前用户",
"success": false,
"data": null
}
```
**失败(缺分片,实测)**:
```json
{
"code": 808184,
"message": "回执上传失败:上传文件为空或缺少 file 分片",
"success": false,
"data": null
}
```
#### 空数据 / 降级响应
- 文件为空或缺 `file` 分片:`code=808184`,message「回执上传失败:上传文件为空或缺少 file 分片」(改前返 HTTP 500)。
#### 错误响应
```json
{
"code": 808110,
"message": "需求不属于当前用户",
"success": false,
"data": null
}
```
**其他错误码**:
- **808090**:未登录或非房务角色,无权操作。
- **808091**:房务组长为只读监督角色,无权执行该操作。
- **808100**:需求不存在。
- **808186**:当前状态不允许上传回执(仅 CLAIMING/PENDING_FINALIZE 可传;PENDING_CLAIM 等其他状态走这个码,不是 808116)。
- **808116**:订单未抢单, 请先抢单再配房(状态闸口之后理论不可达,作纵深防护保留)。
- **808184**:回执上传失败:{0},`{0}` 按触发原因取以下固定文案之一——「上传文件为空或缺少 file 分片」/「文件大小超过 50MB」/「OSS 未配置」/「读取上传文件失败」/「文件存储服务暂不可用,请稍后重试」;异常原文只进服务端日志,不回显给客户端。
#### 业务边界
- **认领校验**:需求 `claimer_id` 必须等于当前操作人;**超管不豁免**,对他人认领的需求上传同样返回 808110。与 #8386(驳回需求,超管豁免)口径不同,前端不要套同一套按钮逻辑。
- **校验顺序**:写门(808090/808091)→ 需求存在(808100)→ 状态闸口(808186)→ 认领归属(808116/808110)→ 文件校验(808184)。
- **文件分片**:`file` 分片改为可选(`required=false`),缺失走 Service 内已有的空文件分支报业务码 808184(改前是 HTML form 漏填触发 `MissingServletRequestPartException`,落到兜底 `Exception` 处理器返 HTTP 200+code 500「系统繁忙」并打 ERROR 日志)。
- **状态限制**:仅 CLAIMING(配房中)与 PENDING_FINALIZE(待确认)两个阶段可上传;PENDING_CLAIM(未抢单)等其他状态返 808186,不是 808116。
- **无事务**:`upload()` 不加 `@Transactional`;OSS `putObject` 成功后才 `insert`,`insert` 失败可能残留孤儿 OSS 对象(由清理 job 兜底);OSS 失败(`putObject` 抛异常)返 808184 且不落库。
---
### 2. 回执列表 `GET /admin/house/assignments/requirements/{requirementId}/receipts`
**VO**: `→ Result<List<HouseFinalizeReceiptRespVO>>`
#### 使用场景
房务或超管查看某需求的全部回执列表(谁上传的、何时、文件链接)。非房务角色返回 808090;房务全角色可见无限制。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| requirementId | Path | Long | 是 | — | 酒店需求 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| receiptId | String | 回执 ID(雪花 ID,序列化为字符串) |
| requirementId | String | 需求 ID(雪花 ID,序列化为字符串) |
| orderId | String | 订单 ID(雪花 ID,序列化为字符串) |
| fileName | String | 原始文件名 |
| ossUrl | String | OSS 公网 URL,不签名 |
| ossKey | String | OSS Object Key |
| fileSize | Long | 文件大小(字节) |
| fileType | String | 附件类型:`PDF` / `IMAGE` / `OTHER` |
| createTime | LocalDateTime | 上传时间,序列化格式 `yyyy-MM-dd HH:mm:ss` |
#### 请求示例
```http
GET /admin/house/assignments/requirements/2103126783048245250/receipts HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
```
#### 响应示例
**成功(房务)**:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": [
{
"receiptId": "2103801203844689921",
"requirementId": "2103126783048245250",
"orderId": "2100542287908892650",
"fileName": "receipt_1.jpg",
"ossUrl": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/house/finalize-receipt/...",
"ossKey": "house/finalize-receipt/2026-09/2103126783048245250_receipt_1.jpg",
"fileSize": 512000,
"fileType": "IMAGE",
"createTime": "2026-09-26 14:30:00"
}
]
}
```
**失败(定制师)**:
```json
{
"code": 808090,
"message": "未登录或非房务角色,无权操作",
"success": false,
"data": null
}
```
#### 空数据 / 降级响应
- 无回执:`code=200`,`data=[]`。
#### 错误响应
```json
{
"code": 808090,
"message": "未登录或非房务角色,无权操作",
"success": false,
"data": null
}
```
**说明**:
- **808090**(新增):非房务角色返回;该读门调 `HouseReadGuard.assertHouseReadPermission()`,**不受** #8390 引入的灰度开关 `group-batch.acl.enforce.house-read-role` 控制,是常开的角色门。
#### 业务边界
- **角色放行**:ROOM_MANAGER / house_keeper_lead / SUPER_ADMIN 均可读;零角色(#7609 G-2 保留项)亦放行。
- **无认领限制**:组长可看全部需求回执(监督权),他人接手后也可查历史。
- **直链有效期**:测试桶直链公共可读(无签名);生产桶读策略未定,前端不得缓存直链。
---
## 四、契约约束与正确调用方式
| 场景 | 调用方法 | 说明 |
|------|---------|------|
| ✅ 认领人上传 | `POST /admin/house/assignments/requirements/{id}/receipts`,`multipart: file=@...` | 该需求的当前认领人上传,返回 200 |
| ✅ 房务查列表 | `GET /admin/house/assignments/requirements/{id}/receipts` | 返回 200 + 全部回执 |
| ✅ 组长查列表 | 同上(house_keeper_lead 身份)| 无限制,200 返回 |
| ❌ 非认领人上传(含 SUPER_ADMIN) | — | 返回 808110,不落库;超管在本端点**不豁免**认领归属校验 |
| ❌ 未认领需求(PENDING_CLAIM)上传 | — | 返回 808186(先过状态闸口) |
| ❌ 定制师查列表 | — | 返回 808090 |
| ❌ 缺 file 分片 | 空 multipart 或无 file 字段 | 返回 808184 |
---
## 五、数据库行为
无变更、无 Flyway。`house_finalize_receipt` 表与逻辑保留;认领人校验在内存判断,无新增存储。
---
## 六、边界行为
| 场景 | 行为 |
|---|---|
| 认领人上传 | 返回 200,回执入库入 OSS |
| 非认领人上传(含 SUPER_ADMIN) | 返回 808110,无新行 |
| 转单后原人上传 | 返回 808110(新人成认领人,原人失去权限) |
| PENDING_CLAIM 需求上传 | 返回 808186(状态闸口先于认领校验) |
| 组长列表查询 | 返回 200,全列表 |
| 定制师列表查询 | 返回 808090,无数据 |
| 缺 file 分片 / 0 字节 file 分片 | 返回 808184(HTTP 200、code 业务码,5 种原因文案) |
## 六.6、修改前后对比
| 维度 | 改前 | 改后 |
|------|------|------|
| 非认领人上传(含超管) | 返回 200,入库 | 返回 808110 |
| 缺 file 分片 | HTTP 500、ERROR 日志 | HTTP 200、code 808184,仅 WARN 日志 |
| 定制师查列表 | 返回 200 + 列表 | 返回 808090 |
## 六.7、影响评估
- **兼容性**:hl-ui 回执上传与列表操作收新错误码(808090/808091/808110/808186/808184);需处理新文案提示。
- **前端要动的**:
- 上传失败按错误码区分:「权限不足」(808090/808091)、「非本人(含超管)」(808110)、「需求未认领」(808186)、「文件」(808184,5 种原因文案,需在 `message` 里判断具体原因而非只判 code)。
- 定制师等非房务角色不再能查回执列表,改改导航或隐藏入口。
- **数据影响**:无;仅加校验,不改历史回执。
- **其它服务**:hl-mp-service、fleet 无直接调用。
---
## 七、不影响范围
- 房务订单详情页回执展示逻辑(虽然定制师不再能查全列表,已登录进详情页的现有回执链接仍有效)。
- 回执删除接口(无,但实体带 `@TableLogic` 支持软删)。
- 其他配房写操作(转单、释放、分房等),无改动。
---
## 八、测试环境已验证
部署:测试环境 order-v3 `1f65d7894`(含 e2313790b)。网关 `api.test.1814.love:9443`。
| # | 场景 | 实测结果 |
|---|---|---|
| 1 | 认领人上传 | code 200,新行 uploader_id = 认领人;已软删清理 |
| 2 | 非认领人上传 | code 808110,无新行 |
| 3 | PENDING_CLAIM 需求上传 | code 808186 |
| 4 | SUPER_ADMIN 对他人认领的需求上传 | code 808110(不豁免) |
| 5 | 缺 file 分片(multipart 只带无关字段)×2 | HTTP 200 + `{"code":808184,"message":"回执上传失败:上传文件为空或缺少 file 分片"}` |
| 6 | 0 字节 file 分片 | 同 #5,同码同文案 |
| 7 | 房务(认领人本人)查列表 | code 200,全部回执 |
| 8 | 定制师 / VEHICLE_MANAGER 查列表 | code 808090,无数据 |
| 9 | house_keeper_lead / 非认领 ROOM_MANAGER / SUPER_ADMIN 查列表 | code 200,全部回执 |
#5、#6 场景 order-v3 日志对应 3 条 WARN `Business error [order.808184]`,无 `Unexpected error` ERROR 行。
---
## 十、相关文档
- Issue:https://git.1814.love/wx/HL/issues/8388
- PR:https://git.1814.love/wx/HL/pulls/8394(原始改动)、https://git.1814.love/wx/HL/pulls/8399(808184 补 `{0}` 占位追加)
- 相关单号:#8386(认领人校验,超管在该单**有**豁免,口径与本单相反)、#8390(读权限门同配方)
---
## 关联 / 联系人
### 联系人
- **后端负责人**: @wx