diff --git a/changelogs/2026-05/16_feat_product_v2_price_edit_permission_lock.md b/changelogs/2026-05/16_feat_product_v2_price_edit_permission_lock.md new file mode 100644 index 0000000..2a87524 --- /dev/null +++ b/changelogs/2026-05/16_feat_product_v2_price_edit_permission_lock.md @@ -0,0 +1,96 @@ +# product-v2: 产品改价权限入口拦截(业务铁规落地) + +> **服务**: hl-product-service-v2 (端口 8083) +> **PR**: #2427 +> **Issue**: #2424(P2 部分实施,剩余 P2.1 #2428 跟踪) +> **日期**: 2026-05-16 +> **影响范围**: 4 个产品价格写入接口的角色权限拦截 + +--- + +## ⚠️ 关键变化(mmg 前端需注意) + +新业务铁规:**只有定制师本人(CUSTOMIZER 且 product.created_by == 当前操作人)+ 超级管理员(SUPER_ADMIN)可以改价**。其他角色(运营/客服/房务/车务/素材/普通 ADMIN)调用价格写入接口一律返 `PERMISSION_DENIED`。 + +前端要做的事: + +1. **隐藏改价按钮**:用户登录后获取 role,如果不是 SUPER_ADMIN 也不是 CUSTOMIZER+owner,前端**隐藏所有改价入口**(产品编辑页 STEP4、班期管理、价格日历编辑等) +2. **错误码处理**:调改价接口收到 `code=410115` 时,弹"无权改价,只有定制师本人或超级管理员可改价"提示(不要继续重试,不要刷新页面) +3. 兼容性:前端短期不改也没事(后端会拦),但 UX 不友好(运营点了按钮才发现"无权"),建议前端尽快配合 + +--- + +## 一、背景 + +用户口述铁规(2026-05-16):定制师对自己的产品负责(价格策略也是个人决策),运营/客服等岗位不该改价。当前 4 个价格写入接口完全没角色校验,等于规则成空文 → 必须在 Service 入口拦截。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更 | +|---|------|------|------|------| +| 1 | 批量设置价格日历 | POST | `/admin/product/item/price-calendar/batch-set` | **新增角色拦截** | +| 2 | 删除价格日历 | POST | `/admin/product/item/price-calendar/delete` | **新增角色拦截** | +| 3 | 保存班期 | POST | `/admin/product/item/schedule/save` | **新增角色拦截** | +| 4 | 批量创建班期 | POST | `/admin/product/item/schedule/batch-create` | **新增角色拦截** | + +(精确路径以代码 `AdminProductController` 为准) + +--- + +## 三、错误码 + +**新增** `PRICE_EDIT_PERMISSION_DENIED = 410115` + +返回示例: + +```json +{ + "code": 410115, + "message": "无权改价,只有定制师本人或超级管理员可改价,productId=2055138817265897473", + "data": null, + "success": false +} +``` + +HTTP status 仍是 200(业务错误码在 Result.code),保持项目惯例。 + +--- + +## 四、校验逻辑(伪代码) + +```java +public void checkPriceEditPermission(Long productId) { + String role = AdminContextUtil.getRole(); + if ("SUPER_ADMIN".equals(role)) return; // 超管放行 + if ("CUSTOMIZER".equals(role)) { + ProductDO p = getProductOrThrow(productId); + if (currentAdminId.equals(p.getCreatedBy())) return; // 定制师 + 本人创建放行 + } + throw new BusinessException(PRICE_EDIT_PERMISSION_DENIED, productId); +} +``` + +--- + +## 五、向后兼容 + +- ✅ 接口路径、入参、出参字段全部不变(只是多了一种业务错误码) +- ✅ SUPER_ADMIN 和 CUSTOMIZER(本人产品)行为不变 +- ❌ OPERATOR / CUSTOMER_SERVICE / ROOM_MANAGER / VEHICLE_MANAGER / MATERIAL_ADMIN / 普通 ADMIN 改价**会被拒**——但本来就不该改 + +--- + +## 六、不在本期范围(P2.1 #2428 跟踪) + +- 集合深 diff(dayHotels/dayNodes 子项变化展示) +- JSON size cap 20KB 防爆 +- CR 4 小修 +- helper 自身行为 4 case 单测 + +--- + +## 七、部署 + +测试服公网光信号红灯,**P2 暂未部署测试服**。等公网恢复后部署,前端可同步联调。