User 前台 API 文档
更新时间:2026-03-31
本文档覆盖 user/src/api/index.ts 当前全部前台 API,字段定义以以下实现为准:
api/internal/router/router.goapi/internal/http/handlers/public/*.goapi/internal/models/*.go
开源仓库地址
- API(主项目):https://github.com/dujiao-next/dujiao-next
- User(用户前台):https://github.com/dujiao-next/user
- Admin(后台):https://github.com/dujiao-next/admin
- Document(文档):https://github.com/dujiao-next/document
0. API 变更日志
0.0.1 Public API Response DTO 精简与安全加固(2026-03-31)
破坏性变更
- 所有订单相关接口不再返回自增
id字段(Order.id、OrderItem.id、Fulfillment.id),统一使用order_no作为订单标识。 - 订单详情路由从
GET /orders/:id改为GET /orders/:order_no;取消路由从POST /orders/:id/cancel改为POST /orders/:order_no/cancel;交付下载从GET /orders/:id/fulfillment/download改为GET /orders/:order_no/fulfillment/download。 - 游客订单详情路由从
GET /guest/orders/:id改为GET /guest/orders/:order_no;交付下载同理。 - 旧路由
GET /orders/by-order-no/:order_no和GET /guest/orders/by-order-no/:order_no已删除,直接使用GET /orders/:order_no。 - 支付接口
POST /payments和GET /payments/latest的请求参数order_id改为order_no(string 类型)。游客支付接口同理。 GET /payments/latest响应中order_id改为order_no。
移除的字段
以下字段从 Public API 响应中永久移除,前端不应再依赖:
- Order:
id、parent_id、user_id、coupon_id、promotion_id、client_ip、updated_at - OrderItem:
id、order_id、delivered_by、created_at、updated_at - Fulfillment:
id、order_id、delivered_by、created_at、updated_at - PublicProduct:
cost_price_amount、manual_stock_locked、manual_stock_sold、is_active、sort_order、created_at、updated_at、is_affiliate_enabled、is_mapped、seo_meta - PublicSKU:
cost_price_amount、product_id、manual_stock_locked、auto_stock_total、auto_stock_locked、auto_stock_sold、sort_order、created_at、updated_at - Banner:
name、is_active、start_at、end_at、sort_order、created_at、updated_at - Post:
is_published、created_at - Category:
created_at - WalletTransaction:
order_id - AffiliateCommission:
order_id
新增的字段
- Order:
member_discount_amount、wallet_paid_amount、online_paid_amount、refunded_amount - OrderItem:
sku_snapshot、member_discount_amount - Fulfillment:
payload_line_count - UserProfile:
member_level_id、total_recharged、total_spent - Category:
parent_id、icon
0.0 活动价系统改进:阶梯规则 + 前端展示优化(2026-03-09)
新增字段
PublicProduct新增promotion_rules字段(类型PromotionRule[]),返回该商品所有生效中的活动规则。- 即使当前 SKU 单价未满足活动门槛,该字段也会返回数据,便于前端展示"多买可享折扣"等活动提示。
阶梯活动规则
- 同一商品支持配置多条活动规则(不同
min_amount门槛),形成阶梯折扣。 - 后端按购买小计(
单价 × 数量)从高到低匹配门槛,取满足条件的最高档规则计算折扣。 - 示例:
- 规则 A:
min_amount=50,优惠 1% - 规则 B:
min_amount=150,优惠 2% - 购买金额 49 → 无折扣;100 → 匹配规则 A(1%);200 → 匹配规则 B(2%)
- 规则 A:
- 单条规则场景行为不变,无破坏性变更。
活动类型说明
| type | 含义 | 计算方式 |
|---|---|---|
percent | 百分比折扣 | 每件单价 = 原价 × (100 - value) / 100 |
fixed | 固定金额减免 | 每件单价 = 原价 - value |
special_price | 直降特价 | 每件单价 = value |
注意: 所有折扣均作用于单件商品价格,而非订单总金额。
min_amount是购买小计门槛(单价 × 数量),达到门槛后每件商品享受对应折扣。
1. 通用约定
1.1 Base URL
- API 前缀:
/api/v1 - 本文中的路径均省略
/api/v1,调用时请自行拼接。
1.2 鉴权
用户登录态接口需携带:
Authorization: Bearer <user_token>1.3 统一响应结构
成功响应
{
"status_code": 0,
"msg": "success",
"data": {},
"pagination": {
"page": 1,
"page_size": 20,
"total": 100,
"total_page": 5
}
}失败响应
{
"status_code": 400,
"msg": "请求参数错误",
"data": {
"request_id": "01HR..."
}
}顶层字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| status_code | number | 业务状态码,0 表示成功,非 0 表示失败 |
| msg | string | 业务提示信息 |
| data | object/array/null | 业务数据 |
| pagination | object | 分页信息,仅分页接口返回 |
1.4 分页参数约定
| 参数 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| page | number | 否 | 1 | 页码,最小 1 |
| page_size | number | 否 | 20 | 每页条数,最大 100 |
1.5 通用请求结构
CaptchaPayload(验证码载荷)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| captcha_id | string | 否 | 图片验证码 ID(provider=image 时使用) |
| captcha_code | string | 否 | 图片验证码文本(provider=image 时使用) |
| turnstile_token | string | 否 | Turnstile Token(provider=turnstile 时使用) |
OrderItemInput(订单项)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| product_id | number | 是 | 商品 ID |
| quantity | number | 是 | 购买数量(>0) |
| fulfillment_type | string | 否 | 交付类型,推荐值:manual / auto |
ManualFormData(人工交付表单值)
manual_form_data 是对象,Key 为 product_id,Value 为该商品的表单提交数据。
{
"1001": {
"receiver_name": "张三",
"phone": "13277745648",
"address": "广东省深圳市..."
}
}2. 数据对象字段字典
以下对象用于后续各接口“返回结构”引用。
2.1 PublicProduct
| 字段 | 类型 | 说明 |
|---|---|---|
| category_id | number | 分类 ID |
| slug | string | 商品唯一标识 |
| title | object | 多语言标题 |
| description | object | 多语言摘要 |
| content | object | 多语言详情内容 |
| price_amount | string | 商品价格金额 |
| images | string[] | 商品图片列表 |
| tags | string[] | 标签列表 |
| purchase_type | string | 购买身份限制:guest / member |
| max_purchase_quantity | number | 单次最大购买数量(0 表示不限) |
| fulfillment_type | string | 交付类型:manual / auto |
| manual_form_schema | object | 人工交付表单 Schema |
| manual_stock_available | number | 人工可用库存 |
| auto_stock_available | number | 自动可用库存 |
| stock_status | string | 库存状态:unlimited / in_stock / low_stock / out_of_stock |
| is_sold_out | boolean | 是否售罄 |
| category | Category | 分类信息 |
| skus | PublicSKU[] | SKU 列表 |
| promotion_id | number | 命中的活动 ID(可选) |
| promotion_name | string | 活动名称(可选) |
| promotion_type | string | 活动类型(可选) |
| promotion_price_amount | string | 活动价金额(可选) |
| promotion_rules | PromotionRule[] | 活动规则列表(可选) |
| member_prices | MemberLevelPrice[] | 会员等级价格列表(可选) |
2.1.1 PublicSKU
skus[] 数组中每个元素的结构如下:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | SKU ID(下单时使用此 ID) |
| sku_code | string | SKU 编码(同商品内唯一) |
| spec_values | object | 规格值(多语言) |
| price_amount | string | SKU 原价 |
| manual_stock_total | number | 人工库存总量(-1 表示无限库存) |
| manual_stock_sold | number | 人工库存已售量 |
| auto_stock_available | number | 自动发货库存可用量 |
| upstream_stock | number | 上游库存(-1 表示无限,0 表示售罄) |
| is_active | boolean | 是否启用 |
| promotion_price_amount | string | SKU 活动价金额(可选) |
| member_price_amount | string | 会员价金额(可选) |
2.1.2 PromotionRule
promotion_rules[] 数组中每个元素的结构如下:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | 活动规则 ID |
| name | string | 活动名称 |
| type | string | 活动类型:percent / fixed / special_price |
| value | string | 活动数值(字符串金额/百分比,如 "2.00" 或 "5.00") |
| min_amount | string | 触发门槛金额(购买小计 = 单价 × 数量,如 "200.00";"0.00" 表示无门槛) |
活动类型与折扣计算:
type 含义 折扣计算(作用于每件单价) percent百分比折扣 单价 = 原价 × (100 - value) / 100 fixed固定金额减免 单价 = 原价 - value special_price直降特价 单价 = value
- 所有折扣均作用于单件商品价格,而非订单总金额。
min_amount是购买小计门槛(单价 × 数量),达到门槛后每件商品享受对应折扣。- 同一商品可配置多条不同
min_amount的规则,形成阶梯折扣。后端从高到低匹配,取满足条件的最高档。
促销价计算说明: 促销活动以商品为维度配置,促销价下沉到每个 SKU 独立计算。例如某商品配置了"优惠 2%"促销,99 元的 SKU 促销价为 97.02,77 元的 SKU 促销价为 75.46。产品级的
promotion_price_amount取所有 SKU 中的最低促销价,适用于列表页展示。promotion_rules返回所有生效规则(按min_amount升序),即使当前单价未满足门槛也会返回,便于前端展示活动提示。
2.2 Post
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | 文章 ID |
| slug | string | 文章唯一标识 |
| type | string | 类型:blog / notice |
| title | object | 多语言标题 |
| summary | object | 多语言摘要 |
| content | object | 多语言内容 |
| thumbnail | string | 缩略图 URL |
| published_at | string/null | 发布时间 |
2.3 Banner
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | Banner ID |
| position | string | 投放位置(如 home_hero) |
| title | object | 多语言标题 |
| subtitle | object | 多语言副标题 |
| image | string | 主图 |
| mobile_image | string | 移动端图 |
| link_type | string | 跳转类型:none / internal / external |
| link_value | string | 跳转值 |
| open_in_new_tab | boolean | 是否新窗口打开 |
2.4 Category
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | 分类 ID |
| parent_id | number | 父分类 ID(0 表示一级分类) |
| slug | string | 分类唯一标识 |
| name | object | 多语言名称 |
| icon | string | 分类图标 |
| sort_order | number | 排序 |
2.5 UserProfile
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | 用户 ID |
| string | 邮箱 | |
| nickname | string | 昵称 |
| email_verified_at | string/null | 邮箱验证时间 |
| locale | string | 语言(如 zh-CN) |
| member_level_id | number | 会员等级 ID |
| total_recharged | string | 累计充值金额 |
| total_spent | string | 累计消费金额 |
| email_change_mode | string | 邮箱变更模式:bind_only / change_with_old_and_new |
| password_change_mode | string | 密码变更模式:set_without_old / change_with_old |
2.6 UserLoginLog
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | 日志 ID |
| user_id | number | 用户 ID(失败时可能为 0) |
| string | 登录邮箱 | |
| status | string | 登录结果:success / failed |
| fail_reason | string | 失败原因枚举 |
| client_ip | string | 客户端 IP |
| user_agent | string | 客户端 UA |
| login_source | string | 登录来源:web / telegram |
| request_id | string | 请求追踪 ID |
| created_at | string | 记录创建时间 |
2.7 OrderPreview
| 字段 | 类型 | 说明 |
|---|---|---|
| currency | string | 币种(全站统一,来源 site_config.currency) |
| original_amount | string | 原价总额 |
| discount_amount | string | 总优惠金额 |
| promotion_discount_amount | string | 活动优惠金额 |
| total_amount | string | 应付总额 |
| items | OrderPreviewItem[] | 预览订单项 |
2.8 OrderPreviewItem
| 字段 | 类型 | 说明 |
|---|---|---|
| product_id | number | 商品 ID |
| title | object | 商品标题快照(多语言) |
| tags | string[] | 商品标签快照 |
| unit_price | string | 单价 |
| quantity | number | 数量 |
| total_price | string | 小计 |
| coupon_discount_amount | string | 优惠券分摊金额 |
| promotion_discount_amount | string | 活动分摊金额 |
| fulfillment_type | string | 交付类型 |
2.9 Order
| 字段 | 类型 | 说明 |
|---|---|---|
| order_no | string | 订单号 |
| guest_email | string | 游客邮箱(游客订单) |
| guest_locale | string | 游客语言 |
| status | string | 订单状态:pending_payment / paid / fulfilling / partially_delivered / delivered / completed / canceled |
| currency | string | 订单币种 |
| original_amount | string | 原价 |
| discount_amount | string | 优惠金额 |
| member_discount_amount | string | 会员优惠金额 |
| promotion_discount_amount | string | 活动优惠金额 |
| total_amount | string | 实付金额 |
| wallet_paid_amount | string | 钱包支付金额 |
| online_paid_amount | string | 在线支付金额 |
| refunded_amount | string | 已退款金额 |
| expires_at | string/null | 待支付过期时间 |
| paid_at | string/null | 支付成功时间 |
| canceled_at | string/null | 取消时间 |
| created_at | string | 创建时间 |
| items | OrderItem[] | 订单项 |
| fulfillment | Fulfillment | 交付记录(可选) |
| children | Order[] | 子订单列表(可选) |
2.10 OrderItem
| 字段 | 类型 | 说明 |
|---|---|---|
| title | object | 商品标题快照 |
| sku_snapshot | object | SKU 快照(编码/规格) |
| tags | string[] | 商品标签快照 |
| unit_price | string | 单价 |
| quantity | number | 数量 |
| total_price | string | 小计 |
| coupon_discount_amount | string | 优惠券分摊金额 |
| member_discount_amount | string | 会员优惠分摊金额 |
| promotion_discount_amount | string | 活动优惠金额 |
| fulfillment_type | string | 交付类型 |
| manual_form_schema_snapshot | object | 人工交付表单 Schema 快照 |
| manual_form_submission | object | 用户提交的人工表单值 |
2.11 Fulfillment
| 字段 | 类型 | 说明 |
|---|---|---|
| type | string | 交付类型:auto / manual |
| status | string | 交付状态:pending / delivered |
| payload | string | 文本交付内容 |
| payload_line_count | number | 交付内容总行数 |
| delivery_data | object | 结构化交付信息 |
| delivered_at | string/null | 交付时间 |
2.12 PaymentLaunch
| 字段 | 类型 | 说明 |
|---|---|---|
| payment_id | number | 支付记录 ID |
| order_no | string | 订单号(latest 接口返回) |
| channel_id | number | 支付渠道 ID(latest 接口返回) |
| provider_type | string | 提供方:official / epay |
| channel_type | string | 渠道:alipay / wechat / paypal / stripe 等 |
| interaction_mode | string | 交互方式:qr / redirect / wap / page |
| pay_url | string | 跳转支付链接 |
| qr_code | string | 二维码内容 |
| expires_at | string/null | 支付单过期时间 |
3. 公共接口(无需登录)
3.1 获取站点配置
接口:GET /public/config
认证:否
请求参数
无
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"languages": ["zh-CN", "zh-TW", "en-US"],
"currency": "CNY",
"contact": {
"telegram": "https://t.me/dujiaostudio",
"whatsapp": "https://wa.me/1234567890"
},
"site_name": "Dujiao-Next",
"scripts": [
{
"name": "Plausible",
"enabled": true,
"position": "head",
"code": "<script defer data-domain=\"localhost\" src=\"https://xxx.com/js/script.js\"></script>"
}
],
"payment_channels": [
{
"id": 1,
"name": "支付宝电脑站",
"provider_type": "official",
"channel_type": "alipay",
"interaction_mode": "page",
"fee_rate": "0.00"
}
],
"captcha": {
"provider": "turnstile",
"scenes": {
"login": true,
"register_send_code": true,
"reset_send_code": false,
"guest_create_order": false
},
"turnstile": {
"site_key": "0x4AAA..."
}
},
"telegram_auth": {
"enabled": true,
"bot_username": "dujiao_auth_bot"
}
}
}返回结构(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| languages | string[] | 站点启用语言列表 |
| currency | string | 全站币种(3 位大写代码,如 CNY) |
| contact | object | 联系方式配置 |
| scripts | object[] | 前台自定义 JS 脚本配置 |
| payment_channels | object[] | 前台可用支付渠道列表 |
| captcha | object | 验证码公开配置 |
| telegram_auth | object | Telegram 登录公开配置(enabled、bot_username) |
| 其他字段 | any | 后台站点设置中的公开字段(动态扩展) |
3.2 商品列表
接口:GET /public/products
认证:否
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | number | 否 | 页码 |
| page_size | number | 否 | 每页条数(最大 100) |
| category_id | string | 否 | 分类 ID |
| search | string | 否 | 搜索关键词(标题等) |
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": [
{
"category_id": 10,
"slug": "netflix-plus",
"title": { "zh-CN": "奈飞会员" },
"description": { "zh-CN": "全区可用" },
"content": { "zh-CN": "详情说明" },
"price_amount": "99.00",
"images": ["/uploads/product/1.png"],
"tags": ["热门"],
"purchase_type": "member",
"fulfillment_type": "manual",
"manual_form_schema": { "fields": [] },
"manual_stock_available": 88,
"auto_stock_available": 0,
"stock_status": "in_stock",
"is_sold_out": false
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 1,
"total_page": 1
}
}返回结构(data)
data:PublicProduct[]pagination:分页对象(见通用约定)
3.3 商品详情
接口:GET /public/products/:slug
认证:否
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| slug | string | 是 | 商品 slug |
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"slug": "netflix-plus",
"title": { "zh-CN": "奈飞会员" },
"price_amount": "99.00",
"fulfillment_type": "manual",
"manual_form_schema": {
"fields": [
{
"key": "receiver_name",
"type": "text",
"required": true,
"label": { "zh-CN": "收件人" }
}
]
},
"manual_stock_available": 88,
"auto_stock_available": 0,
"stock_status": "in_stock",
"is_sold_out": false
}
}返回结构(data)
data:PublicProduct
3.4 文章列表
接口:GET /public/posts
认证:否
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | number | 否 | 页码 |
| page_size | number | 否 | 每页条数 |
| type | string | 否 | 文章类型:blog / notice |
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": [
{
"id": 1,
"slug": "release-2026-02",
"type": "notice",
"title": { "zh-CN": "版本更新" },
"summary": { "zh-CN": "新增支付渠道" },
"content": { "zh-CN": "详细内容" },
"thumbnail": "/uploads/post/1.png",
"published_at": "2026-02-11T10:00:00Z"
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 1,
"total_page": 1
}
}返回结构(data)
data:Post[]pagination:分页对象
3.5 文章详情
接口:GET /public/posts/:slug
认证:否
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| slug | string | 是 | 文章 slug |
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"id": 1,
"slug": "release-2026-02",
"type": "notice",
"title": { "zh-CN": "版本更新" },
"summary": { "zh-CN": "新增支付渠道" },
"content": { "zh-CN": "详细内容" },
"thumbnail": "/uploads/post/1.png",
"published_at": "2026-02-11T10:00:00Z"
}
}返回结构(data)
data:Post
3.6 Banner 列表
接口:GET /public/banners
认证:否
Query 参数
| 参数 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| position | string | 否 | home_hero | Banner 位置 |
| limit | number | 否 | 10 | 最大 50 |
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": [
{
"id": 1,
"position": "home_hero",
"title": { "zh-CN": "欢迎来到 D&N" },
"subtitle": { "zh-CN": "稳定交付" },
"image": "/uploads/banner/hero.png",
"mobile_image": "/uploads/banner/hero-mobile.png",
"link_type": "internal",
"link_value": "/products",
"open_in_new_tab": false
}
]
}返回结构(data)
data:Banner[]
3.7 分类列表
接口:GET /public/categories
认证:否
请求参数
无
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": [
{
"id": 10,
"parent_id": 0,
"slug": "memberships",
"name": { "zh-CN": "会员服务" },
"icon": "",
"sort_order": 100
}
]
}返回结构(data)
data:Category[]
3.8 获取图片验证码挑战
接口:GET /public/captcha/image
认证:否
请求参数
无
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"captcha_id": "9f2b2be147df4f6eb6f8",
"image_base64": "data:image/png;base64,iVBORw0KGgoAAA..."
}
}返回结构(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| captcha_id | string | 本次验证码 ID |
| image_base64 | string | Base64 图片(data URL) |
4. 认证接口(无需登录)
4.1 发送邮箱验证码
接口:POST /auth/send-verify-code
认证:否
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 邮箱 | |
| purpose | string | 是 | 验证码用途:register / reset |
| captcha_payload | object | 否 | 验证码参数(见通用结构) |
请求示例
{
"email": "user@example.com",
"purpose": "register",
"captcha_payload": {
"captcha_id": "",
"captcha_code": "",
"turnstile_token": ""
}
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"sent": true
}
}返回结构(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| sent | boolean | 是否发送成功 |
4.2 用户注册
接口:POST /auth/register
认证:否
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 邮箱 | |
| password | string | 是 | 密码 |
| code | string | 是 | 邮箱验证码 |
| agreement_accepted | boolean | 是 | 是否同意协议,必须为 true |
请求示例
{
"email": "user@example.com",
"password": "StrongPass123",
"code": "123456",
"agreement_accepted": true
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"user": {
"id": 101,
"email": "user@example.com",
"nickname": "user",
"email_verified_at": "2026-02-11T10:00:00Z"
},
"token": "eyJhbGciOi...",
"expires_at": "2026-02-18T10:00:00Z"
}
}返回结构(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| user | object | 注册用户信息(id/email/nickname/email_verified_at) |
| token | string | 用户 JWT |
| expires_at | string | Token 过期时间(RFC3339) |
4.3 用户登录
接口:POST /auth/login
认证:否
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 邮箱 | |
| password | string | 是 | 密码 |
| remember_me | boolean | 否 | 是否延长登录态 |
| captcha_payload | object | 否 | 验证码参数(见通用结构) |
请求示例
{
"email": "user@example.com",
"password": "StrongPass123",
"remember_me": true,
"captcha_payload": {
"captcha_id": "",
"captcha_code": "",
"turnstile_token": ""
}
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"user": {
"id": 101,
"email": "user@example.com",
"nickname": "user",
"email_verified_at": "2026-02-11T10:00:00Z"
},
"token": "eyJhbGciOi...",
"expires_at": "2026-02-25T10:00:00Z"
}
}返回结构(data)
与注册接口一致:user + token + expires_at
4.4 忘记密码
接口:POST /auth/forgot-password
认证:否
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 邮箱 | |
| code | string | 是 | 邮箱验证码 |
| new_password | string | 是 | 新密码 |
请求示例
{
"email": "user@example.com",
"code": "123456",
"new_password": "NewStrongPass123"
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"reset": true
}
}返回结构(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| reset | boolean | 是否重置成功 |
4.5 Telegram 登录
接口:POST /auth/telegram/login
认证:否
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 是 | Telegram 用户 ID |
| first_name | string | 否 | 名 |
| last_name | string | 否 | 姓 |
| username | string | 否 | Telegram 用户名 |
| photo_url | string | 否 | Telegram 头像 URL |
| auth_date | number | 是 | Telegram 授权时间戳(秒) |
| hash | string | 是 | Telegram 登录签名 |
请求示例
{
"id": 123456789,
"first_name": "Dujiao",
"last_name": "User",
"username": "dujiao_user",
"photo_url": "https://t.me/i/userpic/320/xxx.jpg",
"auth_date": 1739250000,
"hash": "f1b2c3..."
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"user": {
"id": 101,
"email": "telegram_123456789@login.local",
"nickname": "telegram_123456789",
"email_verified_at": null
},
"token": "eyJhbGciOi...",
"expires_at": "2026-02-25T10:00:00Z"
}
}返回结构(data)
与注册接口一致:user + token + expires_at
首次 Telegram 登录且未绑定站内账号时,系统会自动创建账号并直接登录。
5. 登录用户资料接口(需 Bearer Token)
5.1 获取当前用户
接口:GET /me
认证:是
请求参数
无
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"id": 101,
"email": "user@example.com",
"nickname": "user",
"email_verified_at": "2026-02-11T10:00:00Z",
"locale": "zh-CN",
"email_change_mode": "change_with_old_and_new",
"password_change_mode": "change_with_old"
}
}返回结构(data)
data:UserProfile
5.2 登录日志列表
接口:GET /me/login-logs
认证:是
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | number | 否 | 页码 |
| page_size | number | 否 | 每页条数 |
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": [
{
"id": 1,
"user_id": 101,
"email": "user@example.com",
"status": "success",
"fail_reason": "",
"client_ip": "127.0.0.1",
"user_agent": "Mozilla/5.0",
"login_source": "web",
"request_id": "01HR...",
"created_at": "2026-02-11T12:00:00Z"
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 1,
"total_page": 1
}
}返回结构(data)
data:UserLoginLog[]pagination:分页对象
5.3 更新用户资料
接口:PUT /me/profile
认证:是
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| nickname | string | 否 | 昵称 |
| locale | string | 否 | 语言,例如 zh-CN |
nickname与locale至少传一个。
请求示例
{
"nickname": "新昵称",
"locale": "zh-CN"
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"id": 101,
"email": "user@example.com",
"nickname": "新昵称",
"email_verified_at": "2026-02-11T10:00:00Z",
"locale": "zh-CN",
"email_change_mode": "change_with_old_and_new",
"password_change_mode": "change_with_old"
}
}返回结构(data)
data:UserProfile
5.4 获取 Telegram 绑定状态
接口:GET /me/telegram
认证:是
请求参数
无
成功响应示例(已绑定)
{
"status_code": 0,
"msg": "success",
"data": {
"bound": true,
"provider": "telegram",
"provider_user_id": "123456789",
"username": "dujiao_user",
"avatar_url": "https://t.me/i/userpic/320/xxx.jpg",
"auth_at": "2026-02-20T12:00:00Z",
"updated_at": "2026-02-20T12:00:00Z"
}
}成功响应示例(未绑定)
{
"status_code": 0,
"msg": "success",
"data": {
"bound": false
}
}返回结构(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| bound | boolean | 是否已绑定 Telegram |
| provider | string | OAuth 提供方(绑定时为 telegram) |
| provider_user_id | string | Telegram 用户 ID(字符串) |
| username | string | Telegram 用户名 |
| avatar_url | string | Telegram 头像 URL |
| auth_at | string | Telegram 授权时间 |
| updated_at | string | 绑定信息更新时间 |
当
bound=false时,仅返回bound字段。
5.5 绑定 Telegram
接口:POST /me/telegram/bind
认证:是
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 是 | Telegram 用户 ID |
| first_name | string | 否 | 名 |
| last_name | string | 否 | 姓 |
| username | string | 否 | Telegram 用户名 |
| photo_url | string | 否 | Telegram 头像 URL |
| auth_date | number | 是 | Telegram 授权时间戳(秒) |
| hash | string | 是 | Telegram 登录签名 |
请求示例
{
"id": 123456789,
"first_name": "Dujiao",
"last_name": "User",
"username": "dujiao_user",
"photo_url": "https://t.me/i/userpic/320/xxx.jpg",
"auth_date": 1739250000,
"hash": "f1b2c3..."
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"bound": true,
"provider": "telegram",
"provider_user_id": "123456789",
"username": "dujiao_user",
"avatar_url": "https://t.me/i/userpic/320/xxx.jpg",
"auth_at": "2026-02-20T12:00:00Z",
"updated_at": "2026-02-20T12:00:00Z"
}
}返回结构(data)
与 GET /me/telegram(已绑定)一致。
5.6 解绑 Telegram
接口:DELETE /me/telegram/unbind
认证:是
请求参数
无
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"unbound": true
}
}返回结构(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| unbound | boolean | 是否解绑成功 |
当用户尚未绑定真实邮箱(
email_change_mode=bind_only)时,不允许解绑 Telegram。
5.7 发送更换邮箱验证码
接口:POST /me/email/send-verify-code
认证:是
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| kind | string | 是 | old(发到旧邮箱)/ new(发到新邮箱) |
| new_email | string | 条件必填 | 当 kind=new 时必填 |
当
email_change_mode=bind_only时,kind=old不可用,请直接使用kind=new绑定真实邮箱。
请求示例
{
"kind": "new",
"new_email": "new@example.com"
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"sent": true
}
}返回结构(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| sent | boolean | 是否发送成功 |
5.8 更换邮箱
接口:POST /me/email/change
认证:是
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| new_email | string | 是 | 新邮箱 |
| old_code | string | 条件必填 | 当 email_change_mode=change_with_old_and_new 时必填;当 bind_only 时可不传(服务端忽略) |
| new_code | string | 是 | 新邮箱验证码 |
请求示例
{
"new_email": "new@example.com",
"old_code": "123456",
"new_code": "654321"
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"id": 101,
"email": "new@example.com",
"nickname": "user",
"email_verified_at": "2026-02-11T10:00:00Z",
"locale": "zh-CN",
"email_change_mode": "change_with_old_and_new",
"password_change_mode": "change_with_old"
}
}返回结构(data)
data:UserProfile
5.9 修改密码
接口:PUT /me/password
认证:是
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| old_password | string | 条件必填 | 当 password_change_mode=change_with_old 时必填;当 set_without_old 时可不传 |
| new_password | string | 是 | 新密码 |
首次通过 Telegram 自动创建且未设置密码的账号,
password_change_mode=set_without_old,仅需提交new_password。
请求示例
{
"old_password": "OldPass123",
"new_password": "NewPass123"
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"updated": true
}
}返回结构(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| updated | boolean | 是否更新成功 |
6. 登录用户订单与支付接口(需 Bearer Token)
6.1 订单金额预览
接口:POST /orders/preview
认证:是
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| items | OrderItemInput[] | 是 | 订单项 |
| coupon_code | string | 否 | 优惠码 |
| manual_form_data | object | 否 | 人工交付表单提交值(见通用结构) |
请求示例
{
"items": [
{
"product_id": 1001,
"quantity": 1,
"fulfillment_type": "manual"
}
],
"coupon_code": "SPRING2026",
"manual_form_data": {
"1001": {
"receiver_name": "张三",
"phone": "13277745648",
"address": "广东省深圳市南山区"
}
}
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"currency": "CNY",
"original_amount": "99.00",
"discount_amount": "10.00",
"promotion_discount_amount": "5.00",
"total_amount": "84.00",
"items": [
{
"product_id": 1001,
"title": { "zh-CN": "奈飞会员" },
"tags": ["热门"],
"unit_price": "99.00",
"quantity": 1,
"total_price": "99.00",
"coupon_discount_amount": "10.00",
"promotion_discount_amount": "5.00",
"fulfillment_type": "manual"
}
]
}
}返回结构(data)
data:OrderPreview
6.2 创建订单
接口:POST /orders
认证:是
Body 参数
与 POST /orders/preview 相同。
请求示例
{
"items": [
{
"product_id": 1001,
"quantity": 1,
"fulfillment_type": "manual"
}
],
"manual_form_data": {
"1001": {
"receiver_name": "张三",
"phone": "13277745648",
"address": "广东省深圳市南山区"
}
}
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"order_no": "DN202602110001",
"status": "pending_payment",
"currency": "CNY",
"original_amount": "99.00",
"discount_amount": "0.00",
"promotion_discount_amount": "0.00",
"total_amount": "99.00",
"expires_at": "2026-02-11T12:30:00Z",
"items": [
{
"title": { "zh-CN": "奈飞会员" },
"quantity": 1,
"unit_price": "99.00",
"total_price": "99.00",
"coupon_discount_amount": "0.00",
"promotion_discount_amount": "0.00",
"fulfillment_type": "manual",
"manual_form_schema_snapshot": {
"fields": [
{ "key": "receiver_name", "type": "text", "required": true }
]
},
"manual_form_submission": {
"receiver_name": "张三"
}
}
]
}
}返回结构(data)
data:Order
6.3 订单列表
接口:GET /orders
认证:是
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | number | 否 | 页码 |
| page_size | number | 否 | 每页条数 |
| status | string | 否 | 状态过滤(见 Order.status 枚举) |
| order_no | string | 否 | 订单号模糊查询 |
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": [
{
"order_no": "DN202602110001",
"status": "pending_payment",
"currency": "CNY",
"total_amount": "99.00",
"created_at": "2026-02-11T12:00:00Z",
"items": [
{
"title": { "zh-CN": "奈飞会员" },
"quantity": 1,
"unit_price": "99.00",
"total_price": "99.00",
"coupon_discount_amount": "0.00",
"promotion_discount_amount": "0.00",
"fulfillment_type": "manual",
"manual_form_schema_snapshot": {},
"manual_form_submission": {}
}
]
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 1,
"total_page": 1
}
}返回结构(data)
data:Order[]pagination:分页对象
6.4 订单详情
接口:GET /orders/:order_no
认证:是
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| order_no | string | 是 | 订单号 |
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"order_no": "DN202602110001",
"status": "pending_payment",
"currency": "CNY",
"total_amount": "99.00",
"items": [
{
"title": { "zh-CN": "奈飞会员" },
"quantity": 1,
"unit_price": "99.00",
"total_price": "99.00",
"coupon_discount_amount": "0.00",
"promotion_discount_amount": "0.00",
"fulfillment_type": "manual",
"manual_form_schema_snapshot": {},
"manual_form_submission": {}
}
],
"fulfillment": null,
"children": []
}
}返回结构(data)
data:Order
6.5 取消订单
接口:POST /orders/:order_no/cancel
认证:是
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| order_no | string | 是 | 订单号 |
Body 参数
无
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"order_no": "DN202602110001",
"status": "canceled",
"currency": "CNY",
"total_amount": "99.00",
"canceled_at": "2026-02-11T12:10:00Z"
}
}返回结构(data)
data:Order
6.6 创建支付单
接口:POST /payments
认证:是
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| order_no | string | 是 | 订单号 |
| channel_id | number | 是 | 支付渠道 ID |
请求示例
{
"order_no": "DN202602110001",
"channel_id": 10
}成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"payment_id": 3001,
"provider_type": "official",
"channel_type": "alipay",
"interaction_mode": "page",
"pay_url": "https://openapi.alipay.com/gateway.do?...",
"qr_code": "",
"expires_at": "2026-02-11T12:30:00Z"
}
}返回结构(data)
data:PaymentLaunch(创建支付时通常不含order_no/channel_id字段)
6.7 捕获支付结果
接口:POST /payments/:id/capture
认证:是
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 是 | 支付记录 ID |
Body 参数
无
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"payment_id": 3001,
"status": "success"
}
}返回结构(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| payment_id | number | 支付记录 ID |
| status | string | 支付状态:initiated / pending / success / failed / expired |
6.8 获取最新待支付记录
接口:GET /payments/latest
认证:是
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| order_no | string | 是 | 订单号 |
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"payment_id": 3001,
"order_no": "DN202602110001",
"channel_id": 10,
"provider_type": "official",
"channel_type": "alipay",
"interaction_mode": "page",
"pay_url": "https://openapi.alipay.com/gateway.do?...",
"qr_code": "",
"expires_at": "2026-02-11T12:30:00Z"
}
}返回结构(data)
data:PaymentLaunch
7. 游客订单与支付接口
游客订单访问凭证为:
email + order_password。
7.1 游客订单预览
接口:POST /guest/orders/preview
认证:否
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
| items | OrderItemInput[] | 是 | 订单项 |
| coupon_code | string | 否 | 优惠码 |
| manual_form_data | object | 否 | 人工交付表单提交值 |
| captcha_payload | object | 否 | 验证码参数(当前预览不会校验,可忽略) |
请求示例
{
"email": "guest@example.com",
"order_password": "guest-pass",
"items": [
{
"product_id": 1001,
"quantity": 1,
"fulfillment_type": "manual"
}
],
"manual_form_data": {
"1001": {
"receiver_name": "张三",
"phone": "13277745648",
"address": "广东省深圳市南山区"
}
}
}成功响应示例
与 POST /orders/preview 一致。
返回结构(data)
data:OrderPreview
7.2 游客创建订单
接口:POST /guest/orders
认证:否
Body 参数
与 POST /guest/orders/preview 相同。
请求示例
{
"email": "guest@example.com",
"order_password": "guest-pass",
"items": [
{
"product_id": 1001,
"quantity": 1,
"fulfillment_type": "manual"
}
],
"manual_form_data": {
"1001": {
"receiver_name": "张三",
"phone": "13277745648",
"address": "广东省深圳市南山区"
}
},
"captcha_payload": {
"captcha_id": "abc",
"captcha_code": "x7g5",
"turnstile_token": ""
}
}成功响应示例
与 POST /orders 一致(游客单的 user_id=0,guest_email 有值)。
返回结构(data)
data:Order
7.3 游客订单列表
接口:GET /guest/orders
认证:否
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
| order_no | string | 否 | 订单号,传入时按单号查询并返回 0/1 条 |
| page | number | 否 | 页码 |
| page_size | number | 否 | 每页条数 |
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": [
{
"order_no": "DN202602110002",
"guest_email": "guest@example.com",
"status": "pending_payment",
"currency": "CNY",
"total_amount": "99.00",
"items": [
{
"title": { "zh-CN": "奈飞会员" },
"quantity": 1,
"unit_price": "99.00",
"total_price": "99.00",
"coupon_discount_amount": "0.00",
"promotion_discount_amount": "0.00",
"fulfillment_type": "manual",
"manual_form_schema_snapshot": {},
"manual_form_submission": {}
}
]
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 1,
"total_page": 1
}
}返回结构(data)
data:Order[]pagination:分页对象
7.4 游客订单详情
接口:GET /guest/orders/:order_no
认证:否
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| order_no | string | 是 | 订单号 |
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
成功响应示例
与用户订单详情结构一致。
返回结构(data)
data:Order
7.5 游客创建支付单
接口:POST /guest/payments
认证:否
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
| order_no | string | 是 | 订单号 |
| channel_id | number | 是 | 支付渠道 ID |
请求示例
{
"email": "guest@example.com",
"order_password": "guest-pass",
"order_no": "DN202602110002",
"channel_id": 10
}成功响应示例
与 POST /payments 返回结构一致。
返回结构(data)
data:PaymentLaunch
7.6 游客捕获支付结果
接口:POST /guest/payments/:id/capture
认证:否
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 是 | 支付记录 ID |
Body 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
请求示例
{
"email": "guest@example.com",
"order_password": "guest-pass"
}成功响应示例
与 POST /payments/:id/capture 一致。
返回结构(data)
| 字段 | 类型 | 说明 |
|---|---|---|
| payment_id | number | 支付记录 ID |
| status | string | 支付状态 |
7.7 游客获取最新待支付记录
接口:GET /guest/payments/latest
认证:否
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
| order_no | string | 是 | 订单号 |
成功响应示例
与 GET /payments/latest 一致。
返回结构(data)
data:PaymentLaunch
8. 前台接入建议
8.1 订单接口统一使用 order_no
所有面向用户的订单接口均使用 order_no 作为标识符,不再暴露自增 ID:
GET /orders/:order_no— 订单详情POST /orders/:order_no/cancel— 取消订单GET /orders/:order_no/fulfillment/download— 下载交付内容GET /guest/orders/:order_no— 游客订单详情GET /guest/orders/:order_no/fulfillment/download— 游客下载交付内容POST /payments、GET /payments/latest— 使用order_no参数
8.2 统一错误处理
前端必须同时判断:
- HTTP 状态(网络层)
status_code(业务层)
当 status_code != 0 时,请读取 msg 提示并记录 data.request_id 便于排查。
8.3 支付成功页与轮询
建议支付流程组合使用:
- 发起支付后跳转
pay_url或展示qr_code - 支付完成回跳后,调用
capture - 再调用
latest兜底轮询确认
可显著降低“已支付但页面未及时更新”的感知问题。
9. 非前台主动调用接口(说明)
9.1 支付平台回调接口
以下回调接口一般由支付平台服务器调用,前台模板无需主动请求:
POST /payments/callbackGET /payments/callbackPOST /payments/webhook/paypalPOST /payments/webhook/stripe
9.2 管理后台 Telegram 登录配置接口(Admin)
以下接口由管理后台调用,不属于前台用户侧接口:
9.2.1 获取 Telegram 登录配置
接口:GET /admin/settings/telegram-auth
认证:管理员 Token
成功响应示例
{
"status_code": 0,
"msg": "success",
"data": {
"enabled": true,
"bot_username": "dujiao_auth_bot",
"bot_token": "",
"has_bot_token": true,
"login_expire_seconds": 300,
"replay_ttl_seconds": 300
}
}返回中的
bot_token始终为脱敏空串,是否已配置通过has_bot_token判断。
9.2.2 更新 Telegram 登录配置
接口:PUT /admin/settings/telegram-auth
认证:管理员 Token
Body 参数(Patch)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| enabled | boolean | 否 | 是否启用 Telegram 登录 |
| bot_username | string | 否 | Telegram Bot 用户名(不带 @) |
| bot_token | string | 否 | Telegram Bot Token(传空串不会覆盖已有值) |
| login_expire_seconds | number | 否 | 登录有效期(30-86400 秒) |
| replay_ttl_seconds | number | 否 | 重放保护时长(60-86400 秒) |
请求示例
{
"enabled": true,
"bot_username": "dujiao_auth_bot",
"bot_token": "123456:ABCDEF",
"login_expire_seconds": 300,
"replay_ttl_seconds": 300
}成功响应
返回结构与 GET /admin/settings/telegram-auth 一致(脱敏)。
9.3 管理后台商品多 SKU 接口(Admin)
以下接口由管理后台调用,不属于前台用户侧接口。
9.3.1 创建商品(支持多 SKU)
接口:POST /admin/products
认证:管理员 Token
Body 关键参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| category_id | number | 是 | 分类 ID |
| slug | string | 是 | 商品唯一标识 |
| title | object | 是 | 多语言标题 |
| price_amount | number | 是 | 单规格模式下商品价格;多 SKU 时建议传 0 或任意占位值,最终以后端 SKU 计算为准 |
| fulfillment_type | string | 否 | manual / auto |
| manual_stock_total | number | 否 | 单规格模式下人工库存总量 |
| skus | array | 否 | 多 SKU 数组,传空或不传表示单规格模式 |
skus[] 字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 否 | 更新已有 SKU 时传;创建新 SKU 可不传 |
| sku_code | string | 是 | SKU 编码(同商品内唯一) |
| spec_values | object | 否 | 规格展示文案(如多语言 {"zh-CN":"标准版","en-US":"Standard"}) |
| price_amount | number | 是 | SKU 价格(必须大于 0) |
| manual_stock_total | number | 否 | SKU 人工库存(手动交付模式下生效) |
| is_active | boolean | 否 | 是否启用,默认 true |
| sort_order | number | 否 | 排序权重,默认 0;数值越大越靠前 |
请求示例(单规格兼容)
{
"category_id": 1,
"slug": "vpn-monthly",
"title": {
"zh-CN": "VPN 月付",
"zh-TW": "VPN 月付",
"en-US": "VPN Monthly"
},
"price_amount": 29.9,
"fulfillment_type": "manual",
"manual_stock_total": 100
}请求示例(多 SKU)
{
"category_id": 1,
"slug": "vpn-subscription",
"title": {
"zh-CN": "VPN 订阅",
"zh-TW": "VPN 訂閱",
"en-US": "VPN Subscription"
},
"price_amount": 0,
"fulfillment_type": "manual",
"skus": [
{
"sku_code": "STANDARD",
"spec_values": {
"zh-CN": "标准版",
"zh-TW": "標準版",
"en-US": "Standard"
},
"price_amount": 29.9,
"manual_stock_total": 100,
"is_active": true,
"sort_order": 10
},
{
"sku_code": "PRO",
"spec_values": {
"zh-CN": "专业版",
"zh-TW": "專業版",
"en-US": "Pro"
},
"price_amount": 49.9,
"manual_stock_total": 80,
"is_active": true,
"sort_order": 20
}
]
}9.3.2 更新商品(支持多 SKU)
接口:PUT /admin/products/:id
认证:管理员 Token
请求结构与 POST /admin/products 一致;若要更新已有 SKU,请在 skus[] 中传对应 id。
9.3.3 后端处理规则
- 当
skus非空时:- 商品展示价自动取“启用 SKU 中的最低价”;
- 若为人工交付,商品人工库存总量自动汇总“启用 SKU 的
manual_stock_total”。
- 当
skus为空时:- 按历史单规格模式处理,价格与库存使用商品本身字段。
9.3.4 管理后台操作指引
- 进入后台
商品管理,新建或编辑商品。 - 在“SKU 规格配置”区域新增一个或多个 SKU,填写编码、规格文案、价格、库存、状态与排序。
- 若已配置 SKU,商品“价格/人工库存总量”字段仅作展示参考,实际以 SKU 数据为准。
- 保存后可在前台商品详情页按 SKU 展示与下单。
9.4 推广返利接口(Affiliate)
以下接口对应前台返利中心与后台返利审核能力。
9.4.1 下单接口新增 affiliate_code 字段
以下接口的请求体支持附带 affiliate_code(联盟ID):
POST /orders/previewPOST /ordersPOST /guest/orders/previewPOST /guest/orders
字段定义:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| affiliate_code | string | 否 | 推广联盟ID(如 AB12CD34),用于订单返利归因 |
9.4.2 公开点击上报
接口:POST /public/affiliate/click
认证:无需登录
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| affiliate_code | string | 是 | 联盟ID |
| visitor_key | string | 否 | 访客标识(前端可持久化) |
| landing_path | string | 否 | 落地路径(如 /?aff=AB12CD34) |
| referrer | string | 否 | 来源页 URL |
成功返回:
{
"status_code": 0,
"msg": "success",
"data": {
"ok": true
}
}9.4.3 用户返利中心接口(需 Bearer Token)
A) 开通返利
- 接口:
POST /affiliate/open - 说明:开通成功后返回返利档案(含联盟ID)。
B) 获取返利看板
- 接口:
GET /affiliate/dashboard
返回 data 关键字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| opened | boolean | 是否已开通 |
| affiliate_code | string | 联盟ID |
| promotion_path | string | 推广路径(如 /?aff=AB12CD34) |
| click_count | number | 点击数 |
| valid_order_count | number | 有效订单数 |
| conversion_rate | number | 转化率(百分比数值) |
| pending_commission | string | 待确认佣金 |
| available_commission | string | 可提现佣金 |
| withdrawn_commission | string | 已提现佣金 |
C) 查询我的佣金记录
- 接口:
GET /affiliate/commissions - 参数:
page、page_size、status - status 可选值:
pending_confirm/available/rejected/withdrawn
D) 查询我的提现记录
- 接口:
GET /affiliate/withdraws - 参数:
page、page_size、status - status 可选值:
pending_review/rejected/paid
E) 申请提现
- 接口:
POST /affiliate/withdraws
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| amount | string | 是 | 提现金额(字符串金额,保留 2 位小数) |
| channel | string | 是 | 提现渠道 |
| account | string | 是 | 提现账号 |
9.4.4 管理后台返利设置(Admin)
A) 获取返利设置
- 接口:
GET /admin/settings/affiliate - 认证:管理员 Token
B) 更新返利设置
- 接口:
PUT /admin/settings/affiliate - 认证:管理员 Token
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| enabled | boolean | 是 | 是否开启返利 |
| commission_rate | number | 是 | 返利比例(0-100,支持 2 位小数) |
| confirm_days | number | 是 | 佣金确认天数(0-3650) |
| min_withdraw_amount | number | 是 | 最低提现金额(>=0) |
| withdraw_channels | string[] | 是 | 提现渠道列表 |
9.4.5 管理后台返利管理(Admin)
以下接口均需管理员 Token:
| 接口 | 说明 |
|---|---|
GET /admin/affiliates/users | 返利用户列表 |
GET /admin/affiliates/commissions | 佣金记录列表 |
GET /admin/affiliates/withdraws | 提现申请列表 |
POST /admin/affiliates/withdraws/:id/reject | 拒绝提现申请 |
POST /admin/affiliates/withdraws/:id/pay | 标记提现已打款 |
其中:
POST /admin/affiliates/withdraws/:id/rejectbody 支持{ "reason": "拒绝原因" }POST /admin/affiliates/withdraws/:id/pay无需额外 body 字段
