# 后端 fix 通知 — CUSTOM 私人定制产品已下单时 canEdit 强制 false

**日期**: 2026-05-12
**类型**: 后端修复 (PR #2085, merge commit `72ef5693`)
**模块**: 产品 V2 管理端列表/详情 → `canEdit` 字段语义增强
**通知**: @mmg
**前端是否需改动**: **不需要** — 前端继续按 `canEdit` 决定"修改/删除"按钮显示, 后端语义自动收紧
**严重性**: P2(防数据不一致)

---

## 背景

私人定制 (CUSTOM) 产品本质上是为单一客户量身设计的方案, 一旦客户下单付费即代表方案已被接受。继续允许编辑产品本身会导致订单 `productSnapshot` 与产品现状失配, 客户拿到的合同/小程序回显 ≠ 实际购买的方案。

## 改动

### `canEdit` 新规则

| 场景 | 旧 canEdit | 新 canEdit |
|------|-----------|-----------|
| 未认证 | false | false (不变) |
| CUSTOM + 有活跃订单 (status NOT IN CANCELLED/REFUNDED) | 超管=true / 创建人=true | **false (强制覆盖)** |
| CUSTOM + 0 活跃订单 (或仅 CANCELLED/REFUNDED) | 超管=true / 创建人=true | 不变 |
| CORE / GROUP | 超管=true / 创建人=true | 不变 (此规则不适用) |

### 受影响接口

- `GET /admin/product/item/{id}` (产品详情)
- `GET /admin/product/item/list` (产品分页列表)

返回的 VO 中 `canEdit` 字段会按上述新规则计算。`isOwner` 字段不受影响 (仍按 `created_by == 当前 adminId` 判定)。

### Swagger 文档已更新

`ProductDetailRespVO.canEdit` / `ProductListRespVO.canEdit` 的 `ApiModelProperty` 已写明完整规则, 前端可从 swagger UI 直接看到。

---

## 前端 mmg 无需改动

前端继续按当前逻辑读 `canEdit` 决定"修改/删除"按钮 disabled / 隐藏即可。CUSTOM 产品被锁的场景下:
- 按钮自动 disabled (符合现有交互)
- 鼠标 hover 提示可考虑加一句"产品已被订单引用, 不可修改" — 但**不是必须**, 现有提示也可继续用

---

## 内部新增端点 (前端无关, 仅记录)

`GET /internal/order/count-active-by-products?productIds=...` → `Map<productId, 活跃订单数>`
供产品列表批量计算 canEdit, 一次 GROUP BY 替代 N+1 调用。仅 product-v2 通过 Feign 调用, 外部访问会被 `X-Internal-Token` 过滤器拦截。

---

## 降级保护

订单服务异常时, `canEdit` 保留原数据权限计算结果 (不强翻 false) — 订单服务抖动不会连带产品列表/详情不可用。日志关键字: `查 CUSTOM 产品活跃订单数失败` / `批量查 CUSTOM 产品活跃订单数失败`。
