185 行
10 KiB
Markdown
185 行
10 KiB
Markdown
---
|
||
schema: "hl-changelog/v2"
|
||
ticket: "6195"
|
||
title: "供应商资源关系查询、设置改绑与解绑"
|
||
consumer: "admin"
|
||
author: "lc(GIT)"
|
||
change_type: "新增接口"
|
||
backend_status: "deployed"
|
||
gateway_status: "verified"
|
||
frontend_status: "verified"
|
||
frontend_owner: "mmg"
|
||
frontend_ref: "9e1aba14"
|
||
target_release: ""
|
||
verified_at: "2026-08-23"
|
||
status_note: "主 PR #6196 与补充 PR #6198/#6201 均已合并 dev-v3,最终链路提交 b71cc41c8 已完成 TEST,并包含在当前部署提交 041fb6d492 中。真实角色、Gateway、绑定/查询/解绑、车辆依赖失败关闭及首次响应版本可直接回用均已验证;两条无安全删除入口的 ACTIVE 测试供应商主档作为已记录残留保留,未执行直接 DML。"
|
||
updated_at: "2026-08-23"
|
||
base: "dev-v3"
|
||
---
|
||
|
||
# 供应商资源关系:查询、设置改绑与解绑
|
||
|
||
本次新增资源页面维护供应商关系的三个管理端接口,覆盖读取当前关系、首次绑定或显式改绑、按并发版本解绑。关系统一存放在 Resource 服务,不向景区、酒店、车辆等资源主表写入 `supplier_id`,也不跨 schema 读写。
|
||
|
||
补充修复 #6197 和 #6200 已一并纳入本契约:Fleet 车辆摘要的所有远端运行时异常统一映射为 `395039`;首次绑定返回的 `updateTime` 与 MySQL 秒精度持久化值一致,可立即用于改绑或解绑。
|
||
|
||
## 变更接口
|
||
|
||
| 编号 | 方法 | 路径 | 权限 | 返回 |
|
||
|------|------|------|------|------|
|
||
| SUP-ADM-048 | GET | `/admin/supplier/resource-relations/{resourceModule}/{resourceId}/view` | `supplier:resource:view` + 资源查看范围 | 当前有效关系 |
|
||
| SUP-ADM-049 | PUT | `/admin/supplier/resource-relations/{resourceModule}/{resourceId}/update` | `supplier:resource:manage` + 资源编辑范围 | 新建或改绑后的关系 |
|
||
| SUP-ADM-050 | POST | `/admin/supplier/resource-relations/{resourceModule}/{resourceId}/unbind` | `supplier:resource:manage` + 资源编辑范围 | `null` |
|
||
|
||
三个路径已经被现有 Gateway `/admin/supplier/**` 路由覆盖,均要求真实管理员 Token。客户端传入的 `X-Admin-Id`、`X-Admin-Role` 不会替代可信身份。
|
||
|
||
## 角色与权限
|
||
|
||
| 角色 | `supplier:resource:view` | `supplier:resource:manage` | 默认能力 |
|
||
|------|:------------------------:|:--------------------------:|----------|
|
||
| ADMIN | ✓ | ✓ | 可查看;仅可维护本人创建、且自身有编辑范围的资源 |
|
||
| FINANCE | ✓ | — | 只读关系 |
|
||
| SUPER_ADMIN | ✓ | ✓ | 可查看和维护 |
|
||
| 其他角色 | — | — | 拒绝 |
|
||
|
||
这两项权限与供应商主档权限双向隔离:`supplier:update` 不能单独维护资源关系,`supplier:resource:manage` 也不会授予供应商主档编辑能力。
|
||
|
||
## 支持的资源模块
|
||
|
||
| `resourceModule` | 展示模块 | `requiredTypeCode` 规则 |
|
||
|------------------|----------|--------------------------|
|
||
| `SCENIC` | 景区管理 | 固定 `SCENIC` |
|
||
| `RESTAURANT` | 餐厅管理 | 固定 `RESTAURANT` |
|
||
| `SUPPLIES` | 备品管理 | 默认 `SUPPLIES`,允许显式类型 |
|
||
| `SUPPLIES_COMBO` | 组合配品 | 固定 `SUPPLIES` |
|
||
| `ACTIVITY` | 游玩项目管理 | 必须显式传入 |
|
||
| `HOTEL` | 酒店管理 | 固定 `HOTEL` |
|
||
| `SERVICE` | 服务管理 | 默认 `SERVICE`,允许显式类型 |
|
||
| `COST_ITEM` | 额外成本 | 必须显式传入 |
|
||
| `STAFF` | 服务人员管理 | 必须显式传入 |
|
||
| `VEHICLE` | 车队管理-车队管理 | 固定 `FLEET` |
|
||
|
||
未知模块返回 `395034`。资源不存在或已删除返回 `395035`。VEHICLE 通过 Internal Token 保护的 Fleet 精确摘要接口读取;超时、fallback、非成功、空响应以及 Feign 抛出的任意运行时异常均失败关闭为 `395039`。
|
||
|
||
## 请求与响应
|
||
|
||
### 首次绑定或改绑
|
||
|
||
```json
|
||
{
|
||
"supplierId": "1900000000000000001",
|
||
"requiredTypeCode": "SCENIC",
|
||
"remark": "主供应商",
|
||
"expectedCurrentSupplierId": "1900000000000000000",
|
||
"expectedRelationUpdateTime": "2026-08-23 11:20:30",
|
||
"changeReason": "资源供应商调整"
|
||
}
|
||
```
|
||
|
||
- 首次绑定不传两个 `expected*` 字段。
|
||
- 已有关系时必须同时传 `expectedCurrentSupplierId` 与 `expectedRelationUpdateTime`;缺一返回参数错误,值不匹配返回 `395014`。
|
||
- 同一供应商、同一要求类型和同一备注的重复请求按业务幂等成功,不更新时间,也不写空审计。
|
||
|
||
### 解绑
|
||
|
||
```json
|
||
{
|
||
"expectedCurrentSupplierId": "1900000000000000001",
|
||
"expectedRelationUpdateTime": "2026-08-23 11:20:30",
|
||
"changeReason": "停止合作"
|
||
}
|
||
```
|
||
|
||
三个字段均必填。关系不存在或已解绑返回 `395038`;版本不一致返回 `395014`。
|
||
|
||
### 关系响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": {
|
||
"relationId": "1900000000000000100",
|
||
"supplierId": "1900000000000000001",
|
||
"supplierNo": "SUP1900000000000000001",
|
||
"supplierName": "示例供应商",
|
||
"resourceModule": "SCENIC",
|
||
"moduleName": "景区管理",
|
||
"resourceId": "10001",
|
||
"resourceName": "示例景区",
|
||
"requiredTypeCode": "SCENIC",
|
||
"requiredTypeName": "景区供应商",
|
||
"remark": "主供应商",
|
||
"available": true,
|
||
"unavailableReasons": [],
|
||
"createTime": "2026-08-23 11:20:30",
|
||
"updateTime": "2026-08-23 11:20:30"
|
||
},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
所有 Snowflake `Long` 输出为 JSON String;时间格式为 `yyyy-MM-dd HH:mm:ss`。响应不包含账户、资质附件、密文或跨服务内部字段。`updateTime` 是并发版本,客户端必须原样回传。
|
||
|
||
## 可用性与写入门禁
|
||
|
||
- 写入目标供应商必须为 `ACTIVE`,要求类型必须启用且属于该供应商,全部必备资质必须齐全且未过期。
|
||
- 查询不会因为后续资格变化删除历史关系,而是通过 `available=false` 和稳定原因列表返回:`SUPPLIER_NOT_ACTIVE`、`REQUIRED_TYPE_INACTIVE`、`SUPPLIER_TYPE_MISSING`、`REQUIRED_QUALIFICATION_MISSING`、`REQUIRED_QUALIFICATION_EXPIRED`。
|
||
- 写接口使用请求摘要幂等、资源粒度分布式锁、数据库行锁、预期版本围栏和唯一索引兜底。
|
||
- 绑定、改绑、解绑与 `supplier_change_log` 同事务;审计失败整笔回滚。改绑保留旧关系软删除历史,并分别为原、新供应商记录安全审计快照。
|
||
- 外部依赖调用在数据库事务外完成;依赖失败、越权、参数错误、版本冲突和资格门禁失败均不产生关系或变更审计写入。
|
||
|
||
## 主要错误码
|
||
|
||
| 业务码 | 含义 |
|
||
|-------:|------|
|
||
| `395014` | 关系并发版本已变化 |
|
||
| `395034` | 不支持的资源模块 |
|
||
| `395035` | 资源不存在或已删除 |
|
||
| `395036` | 资源已关联其他供应商,应显式改绑 |
|
||
| `395037` | 供应商类型不满足资源要求 |
|
||
| `395038` | 当前供应商资源关系不存在 |
|
||
| `395039` | 字典、权限、资源摘要或 Fleet 等必要依赖暂不可用 |
|
||
|
||
统一包络可能以 HTTP 200 承载业务失败,调用方必须检查 `code` 与 `success`。
|
||
|
||
## 验证证据
|
||
|
||
- 合并链:主 PR #6196(`9f83cf9c7`)、Fleet 异常映射补充 PR #6198(`81663f7b6`)、版本时间补充 PR #6201(`b71cc41c8`)。
|
||
- 自动化:Supplier 聚焦测试、Fleet 精确摘要契约、User 权限 migration/权限服务测试、Gateway 路由审计与构建均通过;补充修复分别覆盖 Feign `BusinessException` 映射和首次响应秒精度版本。
|
||
- 真实 TEST 经 Gateway 覆盖权限矩阵、有效资源关系首次绑定/查询/改绑/解绑、同值幂等、版本冲突、资格门禁、VEHICLE 精确摘要成功链及依赖失败 `395039`。
|
||
- #6200 复验确认首次 PUT 返回的 `updateTime` 与立即 GET 一致,并可直接用于解绑;关系和审计副作用按正常接口清理。
|
||
- 临时资源关系、临时角色、临时管理员和对应 Token/权限缓存已清理。两条 `HL6195-TEST` ACTIVE 供应商主档因状态机禁止 ACTIVE 直接删除、且当时没有安全暂停/归档入口而保留;未执行未授权 DML,残留已在三个关联工单记录。
|
||
- HL 仓库没有 Gitea workflow/status context;远端 PR 合并态、目标分支包含关系、提交和文件清单已复核。
|
||
|
||
## 前端接入提示
|
||
|
||
- 资源编辑页先调用 SUP-ADM-048 获取当前关系和 `updateTime`,再决定首次绑定或显式改绑。
|
||
- 改绑和解绑必须使用最近一次 GET/PUT 返回的供应商 ID 与 `updateTime`;收到 `395014` 后重新查询,不要覆盖服务端新版本。
|
||
- FINANCE 只展示关系,不显示或调用改绑、解绑动作;服务端仍会独立校验,按钮隐藏不能代替权限。
|
||
- `available=false` 时展示稳定原因,不应把关系静默当作不存在。
|
||
- `395039` 表示必要依赖无法可靠校验,不应降级为成功或在本地伪造资源名称。
|
||
|
||
## 撤回
|
||
|
||
1. 停止调用三个管理端接口,按 #6200 → #6197 → #6195 的逆序 revert 合并提交并重新构建。
|
||
2. 部署顺序先回退 Resource 消费方,再在确认无消费者后回退 Fleet internal Provider;既有供应商、资源和车辆接口保持运行。
|
||
3. 已产生的 `supplier_resource_rel` 与 `supplier_change_log` 历史不做物理删除;需要恢复业务关系时,在代码回退前依据审计通过正常改绑/解绑补偿。
|
||
4. 已应用的 User 权限 migration 不修改、不删除 Flyway 历史。两个权限可作为无消费者兼容数据保留;确需移除时新增更高版本 migration,先删除精确角色绑定,再删除 `supplier:resource:view/manage`。
|
||
5. 资源锁与幂等键按 TTL 自然过期;紧急处理只清理精确 module/resourceId 和管理员权限缓存,禁止宽泛清 Redis。本链路不使用 MQ,无 MQ 恢复项。
|
||
6. 回退后经 Gateway 验证新接口不可达,原供应商主档、资源查询及 Fleet options 契约不受影响。
|
||
|
||
## 关联 / 联系人
|
||
|
||
### 链接
|
||
|
||
- **主 Issue**: [#6195](https://git.1814.love:8443/wx/HL/issues/6195)
|
||
- **主 PR**: [#6196](https://git.1814.love:8443/wx/HL/pulls/6196)
|
||
- **Fleet 异常映射 Issue / PR**: [#6197](https://git.1814.love:8443/wx/HL/issues/6197) / [#6198](https://git.1814.love:8443/wx/HL/pulls/6198)
|
||
- **版本时间 Issue / PR**: [#6200](https://git.1814.love:8443/wx/HL/issues/6200) / [#6201](https://git.1814.love:8443/wx/HL/pulls/6201)
|
||
- **最终链路提交**: [b71cc41c8](https://git.1814.love:8443/wx/HL/commit/b71cc41c843828c31d6980a58ae6d82a4d1d9f09)
|
||
|
||
### 联系人
|
||
|
||
- **后端负责人**: @lc
|