refactor: 资源服务 ResourceReferenceCheckHelper 抽取 16 处 fail-closed 模板代码 (PR #1841,前端无感)

PR #1838 follow-up,11 service 16 处调用点 8-10 行模板 → 1 行 helper 调用,
净减 178 行;API 行为完全一致,前端无需改动。
测试服 round-trip 实测 ScenicSpot batchDelete 反例 deniedReasons 含 13 个中文产品名 
这个提交包含在:
API Changelog Bot 2026-05-08 00:48:38 +08:00
父节点 eb91ded1ac
当前提交 b7e9e754b8

查看文件

@ -0,0 +1,59 @@
---
date: 2026-05-08
type: backend-refactor
module: hl-resource-service
priority: low
notify: ["@mmg"]
status: tested-passed
restart_service: hl-resource-service
gitea_pr: 1841
gitea_issue: []
---
# 资源服务 ResourceReferenceCheckHelper 抽取(纯重构,前端无感)
## 背景
PR #1838 引入 fail-closed 后,resource service 11 个文件 16 处调用点重复 8-10 行 `productFeignClient.checkResourceInUse + isSuccess + throw + getData + 被引用 throw` 模板。
PR #1841 抽取到 `ResourceReferenceCheckHelper`,2 个公开方法:
- `throwIfInUse(type, id, errorCode)` — 单删除/下架场景
- `getReferencingProducts(type, id)` — batchDelete 累积场景
每处调用点 8-10 行 → 1 行 helper 调用,**净减 178 行**(35 文件 +177/-355,加 helper+test +271 = 净增 87)。
## 前端可见变化
**完全无变化** — API 行为/响应/业务码全部保持一致。前端不需要任何改动。
测试服 round-trip 实测 (PR #1838 同样反例):
```
DELETE /admin/scenic/spots/batch ids=[3001000000000000016]
HTTP=200 code=200
data={successCount:0, deniedIds:["3001000000000000016"],
deniedReasons:["被产品使用: 结婚不回家、带孩子, 看一次真正的草原。、...(13 个产品名)"]}
post-check GET 景区仍存在 ✅
```
## 重启服务
测试服已部署:`hl-resource-service`(PR #1841 合 dev → deploy panel)。
## 改造范围
11 个 service 文件 16 处调用点全部统一改用 helper:
- ScenicSpotService 4 处(updateStatus / batchUpdateStatus / batchDelete / submitApproval)
- StaffService 2 处 / SuppliesService 2 处
- HotelService / RoomTypeService / RestaurantService / ActivityService / ServiceItemService / SuppliesComboService / VehicleModelService / CostItemService 各 1 处
22 个既有 service test 同步切 mock(`ProductFeignClient``ResourceReferenceCheckHelper`)。
## 关联
- PR #1838 / #1839 / Issue #1821(纯重构 follow-up,无新工单)
## Follow-up(本 PR 不修)
- 余下 PR #1838 follow-up 项: RoomType 禁用按 HOTEL 维度校验语义偏严 / `product_itinerary_node(resource_type, resource_id)` 复合索引 / batchDelete TOCTOU 时序问题
- 单测加 H2 集成测试或 @MybatisPlusTest 真跑 SQL 防止 mock 单测假阳(memory `experience/qa/mock-tests-pass-cr-pass-but-prod-schema-mismatch-real-deletes-2-resources.md`)