# 积分商城(Points Mall)接口需求说明 本文档用于对接 aura-web 的「积分商城」页面数据与兑换流程,供后端实现接口与数据表结构时参考。 ## 页面结构对应的数据 页面 UI 由 4 个区域组成: 1. 头部商品分类导航栏(横向) 2. 公告栏 3. banner 区 + 促销活动入口(2 个入口卡片) 4. 商品内容区(商品列表 + 搜索/排序/分页) ## 统一约定 - BaseURL:`https://api.hoyidata.com/aura/v1` - 鉴权:沿用当前登录态(Cookie / Session)或 `Authorization: Bearer `(以现有登录实现为准),接口需校验登录态 - 图片字段:返回绝对 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。