# 积分商城后端 API 设计文档 ## 一、整体设计概述 ### 1.1 设计目标 - 实现 API 调用消费金额与积分的自动累积(1 美元 = 1000 积分) - 提供完整的积分商城商品管理与兑换流程 - 支持实物商品的收货地址管理 - 积分与用户 ID 强绑定,确保数据一致性 ### 1.2 汇率规则 - **1 USD = 1000 积分** - 积分计算以实际消费金额为准,精确到小数点后 2 位 - 消费金额四舍五入后转换为积分 --- ## 二、数据库表结构设计 ### 2.1 用户积分表 (user_points) | 字段名 | 类型 | 说明 | 约束 | |--------|------|------|------| | id | BIGINT | 主键 | PRIMARY KEY, AUTO_INCREMENT | | user_id | VARCHAR(64) | 用户 ID | NOT NULL, INDEX | | points | BIGINT | 当前积分余额 | NOT NULL, DEFAULT 0 | | total_earned | BIGINT | 累计获得积分 | NOT NULL, DEFAULT 0 | | total_spent | BIGINT | 累计消耗积分 | NOT NULL, DEFAULT 0 | | level | VARCHAR(32) | 用户等级 | DEFAULT 'Lv.1' | | created_at | DATETIME | 创建时间 | NOT NULL | | updated_at | DATETIME | 更新时间 | NOT NULL | | version | INT | 乐观锁版本 | NOT NULL, DEFAULT 0 | **索引:** - `uk_user_id`: UNIQUE INDEX on `user_id` ### 2.2 积分流水表 (point_transactions) | 字段名 | 类型 | 说明 | 约束 | |--------|------|------|------| | id | BIGINT | 主键 | PRIMARY KEY, AUTO_INCREMENT | | user_id | VARCHAR(64) | 用户 ID | NOT NULL, INDEX | | type | VARCHAR(32) | 类型:earn/spend | NOT NULL | | points | BIGINT | 变动积分(正数获得,负数消耗) | NOT NULL | | balance | BIGINT | 变动后余额 | NOT NULL | | source_type | VARCHAR(32) | 来源类型 | NOT NULL | | source_id | VARCHAR(64) | 来源 ID | NULL | | description | VARCHAR(255) | 描述 | NULL | | created_at | DATETIME | 创建时间 | NOT NULL | **说明:** - `source_type` 枚举:`api_call`(API 调用)、`exchange`(兑换商品)、`activity`(活动奖励) - `source_id`:对应 API 调用记录 ID 或订单 ID 等 ### 2.3 积分商城商品表 (point_products) | 字段名 | 类型 | 说明 | 约束 | |--------|------|------|------| | id | VARCHAR(64) | 商品 ID | PRIMARY KEY | | category_id | VARCHAR(64) | 分类 ID | NOT NULL, INDEX | | name | VARCHAR(128) | 商品名称 | NOT NULL | | subtitle | VARCHAR(255) | 副标题 | NULL | | description | TEXT | 详细描述 | NULL | | cover_url | VARCHAR(512) | 封面图片 | NULL | | points_price | BIGINT | 所需积分 | NOT NULL | | original_price | DECIMAL(10,2) | 原价(美元) | NULL | | stock | INT | 库存 | NOT NULL, DEFAULT 0 | | sold | INT | 已售数量 | NOT NULL, DEFAULT 0 | | tags | JSON | 标签数组 | NULL | | weight | DECIMAL(8,2) | 重量(kg) | NULL | | is_physical | TINYINT | 是否实物商品 | NOT NULL, DEFAULT 1 | | is_published | TINYINT | 是否上架 | NOT NULL, DEFAULT 0 | | sort_order | INT | 排序权重 | NOT NULL, DEFAULT 0 | | created_at | DATETIME | 创建时间 | NOT NULL | | updated_at | DATETIME | 更新时间 | NOT NULL | ### 2.4 商品分类表 (point_categories) | 字段名 | 类型 | 说明 | 约束 | |--------|------|------|------| | id | VARCHAR(64) | 分类 ID | PRIMARY KEY | | name | VARCHAR(64) | 分类名称 | NOT NULL | | icon_url | VARCHAR(512) | 图标 | NULL | | sort_order | INT | 排序权重 | NOT NULL, DEFAULT 0 | | is_enabled | TINYINT | 是否启用 | NOT NULL, DEFAULT 1 | | created_at | DATETIME | 创建时间 | NOT NULL | ### 2.5 兑换订单表 (point_orders) | 字段名 | 类型 | 说明 | 约束 | |--------|------|------|------| | id | VARCHAR(64) | 订单 ID | PRIMARY KEY | | user_id | VARCHAR(64) | 用户 ID | NOT NULL, INDEX | | product_id | VARCHAR(64) | 商品 ID | NOT NULL | | product_name | VARCHAR(128) | 商品名称快照 | NOT NULL | | points_price | BIGINT | 消耗积分 | NOT NULL | | status | VARCHAR(32) | 订单状态 | NOT NULL, INDEX | | recipient_name | VARCHAR(64) | 收货人姓名 | NOT NULL | | phone | VARCHAR(32) | 联系电话 | NOT NULL | | province | VARCHAR(64) | 省份 | NOT NULL | | city | VARCHAR(64) | 城市 | NOT NULL | | district | VARCHAR(64) | 区/县 | NOT NULL | | address | VARCHAR(512) | 详细地址 | NOT NULL | | zip_code | VARCHAR(16) | 邮政编码 | NULL | | tracking_company | VARCHAR(64) | 物流公司 | NULL | | tracking_number | VARCHAR(64) | 物流单号 | NULL | | shipped_at | DATETIME | 发货时间 | NULL | | delivered_at | DATETIME | 签收时间 | NULL | | remark | VARCHAR(255) | 备注 | NULL | | created_at | DATETIME | 创建时间 | NOT NULL | | updated_at | DATETIME | 更新时间 | NOT NULL | **状态枚举 (status):** - `pending`:待处理 - `confirmed`:已确认 - `shipped`:已发货 - `delivered`:已签收 - `cancelled`:已取消 - `refunded`:已退款 ### 2.6 Banner 表 (point_banners) | 字段名 | 类型 | 说明 | 约束 | |--------|------|------|------| | id | VARCHAR(64) | Banner ID | PRIMARY KEY | | title | VARCHAR(128) | 标题 | NOT NULL | | subtitle | VARCHAR(255) | 副标题 | NULL | | image_url | VARCHAR(512) | 图片 URL | NOT NULL | | link_url | VARCHAR(512) | 跳转链接 | NULL | | sort_order | INT | 排序权重 | NOT NULL, DEFAULT 0 | | is_enabled | TINYINT | 是否启用 | NOT NULL, DEFAULT 1 | | created_at | DATETIME | 创建时间 | NOT NULL | ### 2.7 促销入口表 (point_promo_entries) | 字段名 | 类型 | 说明 | 约束 | |--------|------|------|------| | id | VARCHAR(64) | 入口 ID | PRIMARY KEY | | title | VARCHAR(64) | 标题 | NOT NULL | | subtitle | VARCHAR(128) | 副标题 | NULL | | icon_url | VARCHAR(512) | 图标 URL | NULL | | link_url | VARCHAR(512) | 跳转链接 | NULL | | sort_order | INT | 排序权重 | NOT NULL, DEFAULT 0 | | is_enabled | TINYINT | 是否启用 | NOT NULL, DEFAULT 1 | | created_at | DATETIME | 创建时间 | NOT NULL | --- ## 三、API 接口设计 ### 3.1 通用响应格式 ```json { "code": 0, "message": "success", "data": {} } ``` --- ### 3.2 积分商城概览接口 **GET** `/points-mall/overview` **描述:** 获取积分商城首页概览数据,包括用户积分、分类、Banner、促销入口等 **响应数据:** ```json { "me": { "points": 1280, "level": "Lv.2", "totalEarned": 5680, "totalSpent": 4400 }, "categories": [ { "id": "all", "name": "全部", "sort": 0 }, { "id": "digital", "name": "虚拟权益", "sort": 1 } ], "banners": [ { "id": "b1", "title": "限时活动", "subtitle": "Up to 25% Off", "imageUrl": "https://...", "linkUrl": "https://..." } ], "promoEntries": [ { "id": "p1", "title": "促销活动", "subtitle": "本周精选", "iconUrl": "https://...", "linkUrl": "https://..." } ] } ``` --- ### 3.3 商品列表接口 **GET** `/points-mall/products` **描述:** 获取商品列表,支持分页、筛选、排序 **请求参数:** | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | categoryId | String | 否 | 分类 ID,不传则查询全部 | | q | String | 否 | 搜索关键词 | | sort | String | 否 | 排序方式:`popular`(热度)、`newest`(最新)、`price_asc`(价格升序)、`price_desc`(价格降序),默认 `popular` | | page | Int | 否 | 页码,默认 1 | | pageSize | Int | 否 | 每页数量,默认 24 | **响应数据:** ```json { "page": 1, "pageSize": 24, "total": 120, "items": [ { "id": "prod_001", "categoryId": "digital", "name": "商品名称", "subtitle": "商品简短描述", "coverUrl": "https://...", "pointsPrice": 1990, "stock": 99, "sold": 12, "tags": ["限时", "热卖"], "isPhysical": true } ] } ``` --- ### 3.4 商品详情接口 **GET** `/points-mall/products/{id}` **描述:** 获取单个商品的详细信息 **路径参数:** - `id`: 商品 ID **响应数据:** ```json { "id": "prod_001", "categoryId": "digital", "name": "商品名称", "subtitle": "商品简短描述", "description": "商品详细描述...", "coverUrl": "https://...", "imageUrls": ["https://...", "https://..."], "pointsPrice": 1990, "originalPrice": 19.90, "stock": 99, "sold": 12, "tags": ["限时", "热卖"], "isPhysical": true, "weight": 0.5 } ``` --- ### 3.5 兑换商品接口 **POST** `/points-mall/exchange` **描述:** 提交兑换订单,扣减积分,创建订单 **请求体:** ```json { "productId": "prod_001", "recipientName": "张三", "phone": "13800138000", "province": "广东省", "city": "深圳市", "district": "南山区", "address": "科技园南区XX路XX号", "zipCode": "518000" } ``` **字段说明:** | 字段名 | 类型 | 必填 | 说明 | |--------|------|------|------| | productId | String | 是 | 商品 ID | | recipientName | String | 是 | 收货人姓名 | | phone | String | 是 | 手机号码 | | province | String | 是 | 省份 | | city | String | 是 | 城市 | | district | String | 是 | 区/县 | | address | String | 是 | 详细地址 | | zipCode | String | 否 | 邮政编码 | **响应数据:** ```json { "orderId": "order_20240601_0001", "pointsDeducted": 1990, "remainingPoints": 10810 } ``` **错误码:** | code | message | 说明 | |------|---------|------| | 40001 | 积分不足 | 用户积分不足以兑换该商品 | | 40002 | 商品已下架 | 商品未发布或已下架 | | 40003 | 库存不足 | 商品库存不足 | | 40004 | 商品不存在 | 商品 ID 无效 | --- ### 3.6 兑换订单列表接口 **GET** `/points-mall/orders` **描述:** 获取用户的兑换订单列表 **请求参数:** | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | status | String | 否 | 订单状态筛选 | | page | Int | 否 | 页码,默认 1 | | pageSize | Int | 否 | 每页数量,默认 20 | **响应数据:** ```json { "page": 1, "pageSize": 20, "total": 5, "items": [ { "id": "order_20240601_0001", "productId": "prod_001", "productName": "商品名称", "coverUrl": "https://...", "pointsPrice": 1990, "status": "shipped", "statusText": "已发货", "trackingCompany": "顺丰速运", "trackingNumber": "SF1234567890", "createdAt": "2024-06-01T10:30:00Z" } ] } ``` --- ### 3.7 订单详情接口 **GET** `/points-mall/orders/{id}` **描述:** 获取单个订单的详细信息 **路径参数:** - `id`: 订单 ID **响应数据:** ```json { "id": "order_20240601_0001", "productId": "prod_001", "productName": "商品名称", "coverUrl": "https://...", "pointsPrice": 1990, "status": "shipped", "statusText": "已发货", "recipientName": "张三", "phone": "13800138000", "province": "广东省", "city": "深圳市", "district": "南山区", "address": "科技园南区XX路XX号", "zipCode": "518000", "trackingCompany": "顺丰速运", "trackingNumber": "SF1234567890", "shippedAt": "2024-06-02T14:00:00Z", "createdAt": "2024-06-01T10:30:00Z" } ``` --- ### 3.8 积分流水接口 **GET** `/points-mall/transactions` **描述:** 获取用户积分变动记录 **请求参数:** | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | type | String | 否 | 类型:`earn`(收入)、`spend`(支出) | | page | Int | 否 | 页码,默认 1 | | pageSize | Int | 否 | 每页数量,默认 20 | **响应数据:** ```json { "page": 1, "pageSize": 20, "total": 150, "items": [ { "id": 1001, "type": "earn", "points": 500, "balance": 12800, "sourceType": "api_call", "description": "API 调用奖励", "createdAt": "2024-06-01T10:30:00Z" }, { "id": 1000, "type": "spend", "points": -1990, "balance": 12300, "sourceType": "exchange", "sourceId": "order_20240601_0001", "description": "兑换商品:XXX", "createdAt": "2024-06-01T09:00:00Z" } ] } ``` --- ## 四、积分累积逻辑 ### 4.1 积分累积触发时机 积分累积在以下场景触发: 1. **API 调用完成时**:每次成功的 LLM API 调用后,根据实际消费金额计算积分 2. **活动奖励**:通过活动接口手动发放积分 ### 4.2 积分计算公式 ``` 积分 = 实际消费金额(USD) × 1000 ``` **示例:** - 消费 $0.002 → 2 积分 - 消费 $0.5 → 500 积分 - 消费 $1.2 → 1200 积分 ### 4.3 积分入账流程 1. API 调用完成,记录 token 使用量和费用 2. 计算积分:`points = floor(costUSD * 1000)` 或四舍五入 3. 使用事务更新 `user_points` 表: - `points` += 新增积分 - `total_earned` += 新增积分 4. 写入 `point_transactions` 流水记录 5. **注意**:需要使用乐观锁防止并发问题 --- ## 五、兑换流程设计 ### 5.1 兑换流程图 ``` 用户点击兑换 ↓ 验证积分是否足够 ↓ 验证商品是否上架 ↓ 验证库存是否充足 ↓ 【事务开始】 扣减用户积分 ↓ 扣减商品库存 ↓ 增加商品已售数量 ↓ 创建兑换订单 ↓ 写入积分消费流水 ↓ 【事务提交】 ↓ 返回订单信息 ``` ### 5.2 并发控制 1. **用户积分表**:使用乐观锁(`version` 字段)防止超扣 2. **商品库存**:使用 `UPDATE ... WHERE stock >= 1` 原子操作 3. **事务隔离**:使用可重复读(REPEATABLE READ)级别 --- ## 六、管理后台接口(可选) ### 6.1 商品管理 - **POST** `/admin/points-mall/products` - 创建商品 - **PUT** `/admin/points-mall/products/{id}` - 更新商品 - **DELETE** `/admin/points-mall/products/{id}` - 删除商品 - **PATCH** `/admin/points-mall/products/{id}/publish` - 上架/下架商品 ### 6.2 订单管理 - **GET** `/admin/points-mall/orders` - 订单列表 - **PUT** `/admin/points-mall/orders/{id}/ship` - 发货(填写物流信息) - **PUT** `/admin/points-mall/orders/{id}/confirm` - 确认订单 - **PUT** `/admin/points-mall/orders/{id}/cancel` - 取消订单(退还积分) --- ## 七、注意事项 ### 7.1 数据一致性 - 所有涉及积分变动的操作必须使用数据库事务 - 使用乐观锁防止并发扣减问题 - 积分流水表是审计的重要依据,不可删除或修改 ### 7.2 安全考虑 - 所有接口必须经过用户认证 - 积分扣减必须验证当前用户的积分余额 - 防止重复提交兑换请求(可使用幂等键) ### 7.3 性能优化 - 用户积分数据可考虑缓存(如 Redis) - 商品列表接口建议加缓存 - 积分流水表建议按时间分区 ### 7.4 监控告警 - 积分扣除失败告警 - 库存不足告警 - 异常大额积分变动告警 --- ## 八、前端已实现功能 前端已完成以下功能开发,等待后端接口对接: 1. ✅ 积分商城首页 UI,包含积分展示 2. ✅ 商品分类筛选、搜索、排序 3. ✅ 商品卡片列表展示 4. ✅ 兑换弹窗,包含收货地址表单 5. ✅ 积分不足提示 6. ✅ 统计页面展示消费金额与积分对应关系 7. ✅ 1 USD = 1000 积分汇率展示