changelog-filename-gate / validate (push) Failing after 1s
88caa74 房务接口审计批次:#8385 订房计划守卫/#8386 住宿驳回加门+删车务口/#8387 旧房间分配三口下线/#8388 回执认领校验+读门/#8389 家庭维度写口下线/#8390 16 读端点补门+菜单撤授。前端按钮后端字段驱动+错误码拦截器透 message+被删端点零调用/入口仅房务角色页,6 条均 not_required;#8387/#8389 后端已标,余 4 条翻 not_required,owner/ref 留空,status_note 引号内追充实证。
376 行
15 KiB
Markdown
376 行
15 KiB
Markdown
---
|
||
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: "not_required"
|
||
frontend_owner: ""
|
||
frontend_ref: ""
|
||
target_release: ""
|
||
verified_at: ""
|
||
status_note: "房务回执上传新增认领人校验(超管不豁免)与可选分片处理;列表新增房务读权限门(不受灰度开关控制,常开);错误码 808184 消息补五种原因的具体文案。测试环境网关已实测各分支,含缺分片 808184 新文案。前端实证(mmg 2026-09-27):uploadReceipt 走 http.upload 同一拦截器统一弹 message,808184 原因入 message 无需按 code 分支;回执入口仅房务角色页,零改动 not_required。"
|
||
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
|