aura-web/docs/points-mall-api-full.md

579 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 积分商城后端 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 积分汇率展示