372 lines
8.3 KiB
Markdown
372 lines
8.3 KiB
Markdown
# 积分商城(Points Mall)接口需求说明
|
||
|
||
本文档用于对接 aura-web 的「积分商城」页面数据与兑换流程,供后端实现接口与数据表结构时参考。
|
||
|
||
## 页面结构对应的数据
|
||
|
||
页面 UI 由 4 个区域组成:
|
||
|
||
1. 头部商品分类导航栏(横向)
|
||
2. 公告栏
|
||
3. banner 区 + 促销活动入口(2 个入口卡片)
|
||
4. 商品内容区(商品列表 + 搜索/排序/分页)
|
||
|
||
## 统一约定
|
||
|
||
- BaseURL:`https://api.hoyidata.com/aura/v1`
|
||
- 鉴权:沿用当前登录态(Cookie / Session)或 `Authorization: Bearer <token>`(以现有登录实现为准),接口需校验登录态
|
||
- 图片字段:返回绝对 URL(推荐 CDN 域名),前端不拼接
|
||
- 时间字段:建议统一使用毫秒时间戳(number)
|
||
- 失败格式:沿用现有 aura 接口的错误格式(HTTP Status + JSON message/error)
|
||
- `GET /points-mall/overview` 已废弃(HTTP 410),前端需按数据维度分别调用 /me、/categories、/announcements、/banners、/promo-entries
|
||
|
||
## 接口列表(前端必需)
|
||
|
||
### 1) 我的积分
|
||
|
||
`GET /points-mall/me`
|
||
|
||
**Response**
|
||
```json
|
||
{
|
||
"points": 1280,
|
||
"level": "Lv.2",
|
||
"totalSpentUSD": 0.174
|
||
}
|
||
```
|
||
|
||
### 2) 分类
|
||
|
||
`GET /points-mall/categories`
|
||
|
||
**Response**
|
||
```json
|
||
[
|
||
{ "id": "all", "name": "全部", "sort": 0 },
|
||
{ "id": "digital", "name": "虚拟权益", "sort": 1 }
|
||
]
|
||
```
|
||
|
||
### 3) 公告
|
||
|
||
`GET /points-mall/announcements`
|
||
|
||
**Response**
|
||
```json
|
||
[
|
||
{
|
||
"id": "a1",
|
||
"title": "公告:积分规则升级中",
|
||
"content": "本期暂不开放兑换,页面仅用于 UI 预览。",
|
||
"linkUrl": "https://..."
|
||
}
|
||
]
|
||
```
|
||
|
||
### 4) Banner
|
||
|
||
`GET /points-mall/banners`
|
||
|
||
**Response**
|
||
```json
|
||
[
|
||
{
|
||
"id": "b1",
|
||
"title": "限时上新",
|
||
"subtitle": "Up to 25% Off",
|
||
"imageUrl": "https://cdn.../banner.png",
|
||
"linkUrl": "https://..."
|
||
}
|
||
]
|
||
```
|
||
|
||
### 5) 促销入口
|
||
|
||
`GET /points-mall/promo-entries`
|
||
|
||
**Response**
|
||
```json
|
||
[
|
||
{
|
||
"id": "p1",
|
||
"title": "促销活动",
|
||
"subtitle": "本周精选",
|
||
"iconUrl": "https://cdn.../promo.png",
|
||
"linkUrl": "https://..."
|
||
},
|
||
{
|
||
"id": "p2",
|
||
"title": "积分任务",
|
||
"subtitle": "快速涨积分",
|
||
"iconUrl": "https://cdn.../tasks.png",
|
||
"linkUrl": "https://..."
|
||
}
|
||
]
|
||
```
|
||
|
||
### 6) 商品列表(搜索/筛选/排序/分页)
|
||
|
||
`GET /points-mall/products`
|
||
|
||
**Query**
|
||
- `categoryId`:可选(分类 ID;不传代表全部)
|
||
- `q`:可选(搜索关键字;匹配 name/subtitle)
|
||
- `sort`:可选,默认 `popular`
|
||
- `popular`:热度优先(建议按 sold 或近期兑换量)
|
||
- `newest`:最新上架
|
||
- `price_asc`:积分从低到高
|
||
- `price_desc`:积分从高到低
|
||
- `page`:可选,默认 `1`
|
||
- `pageSize`:可选,默认 `24`
|
||
|
||
**Response**
|
||
```json
|
||
{
|
||
"page": 1,
|
||
"pageSize": 24,
|
||
"total": 120,
|
||
"items": [
|
||
{
|
||
"id": "prd_001",
|
||
"categoryId": "digital",
|
||
"name": "Claude Pro 月卡",
|
||
"subtitle": "兑换后获得激活码",
|
||
"coverUrl": "https://cdn.../cover.png",
|
||
"pointsPrice": 1999,
|
||
"stock": 99,
|
||
"sold": 12,
|
||
"tags": ["热卖", "限时"]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 7) 商品详情
|
||
|
||
`GET /points-mall/products/{productId}`
|
||
|
||
**Response(建议)**
|
||
```json
|
||
{
|
||
"id": "prd_001",
|
||
"categoryId": "digital",
|
||
"name": "Claude Pro 月卡",
|
||
"subtitle": "兑换后获得激活码",
|
||
"coverUrl": "https://cdn.../cover.png",
|
||
"imageUrls": ["https://cdn.../1.png", "https://cdn.../2.png"],
|
||
"pointsPrice": 1999,
|
||
"stock": 99,
|
||
"sold": 12,
|
||
"tags": ["热卖", "限时"],
|
||
"description": "富文本/Markdown 均可,建议 Markdown",
|
||
"type": "virtual",
|
||
"delivery": {
|
||
"mode": "code",
|
||
"tips": "兑换成功后在【订单详情】查看兑换码"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 8) 创建兑换订单(扣积分)
|
||
|
||
`POST /points-mall/orders`
|
||
|
||
**Request**
|
||
```json
|
||
{
|
||
"productId": "prd_001",
|
||
"quantity": 1
|
||
}
|
||
```
|
||
|
||
**Response**
|
||
```json
|
||
{
|
||
"orderId": "ord_001",
|
||
"status": "paid",
|
||
"pointsCost": 1999,
|
||
"remainingPoints": 281
|
||
}
|
||
```
|
||
|
||
**关键规则**
|
||
- 需要幂等:建议支持 `Idempotency-Key` header,避免重复扣积分
|
||
- 校验库存与用户积分
|
||
- 虚拟商品/实物商品可共用该接口,但实物商品需要补充收货信息(可扩展字段)
|
||
|
||
### 9) 订单列表 / 订单详情
|
||
|
||
`GET /points-mall/orders`
|
||
|
||
**Query(建议)**
|
||
- `page` / `pageSize`
|
||
- `status`:可选
|
||
|
||
`GET /points-mall/orders/{orderId}`
|
||
|
||
**Response(建议)**
|
||
```json
|
||
{
|
||
"id": "ord_001",
|
||
"status": "paid",
|
||
"createdAt": 1730000000000,
|
||
"product": {
|
||
"id": "prd_001",
|
||
"name": "Claude Pro 月卡",
|
||
"coverUrl": "https://cdn.../cover.png"
|
||
},
|
||
"quantity": 1,
|
||
"pointsCost": 1999,
|
||
"delivery": {
|
||
"mode": "code",
|
||
"code": "XXXX-YYYY-ZZZZ"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 10) 积分流水(可选,但强烈建议)
|
||
|
||
用于解释积分变动与对账。
|
||
|
||
`GET /points-mall/me/points-ledger`
|
||
|
||
**Query(建议)**
|
||
- `page` / `pageSize`
|
||
|
||
**Response(建议)**
|
||
```json
|
||
{
|
||
"page": 1,
|
||
"pageSize": 20,
|
||
"total": 300,
|
||
"items": [
|
||
{
|
||
"id": "pl_001",
|
||
"createdAt": 1730000000000,
|
||
"change": -1999,
|
||
"balance": 281,
|
||
"reason": "兑换 Claude Pro 月卡",
|
||
"bizType": "points_mall_order",
|
||
"bizId": "ord_001"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
## 数据表结构建议(后端实现参考)
|
||
|
||
说明:下述为建议表结构,字段类型可按现有数据库规范调整;建议均包含 `created_at / updated_at` 与必要索引。
|
||
|
||
### 1) `points_mall_categories`(分类)
|
||
- `id`(PK, string/uuid)
|
||
- `name`(varchar)
|
||
- `sort`(int)
|
||
- `enabled`(bool)
|
||
|
||
索引:
|
||
- `enabled, sort`
|
||
|
||
### 2) `points_mall_announcements`(公告)
|
||
- `id`(PK)
|
||
- `title`(varchar)
|
||
- `content`(text)
|
||
- `link_url`(varchar, nullable)
|
||
- `start_at`(bigint, nullable)
|
||
- `end_at`(bigint, nullable)
|
||
- `enabled`(bool)
|
||
- `sort`(int)
|
||
|
||
索引:
|
||
- `enabled, sort`
|
||
- `start_at, end_at`
|
||
|
||
### 3) `points_mall_banners`(banner)
|
||
- `id`(PK)
|
||
- `title`(varchar)
|
||
- `subtitle`(varchar, nullable)
|
||
- `image_url`(varchar)
|
||
- `link_url`(varchar, nullable)
|
||
- `start_at`(bigint, nullable)
|
||
- `end_at`(bigint, nullable)
|
||
- `enabled`(bool)
|
||
- `sort`(int)
|
||
|
||
索引:
|
||
- `enabled, sort`
|
||
- `start_at, end_at`
|
||
|
||
### 4) `points_mall_promo_entries`(促销入口)
|
||
- `id`(PK)
|
||
- `title`(varchar)
|
||
- `subtitle`(varchar, nullable)
|
||
- `icon_url`(varchar, nullable)
|
||
- `link_url`(varchar, nullable)
|
||
- `enabled`(bool)
|
||
- `sort`(int)
|
||
|
||
索引:
|
||
- `enabled, sort`
|
||
|
||
### 5) `points_mall_products`(商品)
|
||
- `id`(PK)
|
||
- `category_id`(FK -> categories.id)
|
||
- `name`(varchar)
|
||
- `subtitle`(varchar, nullable)
|
||
- `description`(text / markdown)
|
||
- `cover_url`(varchar)
|
||
- `image_urls`(json/text,数组)
|
||
- `type`(enum: `virtual` | `physical`)
|
||
- `points_price`(int)
|
||
- `stock`(int)
|
||
- `sold`(int,累计兑换量,或可由订单聚合)
|
||
- `tags`(json/text,数组)
|
||
- `enabled`(bool)
|
||
- `start_at`(bigint, nullable)
|
||
- `end_at`(bigint, nullable)
|
||
- `sort`(int)
|
||
|
||
索引:
|
||
- `enabled, start_at, end_at`
|
||
- `category_id, enabled, sort`
|
||
- 搜索:`name`/`subtitle` 建议全文索引或 LIKE(视数据库而定)
|
||
|
||
### 6) `points_mall_orders`(订单)
|
||
- `id`(PK)
|
||
- `user_id`(FK)
|
||
- `product_id`(FK)
|
||
- `quantity`(int)
|
||
- `points_cost`(int)
|
||
- `status`(enum: `created` | `paid` | `delivered` | `canceled` | `refunded`)
|
||
- `delivery_mode`(enum: `code` | `shipping`)
|
||
- `delivery_payload`(json,虚拟码/物流信息等)
|
||
- `idempotency_key`(varchar, nullable,建议唯一索引 + user_id 组合)
|
||
|
||
索引:
|
||
- `user_id, created_at desc`
|
||
- `status, created_at desc`
|
||
- `user_id, idempotency_key`(唯一)
|
||
|
||
### 7) `points_ledger`(积分流水)
|
||
- `id`(PK)
|
||
- `user_id`(FK)
|
||
- `change`(int,正负)
|
||
- `balance`(int)
|
||
- `reason`(varchar)
|
||
- `biz_type`(varchar)
|
||
- `biz_id`(varchar)
|
||
|
||
索引:
|
||
- `user_id, created_at desc`
|
||
- `biz_type, biz_id`
|
||
|
||
## 前端实现已对齐的字段
|
||
|
||
前端已按以下字段读取:
|
||
- 分类:`id/name/sort`
|
||
- 公告:`title/content/linkUrl`
|
||
- banner:`title/subtitle/imageUrl/linkUrl`
|
||
- 促销入口:`title/subtitle/iconUrl/linkUrl`
|
||
- 商品列表:`id/categoryId/name/subtitle/coverUrl/pointsPrice/stock/sold/tags`
|
||
|
||
如后端字段命名需要用 snake_case,可在接口层做映射,但建议直接使用上述 camelCase,减少前端 mapping。
|