## 项目架构对齐 - 后端框架:`gin`(`internal/pkg/core`),统一中间件与响应封装 - 路由分组:`internal/router/router.go` 下 APP 公开/鉴权分组可扩展 `products` 资源 - 鉴权:APP 端采用 `core.WrapAuthHandler(intc.AppTokenAuthVerify)`,`Authorization: Bearer ` - 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(测试环境数据量下) - 鉴权与状态校验生效;缓存命中率提升热点访问性能