10 KiB
10 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 6195 | 供应商资源关系查询、设置改绑与解绑 | admin | lc(GIT) | 新增接口 | deployed | verified | verified | mmg | 9e1aba14 | 2026-08-23 | 主 PR #6196 与补充 PR #6198/#6201 均已合并 dev-v3,最终链路提交 b71cc41c8 已完成 TEST,并包含在当前部署提交 041fb6d492 中。真实角色、Gateway、绑定/查询/解绑、车辆依赖失败关闭及首次响应版本可直接回用均已验证;两条无安全删除入口的 ACTIVE 测试供应商主档作为已记录残留保留,未执行直接 DML。 | 2026-08-23 | 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。
请求与响应
首次绑定或改绑
{
"supplierId": "1900000000000000001",
"requiredTypeCode": "SCENIC",
"remark": "主供应商",
"expectedCurrentSupplierId": "1900000000000000000",
"expectedRelationUpdateTime": "2026-08-23 11:20:30",
"changeReason": "资源供应商调整"
}
- 首次绑定不传两个
expected*字段。 - 已有关系时必须同时传
expectedCurrentSupplierId与expectedRelationUpdateTime;缺一返回参数错误,值不匹配返回395014。 - 同一供应商、同一要求类型和同一备注的重复请求按业务幂等成功,不更新时间,也不写空审计。
解绑
{
"expectedCurrentSupplierId": "1900000000000000001",
"expectedRelationUpdateTime": "2026-08-23 11:20:30",
"changeReason": "停止合作"
}
三个字段均必填。关系不存在或已解绑返回 395038;版本不一致返回 395014。
关系响应
{
"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-TESTACTIVE 供应商主档因状态机禁止 ACTIVE 直接删除、且当时没有安全暂停/归档入口而保留;未执行未授权 DML,残留已在三个关联工单记录。 - HL 仓库没有 Gitea workflow/status context;远端 PR 合并态、目标分支包含关系、提交和文件清单已复核。
前端接入提示
- 资源编辑页先调用 SUP-ADM-048 获取当前关系和
updateTime,再决定首次绑定或显式改绑。 - 改绑和解绑必须使用最近一次 GET/PUT 返回的供应商 ID 与
updateTime;收到395014后重新查询,不要覆盖服务端新版本。 - FINANCE 只展示关系,不显示或调用改绑、解绑动作;服务端仍会独立校验,按钮隐藏不能代替权限。
available=false时展示稳定原因,不应把关系静默当作不存在。395039表示必要依赖无法可靠校验,不应降级为成功或在本地伪造资源名称。
撤回
- 停止调用三个管理端接口,按 #6200 → #6197 → #6195 的逆序 revert 合并提交并重新构建。
- 部署顺序先回退 Resource 消费方,再在确认无消费者后回退 Fleet internal Provider;既有供应商、资源和车辆接口保持运行。
- 已产生的
supplier_resource_rel与supplier_change_log历史不做物理删除;需要恢复业务关系时,在代码回退前依据审计通过正常改绑/解绑补偿。 - 已应用的 User 权限 migration 不修改、不删除 Flyway 历史。两个权限可作为无消费者兼容数据保留;确需移除时新增更高版本 migration,先删除精确角色绑定,再删除
supplier:resource:view/manage。 - 资源锁与幂等键按 TTL 自然过期;紧急处理只清理精确 module/resourceId 和管理员权限缓存,禁止宽泛清 Redis。本链路不使用 MQ,无 MQ 恢复项。
- 回退后经 Gateway 验证新接口不可达,原供应商主档、资源查询及 Fleet options 契约不受影响。
关联 / 联系人
链接
- 主 Issue: #6195
- 主 PR: #6196
- Fleet 异常映射 Issue / PR: #6197 / #6198
- 版本时间 Issue / PR: #6200 / #6201
- 最终链路提交: b71cc41c8
联系人
- 后端负责人: @lc