# 🔍 Hurl 测试发现报告

- **测试文件**：`flows/package-resource-full-flow.hurl`
- **生成时间**：2026-03-30
- **最后更新**：2026-03-30（修复 B-1/B-2/C-1/C-2/C-3 后更新）
- **总请求数**：37
- **通过**：36
- **失败**：1（C-4，新发现 bug，断言保留）
- **发现总计**：A类: 2, B类: 2, C类: 4

---

## A 类（测试自身错误）— 已自动修正

### A-1: 角色名冲突导致创建失败（409）

- **位置**：第 2 步 `POST /api/admin/roles`
- **原因**：角色名使用固定值 `"hurl测试代理角色"`，重复运行时触发唯一约束冲突
- **修正**：角色名改为 `"hurl测试角色-{{newUuid}}"`

### A-2: 店铺创建时初始账号用户名冲突（409）

- **位置**：第 5/7 步 `POST /api/admin/shops`
- **原因**：`init_username` 使用 env 文件中的固定值，重复运行时触发 `CodeUsernameExists (1013)`
- **修正**：改为幂等策略 — `HTTP *` 接受创建结果 + 通过账号查询获取 `shop_id`
- **备注**：env 文件中的 `agent1_username` / `agent2_username` 有唯一约束，首次运行后不可变更。如需重新创建，需先清理旧数据。

---

## B 类（代码一致性问题）— ✅ 已修复

### B-1: 角色接口返回原始 GORM 模型而非 DTO

- **位置**：`POST /api/admin/roles` → 响应体
- **DTO 定义**：`RoleResponse` 有 `json:"id"`（小写）、`json:"role_name"`、`json:"created_at"` 等
- **Handler 实际（修复前）**：直接返回 `model.Role`，字段为 `"ID"`（大写 GORM 默认）、`"CreatedAt"` 等
- **影响**：前端按 API 文档对接会取不到 `id` 字段
- **决策**：添加 model→DTO 转换保持一致性
- **✅ 修复**：`internal/service/role/service.go` — `Create`/`Get`/`Update` 返回 `*dto.RoleResponse`，新增 `toResponse()` 辅助函数
- **验证**：hurl 测试断言 `$.data.id`（小写）通过

### B-2: 所有分页接口的实际格式与 DTO 定义不一致

- **位置**：所有列表接口（`GET /packages`, `GET /shops`, `GET /shop-series-grants` 等）
- **DTO 定义（修复前）**：`json:"list"` / `json:"page_size"` / `json:"total_pages"`
- **Handler 实际**：`response.SuccessWithPagination()` 返回 `{items, total, page, size}`
- **影响**：前端按 DTO 文档对接分页字段名不匹配
- **决策**：DTO 定义改为匹配 `SuccessWithPagination` 格式
- **✅ 修复**：27 个 DTO 文件 json tag 统一（`list→items`, `page_size→size`），删除 `TotalPages` 字段及 12 个 service/handler 中的计算逻辑
- **验证**：hurl 测试断言 `$.data.items` / `$.data.size` 通过

---

## C 类（接口行为 Bug）

### C-1: 套餐详情接口缺少 `one_time_commission_amount` 字段 — ✅ 已修复

- **位置**：`GET /api/admin/packages/:id`（代理视角）
- **预期**：`PackageResponse` DTO 定义了 `one_time_commission_amount` 字段
- **实际（修复前）**：列表接口返回该字段，详情接口不返回
- **决策**：Detail handler 增加佣金信息增强逻辑
- **✅ 修复**：`internal/service/package/service.go` → `Get()` 方法增加代理用户佣金信息增强，调用 `fillCommissionInfo` 与 `List` 保持一致
- **验证**：hurl 断言 `$.data.one_time_commission_amount == 3000`（一级代理）和 `== 2000`（二级代理）均通过

### C-2: 批量分配对已有授权的店铺返回 500 — ✅ 已修复

- **位置**：`POST /api/admin/shop-package-batch-allocations`
- **预期**：对已有分配记录应跳过或返回业务错误码，而非 500
- **实际（修复前）**：唯一约束冲突触发 `{code: 2001, msg: "内部服务器错误"}`
- **决策**：添加已存在记录的前置检查
- **✅ 修复**：`internal/service/shop_package_batch_allocation/service.go` → `BatchAllocate()` 方法在创建前用 `GetByShopAndPackageForSystem` 检查记录是否已存在，已存在则跳过
- **验证**：hurl 测试批量分配请求不再返回 500

### C-3: 创建授权接口响应不包含 packages 数组 — ✅ 已修复

- **位置**：`POST /api/admin/shop-series-grants`
- **预期**：创建响应应包含完整 `packages` 列表
- **实际（修复前）**：`packages` 为空数组（事务内查询，新数据未提交不可见）
- **根因**：`buildGrantResponse` 在事务内部调用，使用 `s.db` 而非事务 `tx` 查询 packages
- **决策**：创建授权后返回完整响应
- **✅ 修复**：`internal/service/shop_series_grant/service.go` → `Create()` 方法将 `buildGrantResponse` 调用移到事务提交后
- **验证**：hurl 测试 GET 详情确认 packages 存在

### C-4: 创建授权不校验目标店铺是否存在 — 🐛 未修复（新发现）

- **位置**：`POST /api/admin/shop-series-grants`
- **预期**：`shop_id` 对应的店铺不存在时应返回 404
- **实际**：`shop_id=9999999`（不存在的店铺）也能成功创建授权记录，返回 200
- **影响**：
  - 产生无效的授权数据（关联到不存在的店铺）
  - 数据一致性风险：后续查询/操作该授权时可能出现不可预期的行为
- **测试断言**：保留 `HTTP 404`（预期失败，直到代码修复）
- **建议**：`Create()` 方法中添加店铺存在性校验：
  ```go
  _, err := s.shopStore.GetByID(ctx, req.ShopID)
  if err != nil {
      return nil, errors.New(errors.CodeNotFound, "目标店铺不存在")
  }
  ```

- 决策: 修复吧

---

## 非 Bug 项（已确认的正常业务逻辑）

### 强充金额锁定机制

- **位置**：`PUT /api/admin/shop-series-grants/:id`
- **行为**：当 `force_recharge_locked=true` 时，grant 级别无法修改 `force_recharge_amount`，金额由套餐系列配置控制
- **判定**：正常业务逻辑，非 Bug
- **断言**：已按实际行为编写（`force_recharge_amount == 5000`）

### 有下级分配时不可修改成本价

- **位置**：`PUT /api/admin/shop-series-grants/:id/packages`
- **行为**：存在下级分配记录时返回 `{code: 1005, msg: "存在下级分配记录，请先回收后再修改成本价"}`
- **判定**：正常业务保护机制，非 Bug
- **断言**：改为只读验证（GET 详情），不再尝试修改

---

## 修复历史

| 日期 | 操作 | 涉及文件 |
|------|------|---------|
| 2026-03-30 | 初始报告生成 | — |
| 2026-03-30 | 修复 B-1 | `internal/service/role/service.go` |
| 2026-03-30 | 修复 B-2 | 27 个 DTO + 12 个 service/handler |
| 2026-03-30 | 修复 C-1 | `internal/service/package/service.go` |
| 2026-03-30 | 修复 C-2 | `internal/service/shop_package_batch_allocation/service.go` |
| 2026-03-30 | 修复 C-3 | `internal/service/shop_series_grant/service.go` |
| 2026-03-30 | 新增 C-4 | 未修复，断言保留 |
