## 项目架构对齐

* 后端框架：`gin`（`internal/pkg/core`），统一中间件与响应封装

* 路由分组：`internal/router/router.go` 下 APP 公开/鉴权分组可扩展 `products` 资源

* 鉴权：APP 端采用 `core.WrapAuthHandler(intc.AppTokenAuthVerify)`，`Authorization: Bearer <token>`

* ORM/DB：`gorm` + `gorm/gen`（`internal/repository/mysql/dao/Query`），读写分离 `GetDbR/GetDbW`

* Swagger：通过注释生成（`swaggo/swag`），挂载 `GET /swagger/*any`

* 商品相关模型：`product`（SPU）、`sku`、`category`、`spec`、`spec_option`、`product_spec`、`sku_spec_value`、推荐位 `recommendation_slot/item`

## 接口设计

### 1) 商品列表

* 路径：`GET /api/app/products`

* 鉴权：需要（App Token）

* 请求参数：

  * `page`（默认1）`pageSize`（默认20，最大50）

  * `categoryId`（可选）

  * `priceMin`、`priceMax`（可选，闭区间）

  * `salesMin`（可选）

  * `inStock`（布尔，可选，默认true，仅展示可售）

  * `sortBy`（`sales|price|createdAt`）`order`（`asc|desc`，默认 `desc`）

* 业务规则：只返回 `status=上架` 的 SPU；`inStock=true` 时需至少一个 SKU `stock-stock_locked>0`

* 响应结构：

```json
{
  "total": 1234,
  "currentPage": 1,
  "pageSize": 20,
  "list": [
    {
      "id": 1001,
      "name": "羽绒服",
      "mainImage": "https://.../xx.jpg",
      "price": 299.00,
      "sales": 5230,
      "inStock": true,
      "categoryId": 12
    }
  ]
}
```

* 过滤/排序实现：

  * 基于 `product` 表 `status/category_id/sales_count/created_at/price_min/price_max` 组合条件

  * `inStock` 通过子查询/EXISTS 关联 `sku` 计算有效库存

### 2) 商品详情

* 路径：`GET /api/app/products/:id`

* 鉴权：需要（App Token）

* 业务规则：SPU `status=上架` 且可售；下架/缺货返回业务态错误码

* 响应结构：

```json
{
  "id": 1001,
  "name": "羽绒服",
  "album": ["https://.../1.jpg", "https://.../2.jpg"],
  "priceRange": {"min": 199.00, "max": 399.00},
  "sales": 5230,
  "stock": {"total": 235, "available": 220},
  "specs": [
    {"name": "颜色", "options": ["黑色", "白色"]},
    {"name": "尺码", "options": ["M", "L", "XL"]}
  ],
  "description": "高蓬松保暖...",
  "service": ["7天无理由", "运费险"],
  "recommendations": [
    {"id": 1002, "name": "羽绒马甲", "mainImage": "...", "price": 199.00}
  ]
}
```

* 规格/相册/描述来源：

  * 相册：`product.images_json` 或关联表（若存在），解析为数组

  * 规格：`product_spec` + `spec` + `spec_option` 聚合；

  * 描述/服务保障：从 `product` 富文本字段（或扩展表）读取

* 推荐逻辑：

  * 优先使用 `recommendation_slot.scene='detail_related'` 的绑定数据

  * 兜底：同类目类目内 `sales_count` Top N（去重当前商品）

## 路由与代码结构

* 路由注册：`internal/router/router.go`

  * `appAuthGroup := router.Group("/api/app", core.WrapAuthHandler(intc.AppTokenAuthVerify))`

  * `appAuthGroup.GET("/products", product.New(repo, logger).List)`

  * `appAuthGroup.GET("/products/:id", product.New(repo, logger).Detail)`

* Handler：`internal/api/product/list_app.go`、`internal/api/product/detail_app.go`

  * 统一入参校验、容错与错误码

* Service：`internal/service/product/service.go`

  * 封装查询、库存计算、推荐聚合、缓存命中

* DAO查询：使用 `dao.Query` 与 `GetDbR`

* DTO：`internal/api/product/dto.go`（列表项与详情结构体）

## 鉴权与权限

* 使用现有 APP Token 验证中间件；Swagger `@Security LoginVerifyToken`

* 接口动作无需RBAC（面向APP用户），但保留限流与风控扩展点

## 性能与优化（500ms SLA）

* 索引：

  * `product(status, category_id, sales_count, created_at)` 复合索引

  * `sku(product_id, status)`、`sku(stock, stock_locked)` 参与计算库存

* 查询：

  * 列表：单次查询 + `EXISTS` 子查询校验库存；分页使用 `LIMIT/OFFSET`（页码≤100）

  * 详情：读取 SPU + 聚合 `SUM(stock-stock_locked)` + 规格与相册多表查询

* 缓存：

  * 本地 TTL 缓存：

    * 列表：键 `list:{filters}:{page}:{pageSize}`，TTL 30s，命中后直接返回

    * 详情：键 `detail:{id}`，TTL 60s；库存变更事件可主动失效（后续扩展）

  * 并发合并：使用 `singleflight` 避免热点详情击穿

* 传输：仅必要字段；GZIP 已由中间件统一处理

* 限流：`/products` 每用户每秒 ≤10（后续中间件实现，可选）

## 错误码与校验

* 下架：`PRODUCT_OFFSHELF`（HTTP 200，业务码），提示“商品已下架”

* 缺货：`PRODUCT_OUT_OF_STOCK`

* 参数错误：`INVALID_PARAM`（页码、价格区间校验）

* 统一响应封装沿用 `internal/pkg/core/context.go`

## Swagger文档

* 在两个 Handler 顶部添加注释：

  * `@Summary`、`@Description`、`@Tags products`

  * `@Param`（列出查询参数与类型）

  * `@Success 200 {object} ListResp` / `DetailResp`

  * `@Router /api/app/products [get]`、`/api/app/products/{id} [get]`

  * `@Security LoginVerifyToken`

* 生成后可在 `http://127.0.0.1:9991/swagger/index.html` 查看

## 单元测试

* Handler：`httptest.NewServer` + 构造 `Authorization` 头，校验分页与过滤、错误码

* Service：使用内存/模拟 DAO（或事务性测试库）覆盖：库存计算、规格聚合、推荐兜底

* 覆盖率目标：核心逻辑 ≥70%

## 压力测试

* 工具：`hey` 或 `wrk`

* 示例：

  * 列表：`hey -n 5000 -c 100 'http://localhost:9991/api/app/products?page=1&pageSize=20'`

  * 详情：`wrk -t4 -c200 -d30s 'http://localhost:9991/api/app/products/1001'`

* 目标：P95 ≤ 500ms，错误率 < 0.1%

## 联调与交付

* 与前端约定字段与筛选参数；提供响应示例与错误码表

* 提供 Swagger 文档与可复用的 Postman/HTTP 文件

* 完成线上/测试环境联调回归

## 验收标准

* 两个接口完成并通过单元与压力测试

* Swagger 文档完整，含请求/响应示例

* P95 响应时间 ≤ 500ms（测试环境数据量下）

* 鉴权与状态校验生效；缓存命中率提升热点访问性能

