Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 50s
更新了前端构建产物包括JavaScript、CSS和HTML文件,主要涉及以下变更: 1. 新增了多个组件和工具函数,包括异常页面组件、iframe组件等 2. 更新了活动管理、产品管理、优惠券管理等业务模块 3. 优化了构建配置和依赖管理 4. 修复了一些样式和功能问题 5. 更新了测试相关文件 同时更新了部分后端服务接口和测试用例。这些变更主要是为了支持新功能和改进现有功能的用户体验。
230 lines
6.1 KiB
Markdown
230 lines
6.1 KiB
Markdown
## 项目架构对齐
|
||
|
||
* 后端框架:`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(测试环境数据量下)
|
||
|
||
* 鉴权与状态校验生效;缓存命中率提升热点访问性能
|
||
|