2071 lines
96 KiB
Markdown
2071 lines
96 KiB
Markdown
# Backend API 文档
|
||
|
||
生成日期:2026-05-08
|
||
生成依据:`backend/routes/api.php`、`php artisan route:list --path=api`、`app/Http/Controllers/Api/**`、`app/Services/**`
|
||
|
||
本文档是当前 backend API 的唯一维护入口。`backend/docs` 下的旧 API 文档已移除,后续接口变更统一更新本文件。
|
||
|
||
本文档只覆盖当前 `backend` 暴露的 API 路由,共 43 个路由入口。Laravel 自动支持的 `HEAD` 不单独列出。
|
||
|
||
## 通用约定
|
||
|
||
### Base URL
|
||
|
||
生产或本地部署时以实际后端域名为准,下文统一写作:
|
||
|
||
```text
|
||
{BACKEND_API_BASE_URL}/api
|
||
```
|
||
|
||
本地默认通常是:
|
||
|
||
```text
|
||
http://127.0.0.1:8000/api
|
||
```
|
||
|
||
### 请求头
|
||
|
||
| 请求头 | 适用接口 | 说明 |
|
||
|---------------------------------------------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||
| `Content-Type: application/json` | 有 JSON body 的接口 | 请求体使用 JSON。 |
|
||
| `Authorization: Bearer <sanctum_token>` | Dashboard / 当前用户接口 | Laravel Sanctum Web 用户 token。邮箱已验证账号可由 `/api/auth/login` 返回;邮箱未验证账号必须先通过 `/api/auth/email/verify-code`,验证码校验成功后才返回 token;重置密码仅在账号邮箱已验证时返回 token。Dashboard 路由会拒绝扩展 token。 |
|
||
| `Authorization: Bearer <extension_sanctum_token>` | Chrome 扩展接口 | 扩展专用 Sanctum token,由 `/api/auth/extension-pair` 或 `/api/auth/refresh-extension-token` 返回;后端会校验 `storeai_extension` token name 和 `extension:*` ability。扩展 token 只能访问扩展接口和 `/api/auth/logout`。 |
|
||
| `X-Ext-Version: <version>` | 扩展扫描相关接口 | 用于扩展版本门禁,低于 `MIN_EXT_VERSION` 会返回 `ext_outdated`。 |
|
||
| `Authorization: Bearer <CRON_SECRET>` | Cron 接口 | 配置 `CRON_SECRET` 后必填;生产环境未配置会返回 `misconfigured`。 |
|
||
| `Stripe-Signature: <signature>` | Stripe webhook | Stripe webhook 原始 body 签名。 |
|
||
| `X-Telegram-Bot-Api-Secret-Token: <secret>` | Telegram webhook | Telegram webhook secret token。 |
|
||
|
||
### 响应格式
|
||
|
||
所有 backend API 统一返回三元素 envelope。前端已确定重写,不再保留旧 `{ ok: ... }` 顶层兼容格式。
|
||
|
||
```json
|
||
{
|
||
"code": 1,
|
||
"message": "ok",
|
||
"data": {}
|
||
}
|
||
```
|
||
|
||
失败响应的顶层 `code` 放整型 status,旧接口字符串错误码统一放入 `data.apiCode`;异常响应不改变实际 HTTP status code,HTTP
|
||
层保持 200:
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "Validation failed.",
|
||
"data": {
|
||
"apiCode": "invalid_payload"
|
||
}
|
||
}
|
||
```
|
||
|
||
部分错误会附带额外字段:
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "schema validation failed",
|
||
"data": {
|
||
"apiCode": "invalid_payload",
|
||
"details": {
|
||
"fieldErrors": {
|
||
"payload.today.orders": ["must be an integer"]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
空业务数据返回空数组或空对象,取决于控制器业务返回值;客户端只应依赖外层 `code/message/data`。
|
||
|
||
```json
|
||
{
|
||
"code": 1,
|
||
"message": "ok",
|
||
"data": {}
|
||
}
|
||
```
|
||
|
||
### 通用错误
|
||
|
||
下表的 `响应 code` 指 JSON 响应体里的 `code` 字段,不是 HTTP status code;异常响应的 HTTP status 保持 200。
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------------------------------------------------------------|-----------------------------|
|
||
| 400 | `invalid_payload` / `invalid_email` / `invalid_code` | 请求参数格式错误。 |
|
||
| 401 | `unauthorized` | 缺少 token、token 无效、签名错误或未登录。 |
|
||
| 402 | `subscription_required` | 当前订阅状态不允许扫描。 |
|
||
| 404 | `not_found` | 资源不存在、无权访问或 debug 门禁关闭。 |
|
||
| 409 | `platform_account_mismatch` / `subscription_active` / `agreement_stale` | 业务状态冲突。 |
|
||
| 426 | `ext_outdated` | Chrome 扩展版本低于后端要求。 |
|
||
| 429 | `too_many_requests` / `rate_limited` | 请求过于频繁或手动扫描达到日限额。 |
|
||
| 500 | `server_error` / `config_error` / `stripe_error` / `handler_error` | 服务端错误、配置缺失或第三方调用失败。 |
|
||
| 503 | `misconfigured` | 生产环境缺少必需配置。 |
|
||
|
||
## 公共对象
|
||
|
||
### `User`
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------------|--------------|------------------|
|
||
| `id` | string | Laravel 用户 UUID。 |
|
||
| `email` | string | 邮箱。 |
|
||
| `name` | string\|null | 用户名,可为空。 |
|
||
| `emailVerifiedAt` | string\|null | 邮箱验证时间,ISO 字符串。 |
|
||
|
||
### `ApiUser`
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---------|--------------|--------------|
|
||
| `id` | string | 当前登录用户 UUID。 |
|
||
| `email` | string\|null | 当前登录用户邮箱。 |
|
||
|
||
### `BrandCore`
|
||
|
||
`BrandCore` 是 Dashboard 读接口返回的品牌基础对象。不同接口会按页面需要裁剪或追加字段。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|--------------------------|---------------|-------------------------------------------------|
|
||
| `id` | string | 品牌 UUID。 |
|
||
| `name` | string | 品牌名称。 |
|
||
| `timezone` | string\|null | 品牌时区,例如 `Asia/Kuala_Lumpur`。 |
|
||
| `morning_brief_hour` | integer\|null | 早报本地小时,0-23。 |
|
||
| `evening_recap_hour` | integer\|null | 晚报本地小时,0-23。 |
|
||
| `last_morning_brief_at` | string\|null | 最近早报发送时间。 |
|
||
| `last_evening_recap_at` | string\|null | 最近晚报发送时间。 |
|
||
| `subscription_status` | string\|null | 订阅状态,例如 `trial`、`active`、`past_due`、`canceled`。 |
|
||
| `trial_ends_at` | string\|null | 试用结束时间。 |
|
||
| `subscription_cancel_at` | string\|null | 订阅预约取消时间。 |
|
||
| `is_comp` | boolean | 是否内部赠送账号。 |
|
||
| `telegram_chat_id` | string\|null | Telegram chat id。 |
|
||
| `platform_account_id` | string\|null | 已绑定 Shopee seller id。 |
|
||
| `created_at` | string\|null | 创建时间。 |
|
||
| `last_extension_version` | string\|null | 最近扩展心跳版本。 |
|
||
| `stores` | array | 店铺列表,元素为 `{ id: string }`。 |
|
||
|
||
#### `subscription_status` 枚举
|
||
|
||
`subscription_status` 是 `brands` 表保存的原始账单状态;前端判断“是否可用 / 是否到期”时还要结合 `is_comp`、`trial_ends_at`
|
||
和 `subscription_cancel_at`。
|
||
|
||
| 值 | 含义 | 到期时间口径 |
|
||
|--------------|------------------------------------------------|---------------------------------------------------------------------------------------|
|
||
| `trial` | 试用中。创建首个品牌时后端写入 `trial_ends_at = now + 1 day`。 | 使用 `trial_ends_at` 判断试用到期;为空时按试用可用但无倒计时时间处理。 |
|
||
| `active` | Stripe 订阅有效。 | `subscription_cancel_at` 为空表示当前无已知到期时间;若为未来时间,表示已预约取消,付费权益到 `subscription_cancel_at`。 |
|
||
| `past_due` | Stripe 付款异常或 `unpaid`。 | 当前 API 不给确定到期时间;Stripe 后续重试失败并发送 deleted webhook 后会变为 `canceled`。 |
|
||
| `canceled` | 订阅已取消或 Stripe `incomplete_expired`。 | 已到期,不再使用未来到期时间;后端会清空 `subscription_cancel_at`。 |
|
||
| `null` / 其它值 | 旧数据或异常数据。 | 按无有效订阅处理。 |
|
||
|
||
有效订阅状态的派生规则:
|
||
|
||
| 条件 | 派生状态 | 扫描权限 |
|
||
|-----------------------------------------------------------------------|--------------------|------------------|
|
||
| `is_comp = true` | `comp` | 允许,内部赠送账号优先级最高。 |
|
||
| `subscription_status = trial` 且 `trial_ends_at` 为空或晚于当前 UTC 时间 | `trial_active` | 允许。 |
|
||
| `subscription_status = trial` 且 `trial_ends_at` 已过期或无法解析 | `trial_expired` | 不允许。 |
|
||
| `subscription_status = active` 且 `subscription_cancel_at` 为空 | `active` | 允许。 |
|
||
| `subscription_status = active` 且 `subscription_cancel_at` 晚于当前 UTC 时间 | `active_canceling` | 允许,展示“已预约取消”。 |
|
||
| `subscription_status = active` 且 `subscription_cancel_at` 已过期或无法解析 | `canceled` | 不允许。 |
|
||
| `subscription_status = past_due` | `past_due` | 允许,但应提示用户更新付款方式。 |
|
||
| `subscription_status = canceled` | `canceled` | 不允许。 |
|
||
|
||
如果前端要展示“订阅到期时间”,优先按以下顺序取值:`is_comp=true` 时无到期时间;`trial` 取 `trial_ends_at`;`active` 且已预约取消时取
|
||
`subscription_cancel_at`;普通 `active` 当前没有到期时间字段,因为后端暂未保存 Stripe `current_period_end`。
|
||
|
||
### `ScanRow`
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------------------|---------------|----------------------------|
|
||
| `id` | string | scan UUID。 |
|
||
| `scanned_at` | string | 扫描时间。 |
|
||
| `status` | string | `ok`、`partial` 或 `failed`。 |
|
||
| `error` | string\|null | 扩展提取错误文本。 |
|
||
| `payload` | object\|null | 扫描原始 payload。 |
|
||
| `telegram_message_id` | integer\|null | 手动扫描 Telegram 回声消息 id。 |
|
||
|
||
### `AlertRow`
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------|---------|---------------------|
|
||
| `id` | string | alert UUID。 |
|
||
| `rule_id` | string | 规则 ID。 |
|
||
| `priority` | string | 告警优先级,例如 `P1`、`P2`。 |
|
||
| `confidence` | string | 置信度。 |
|
||
| `title` | string | 标题。 |
|
||
| `diagnosis` | string | 诊断文案。 |
|
||
| `action` | string | 建议动作。 |
|
||
| `data` | object | 告警附加 JSON。 |
|
||
| `acknowledged` | boolean | 是否已确认。 |
|
||
| `created_at` | string | 创建时间。 |
|
||
|
||
### `StoreBrief`
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------------------|--------------|-----------------|
|
||
| `verdict_tier` | string\|null | AI 今日结论等级。 |
|
||
| `verdict_headline` | string\|null | AI 今日结论标题。 |
|
||
| `verdict_factors` | array | AI 结论因素。 |
|
||
| `verdict_action` | string\|null | AI 建议行动。 |
|
||
| `bottleneck_stage` | string\|null | 增长瓶颈阶段。 |
|
||
| `growth_diagnosis_json` | object\|null | AI 增长诊断 JSON。 |
|
||
| `top_actions_json` | array\|null | AI Top Actions。 |
|
||
| `confidence` | object | AI 置信度 JSON。 |
|
||
|
||
## 系统接口
|
||
|
||
### GET `/api/health`
|
||
|
||
鉴权:无。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/health"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|--------------------|--------------|--------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.service` | string | 固定为 `storeai-api`。 |
|
||
| `data.version` | string\|null | `config('app.version')`。 |
|
||
| `data.environment` | string | 当前 Laravel 环境。 |
|
||
|
||
## 认证接口
|
||
|
||
### POST `/api/auth/register`
|
||
|
||
鉴权:无。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|------------|--------------|----|-----------------------|
|
||
| body | `email` | string | 是 | 合法邮箱,服务端会转小写并 trim。 |
|
||
| body | `password` | string | 是 | 8-128 字符。 |
|
||
| body | `name` | string\|null | 否 | 最多 120 字符,空字符串按 null。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/auth/register" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"email": "owner@example.test",
|
||
"password": "secure-password",
|
||
"name": "Store Owner"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------------------------|---------|----------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.requiresEmailVerification` | boolean | 固定为 `true`,表示必须先完成邮箱验证码校验。 |
|
||
| `data.email` | string | 标准化后的邮箱。 |
|
||
| `data.user` | `User` | 新建用户。 |
|
||
| `data.sent` | boolean | 是否已发送邮箱验证码。 |
|
||
| `data.expiresInSeconds` | integer | 验证码剩余有效秒数。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|------------|
|
||
| 400 | `invalid_payload` | 邮箱或密码格式错误。 |
|
||
| 400 | `duplicate_email` | 邮箱已注册。 |
|
||
|
||
备注:注册成功后会发送 6 位邮箱验证码,但不会签发登录 token;只有 `/api/auth/email/verify-code` 校验成功后才会签发 Web
|
||
Sanctum token。`users.id` 同时作为业务 `account_id` 使用。
|
||
|
||
### POST `/api/auth/login`
|
||
|
||
鉴权:无。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|------------|--------|----|-----------|
|
||
| body | `email` | string | 是 | 合法邮箱。 |
|
||
| body | `password` | string | 是 | 8-128 字符。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/auth/login" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"email": "owner@example.test",
|
||
"password": "secure-password"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------------------------|---------|---------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.user` | `User` | 登录用户。邮箱未验证时也会返回,`emailVerifiedAt` 为 `null`。 |
|
||
| `data.token` | string | 邮箱已验证时返回的新签发 Sanctum Bearer token。 |
|
||
| `data.requiresEmailVerification` | boolean | 邮箱未验证时返回 `true`。 |
|
||
| `data.email` | string | 邮箱未验证时返回的标准化邮箱。 |
|
||
| `data.sent` | boolean | 邮箱未验证时表示是否已发送新的验证码。 |
|
||
| `data.expiresInSeconds` | integer | 新验证码剩余有效秒数。 |
|
||
| `data.retryAfterSeconds` | integer | 验证码重发限流时返回。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-----------------------|----------|
|
||
| 400 | `invalid_payload` | 入参格式错误。 |
|
||
| 401 | `invalid_credentials` | 邮箱或密码错误。 |
|
||
|
||
备注:邮箱已验证时,登录成功会先删除当前账号所有旧 Sanctum token,包括旧 Web token 和扩展 token,再签发新的 Web
|
||
token;扩展需要重新配对。邮箱未验证时不会返回 token,会清理该账号旧 token 并返回 `requiresEmailVerification=true`
|
||
,前端应进入邮箱验证码流程。
|
||
|
||
### GET `/api/auth/me`
|
||
|
||
鉴权:Sanctum。仅 Web/Dashboard token 可调用,扩展 token 会被 `dashboard.token` 门禁拒绝。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/auth/me" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------|--------------|----------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.user` | `User`\|null | 当前 token 对应用户。 |
|
||
|
||
### POST `/api/auth/logout`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/auth/logout" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------|---------|-----------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
|
||
备注:退出登录会删除当前账号所有 Sanctum token,包括 Web token 和扩展 token,让 Dashboard 和 Chrome 扩展同时下线。
|
||
|
||
### POST `/api/auth/rotate-session`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/auth/rotate-session" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------|---------|--------------------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.rotated` | boolean | 固定为 `false`。 |
|
||
| `data.reason` | string | 固定为 `sanctum_bearer_tokens_are_already_device_scoped`。 |
|
||
|
||
备注:兼容 Origin 的 Supabase session rotate 调用;Sanctum 下是 no-op。
|
||
|
||
### PATCH `/api/auth/password`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|------------|--------|----|---------------|
|
||
| body | `password` | string | 是 | 新密码,8-128 字符。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X PATCH "{BACKEND_API_BASE_URL}/api/auth/password" \
|
||
-H "Authorization: Bearer <sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"password": "new-secure-password"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------|---------|-----------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|----------|
|
||
| 400 | `invalid_payload` | 密码长度不合法。 |
|
||
|
||
### GET `/api/auth/check-verified`
|
||
|
||
鉴权:无。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|-------|---------|--------|----|-------------|
|
||
| query | `email` | string | 是 | 要查询验证状态的邮箱。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/auth/check-verified?email=owner%40example.test"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------------|---------|---------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.verified` | boolean | `users.email_verified_at` 是否非空。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-----------------|---------|
|
||
| 400 | `invalid_email` | 邮箱格式错误。 |
|
||
|
||
备注:响应头包含 `cache-control: no-store`。
|
||
|
||
### POST `/api/auth/email/verification-code`
|
||
|
||
鉴权:无。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|---------|--------|----|------------|
|
||
| body | `email` | string | 是 | 要发送验证码的邮箱。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/auth/email/verification-code" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"email": "owner@example.test"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------------------|---------|---------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.sent` | boolean | 是否触发发送。未知邮箱也返回 `true` 以防账号枚举。 |
|
||
| `data.alreadyVerified` | boolean | 已验证邮箱返回 `true`,此时 `sent=false`。 |
|
||
| `data.expiresInSeconds` | integer | 验证码剩余有效秒数,实际发送时返回。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|---------------------|---------------------------------|
|
||
| 400 | `invalid_email` | 邮箱格式错误。 |
|
||
| 429 | `too_many_requests` | 发送过于频繁,响应含 `retryAfterSeconds`。 |
|
||
|
||
### POST `/api/auth/email/verify-code`
|
||
|
||
鉴权:无。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|---------|--------|----|-----------|
|
||
| body | `email` | string | 是 | 邮箱。 |
|
||
| body | `code` | string | 是 | 6 位数字验证码。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/auth/email/verify-code" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"email": "owner@example.test",
|
||
"code": "123456"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------------|---------|---------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.verified` | boolean | 成功为 `true`。 |
|
||
| `data.user` | `User` | 已验证用户。 |
|
||
| `data.token` | string | 新签发 Sanctum Bearer token。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-----------------|-----------------------------|
|
||
| 400 | `invalid_email` | 邮箱格式错误。 |
|
||
| 400 | `invalid_code` | 验证码格式错误、邮箱不存在、验证码不存在或验证码错误。 |
|
||
| 400 | `expired_code` | 验证码已过期。 |
|
||
|
||
备注:校验成功等价于一次 Web 登录,会删除当前账号所有旧 Web/扩展 token,并签发新 Web token。
|
||
|
||
### POST `/api/auth/password/forgot`
|
||
|
||
鉴权:无。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|---------|--------|----|-----------|
|
||
| body | `email` | string | 是 | 要重置密码的邮箱。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/auth/password/forgot" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"email": "owner@example.test"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------------------|---------|-----------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.sent` | boolean | 固定按成功处理;未知邮箱不发信但仍返回 `true`。 |
|
||
| `data.expiresInSeconds` | integer | 验证码有效秒数,实际发信时返回。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|---------------------|---------------------------------|
|
||
| 400 | `invalid_email` | 邮箱格式错误。 |
|
||
| 429 | `too_many_requests` | 发送过于频繁,响应含 `retryAfterSeconds`。 |
|
||
|
||
### POST `/api/auth/password/reset`
|
||
|
||
鉴权:无。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|------------|--------|----|---------------|
|
||
| body | `email` | string | 是 | 邮箱。 |
|
||
| body | `code` | string | 是 | 6 位数字验证码。 |
|
||
| body | `password` | string | 是 | 新密码,8-128 字符。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/auth/password/reset" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"email": "owner@example.test",
|
||
"code": "123456",
|
||
"password": "new-secure-password"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------------------------|---------|------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.reset` | boolean | 成功为 `true`。 |
|
||
| `data.user` | `User` | 重置密码后的用户。 |
|
||
| `data.token` | string | 邮箱已验证时返回的新签发 Sanctum Bearer token。 |
|
||
| `data.requiresEmailVerification` | boolean | 账号邮箱未验证时返回 `true`,此时不返回 `token`。 |
|
||
| `data.email` | string | 账号邮箱未验证时返回的标准化邮箱。 |
|
||
| `data.sent` | boolean | 账号邮箱未验证时表示是否已发送邮箱验证验证码。 |
|
||
| `data.expiresInSeconds` | integer | 邮箱验证验证码剩余有效秒数。 |
|
||
| `data.retryAfterSeconds` | integer | 邮箱验证验证码重发限流时返回。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|-----------------------------|
|
||
| 400 | `invalid_email` | 邮箱格式错误。 |
|
||
| 400 | `invalid_code` | 验证码格式错误、邮箱不存在、验证码不存在或验证码错误。 |
|
||
| 400 | `expired_code` | 验证码已过期。 |
|
||
| 400 | `invalid_payload` | 新密码不符合长度要求。 |
|
||
|
||
备注:成功后会消费该邮箱所有未使用重置码。若账号邮箱已验证,会删除当前账号所有旧 Web/扩展 token,并签发新 Web
|
||
token;若账号邮箱未验证,只更新密码、清理旧 token、发送邮箱验证验证码并返回 `requiresEmailVerification=true`。
|
||
|
||
### POST `/api/auth/extension-pair`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|---------|--------|----|---------------------|
|
||
| body | `label` | string | 否 | 扩展设备标签,最多保留 120 字符。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/auth/extension-pair" \
|
||
-H "Authorization: Bearer <sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"label": "Chrome - Store Owner Laptop"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------------------------------|--------------|---------------------------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.token` | string | 扩展专用 Sanctum plaintext token,格式为 Sanctum 标准 `<id>\|<secret>`。 |
|
||
| `data.expiresAt` | string | 扩展 token 过期时间。 |
|
||
| `data.userEmail` | string\|null | 当前用户邮箱。 |
|
||
| `data.apiBaseUrl` | string | 后续扩展请求使用的 API base URL。 |
|
||
| `data.priorTokensRevoked` | integer | 本次撤销的旧扩展 token 数量。 |
|
||
| `data.priorTokensFromOtherDevice` | boolean | 是否踢掉了其他设备 token。 |
|
||
|
||
备注:扩展 token 明文只返回一次,数据库写入 `personal_access_tokens`,`name` 为 `storeai_extension` 或
|
||
`storeai_extension:<label>`,abilities 只包含扩展权限;新配对会删除该账号所有旧扩展 token,保持扩展侧单设备策略,但不会删除当前
|
||
Web token。普通 Dashboard Sanctum token 不能调用扩展接口。
|
||
|
||
### POST `/api/auth/refresh-extension-token`
|
||
|
||
鉴权:扩展机器 token。
|
||
必需 header:`Authorization: Bearer <extension_sanctum_token>`。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/auth/refresh-extension-token" \
|
||
-H "Authorization: Bearer <extension_sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------------------|---------|----------------------------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.token` | string | 新扩展专用 Sanctum plaintext token,格式为 Sanctum 标准 `<id>\|<secret>`。 |
|
||
| `data.expiresAt` | string | 新 token 过期时间。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|----------------|-----------------------------------------|
|
||
| 401 | `unauthorized` | 缺少、格式错误、已删除、已过期或缺少对应扩展 ability 的 token。 |
|
||
|
||
备注:刷新成功会先创建新扩展 token,再删除同账号其它扩展 token;删除范围只限扩展 token,不删除 Web token。
|
||
|
||
## Dashboard 与 Onboarding
|
||
|
||
### GET `/api/dashboard/shell`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/dashboard/shell" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------------------------------|--------------|-------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.user` | `ApiUser` | 当前用户。 |
|
||
| `data.brand` | object\|null | 当前账号第一品牌的 shell 信息。 |
|
||
| `data.brand.id` | string | 品牌 UUID。 |
|
||
| `data.brand.name` | string | 品牌名。 |
|
||
| `data.brand.subscription_status` | string | 订阅状态,枚举见 `BrandCore.subscription_status`。 |
|
||
| `data.brand.trial_ends_at` | string\|null | 试用结束时间。 |
|
||
| `data.brand.subscription_cancel_at` | string\|null | 预约取消时间。 |
|
||
| `data.brand.is_comp` | boolean | 是否内部赠送账号。 |
|
||
|
||
### GET `/api/dashboard`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/dashboard" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------------------|--------------------|------------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.user` | `ApiUser` | 当前用户。 |
|
||
| `data.brand` | `BrandCore`\|null | 当前账号第一品牌。无品牌时只返回 `user` 和 `brand:null`。 |
|
||
| `data.latestScan` | `ScanRow`\|null | 最近一次扫描。 |
|
||
| `data.previousScan` | `ScanRow`\|null | 最近一次可对比的历史成功扫描。 |
|
||
| `data.scanDots` | array | 最近 12 次扫描轻量列表,元素 `{ id, scanned_at, status }`。 |
|
||
| `data.alerts` | `AlertRow[]` | 最近扫描下的告警列表。 |
|
||
| `data.competitorMeta` | array | 竞品元数据,元素 `{ product_url, label }`。 |
|
||
| `data.storeBrief` | `StoreBrief`\|null | 最近扫描 AI brief。 |
|
||
| `data.dismissedKeys` | string[] | 当前品牌本地日已 Mark done 的 action key。 |
|
||
| `data.skuMatrixEntries` | array\|null | 当日 SKU Matrix 条目,来自 `sku_matrix.matrix_json`。 |
|
||
| `data.skuMatrixConfidence` | string\|null | SKU Matrix 置信度。 |
|
||
| `data.gmvRows` | array | 最近 40 条成功 scan 的 `{ scanned_at, payload }`。 |
|
||
|
||
### GET `/api/dashboard/settings`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/dashboard/settings" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------------------------|-------------------|------------------------------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.user` | `ApiUser` | 当前用户。 |
|
||
| `data.brand` | `BrandCore`\|null | 设置页品牌信息;附加 `thresholds`、`platform_account_id_bound_at`、`stores`。 |
|
||
| `data.manualUsedToday` | integer | 品牌本地日内已使用手动扫描次数。 |
|
||
| `data.lastAutoScanAt` | string\|null | 最近一次 scheduled scan 时间。 |
|
||
| `data.competitors` | array | 竞品列表,元素 `{ id, product_url, label, created_at }`。 |
|
||
|
||
### GET `/api/dashboard/billing`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/dashboard/billing" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------|-------------------|-----------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.user` | `ApiUser` | 当前用户。 |
|
||
| `data.brand` | `BrandCore`\|null | 账单页品牌信息,含 Stripe customer/subscription id 字段。 |
|
||
| `data.billing` | null | 当前固定为 null,保留给前端兼容。 |
|
||
|
||
### GET `/api/onboarding/status`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/onboarding/status" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------------|-------------------|------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.user` | `ApiUser` | 当前用户。 |
|
||
| `data.brand` | `BrandCore`\|null | Onboarding 所需品牌信息。 |
|
||
| `data.lastScanAt` | string\|null | 当前店铺最近一次 `ok` 或 `partial` scan 时间。 |
|
||
|
||
## 当前用户与扩展接口
|
||
|
||
### POST `/api/me/brand`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|------------|--------|----|----------------------|
|
||
| body | `name` | string | 是 | 品牌名称,trim 后 1-80 字符。 |
|
||
| body | `timezone` | string | 是 | PHP 支持的时区名,最长 64 字符。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/me/brand" \
|
||
-H "Authorization: Bearer <sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"name": "StoreAI Demo Brand",
|
||
"timezone": "Asia/Kuala_Lumpur"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------------------|---------|---------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.brand.id` | string | 新品牌 UUID。 |
|
||
| `data.brand.name` | string | 品牌名称。 |
|
||
| `data.brand.timezone` | string | 品牌时区。 |
|
||
| `data.store.id` | string | 同步创建的店铺 UUID。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|-------------|
|
||
| 400 | `invalid_payload` | 品牌名或时区格式错误。 |
|
||
| 400 | `brand_exists` | 当前账号已有品牌。 |
|
||
|
||
### PATCH `/api/me/brand`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|------------|--------|----|----------------------|
|
||
| body | `name` | string | 否 | 品牌名称,trim 后 1-80 字符。 |
|
||
| body | `timezone` | string | 否 | PHP 支持的时区名,最长 64 字符。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X PATCH "{BACKEND_API_BASE_URL}/api/me/brand" \
|
||
-H "Authorization: Bearer <sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"name": "StoreAI Demo Brand",
|
||
"timezone": "Asia/Kuala_Lumpur"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|--------------|---------|----------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.brand` | object | 有变更时返回 `{ id, name, timezone }`。 |
|
||
| `data.noop` | boolean | 没有可更新字段时返回 `true`。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|----------------------|
|
||
| 400 | `invalid_payload` | 品牌名或时区格式错误。 |
|
||
| 500 | `server_error` | 当前账号没有可更新品牌或更新后读取失败。 |
|
||
|
||
### DELETE `/api/me/account`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X DELETE "{BACKEND_API_BASE_URL}/api/me/account" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------|---------|-----------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-----------------------|-----------------------------------------|
|
||
| 409 | `subscription_active` | 品牌仍有 active 或 past_due 订阅,必须先取消或等待周期结束。 |
|
||
|
||
### POST `/api/me/action/dismiss`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|--------------|--------|----|--------------------------|
|
||
| body | `action_key` | string | 是 | Top Action key,1-200 字符。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/me/action/dismiss" \
|
||
-H "Authorization: Bearer <sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"action_key": "sku:SKU-001:fix_conversion"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------|---------|-----------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|-----------------------|
|
||
| 400 | `no_brand` | 当前账号没有品牌。 |
|
||
| 400 | `invalid_payload` | `action_key` 缺失或长度非法。 |
|
||
|
||
备注:`dismiss_date` 使用品牌本地日开始时间转 UTC 后的日期键,与 Dashboard 和定时报表过滤逻辑一致。
|
||
|
||
### GET `/api/me/competitors`
|
||
|
||
鉴权:扩展机器 token。
|
||
必需 header:`Authorization: Bearer <extension_sanctum_token>`、`X-Ext-Version`。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/me/competitors" \
|
||
-H "Authorization: Bearer <extension_sanctum_token>" \
|
||
-H "X-Ext-Version: 0.1.0"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------------------------|--------------|----------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.competitors` | array | 当前账号所有品牌已登记竞品。 |
|
||
| `data.competitors[].id` | string | 竞品 UUID。 |
|
||
| `data.competitors[].brand_id` | string | 品牌 UUID。 |
|
||
| `data.competitors[].product_url` | string | Shopee 商品 URL。 |
|
||
| `data.competitors[].label` | string\|null | 竞品备注。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|----------------|-----------------|
|
||
| 401 | `unauthorized` | 扩展 token 缺失或无效。 |
|
||
| 426 | `ext_outdated` | 扩展版本过旧。 |
|
||
|
||
### GET `/api/me/scan-quota`
|
||
|
||
鉴权:扩展机器 token。
|
||
必需 header:`Authorization: Bearer <extension_sanctum_token>`、`X-Ext-Version`。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/me/scan-quota" \
|
||
-H "Authorization: Bearer <extension_sanctum_token>" \
|
||
-H "X-Ext-Version: 0.1.0"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------------------------|--------------|---------------------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.allowed` | boolean | 是否允许手动扫描。 |
|
||
| `data.reason` | string | 不允许时返回,例如 `subscription_required`、`manual_cap_reached`。 |
|
||
| `message` | string | 不允许时的用户提示。 |
|
||
| `data.manualUsed` | integer | 品牌本地日已使用手动扫描次数。 |
|
||
| `data.manualCap` | integer | 每日手动扫描上限,默认 3。 |
|
||
| `data.remaining` | integer | 剩余手动扫描次数。 |
|
||
| `data.boundPlatformAccountId` | string\|null | 品牌已绑定 Shopee seller id。 |
|
||
| `data.subState` | string | 计算后的订阅状态。 |
|
||
| `data.trialEndsAt` | string\|null | 试用结束时间。 |
|
||
| `data.latestExtVersion` | string | 后端配置的最新扩展版本。 |
|
||
| `data.extDownloadUrl` | string | 扩展下载地址。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|----------------|------------------|
|
||
| 401 | `unauthorized` | 扩展 token 缺失或无效。 |
|
||
| 404 | `not_found` | 当前账号没有品牌或品牌没有店铺。 |
|
||
| 426 | `ext_outdated` | 扩展版本过旧。 |
|
||
|
||
### POST `/api/extension/heartbeat`
|
||
|
||
鉴权:扩展机器 token。
|
||
必需 header:`Authorization: Bearer <extension_sanctum_token>`。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|---------------|--------------|----|-------------------------|
|
||
| body | `skip_reason` | string\|null | 是 | 扩展本轮跳过扫描原因,可为 null。 |
|
||
| body | `ext_version` | string | 是 | 扩展版本,1-40 字符。 |
|
||
| body | `tick_at` | string | 是 | 客户端心跳时间,可被解析为 datetime。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/extension/heartbeat" \
|
||
-H "Authorization: Bearer <extension_sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"skip_reason": "Shopee tab not open",
|
||
"ext_version": "0.1.0",
|
||
"tick_at": "2026-05-08T10:15:00Z"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------|---------|----------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `code` | string | 更新失败时可能返回 `update_failed`。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|--------------------|
|
||
| 400 | `invalid_payload` | 请求体格式错误。 |
|
||
| 401 | `unauthorized` | 扩展 token 缺失或无效。 |
|
||
| 404 | `not_found` | 当前 token 所属账号没有品牌。 |
|
||
|
||
## 品牌设置接口
|
||
|
||
### GET `/api/brands/{id}/competitors`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|--------|----|----------|
|
||
| path | `id` | string | 是 | 品牌 UUID。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/competitors" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------------------------|--------------|-----------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.competitors` | array | 竞品列表。无权访问品牌时返回空数组,不泄露品牌存在性。 |
|
||
| `data.competitors[].id` | string | 竞品 UUID。 |
|
||
| `data.competitors[].product_url` | string | Shopee 商品 URL。 |
|
||
| `data.competitors[].label` | string\|null | 竞品备注。 |
|
||
| `data.competitors[].created_at` | string\|null | 创建时间。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|----------------|
|
||
| 400 | `invalid_payload` | 品牌 id 不是 UUID。 |
|
||
|
||
### POST `/api/brands/{id}/competitors`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|--------------|--------------|----|---------------------------------------------|
|
||
| path | `id` | string | 是 | 品牌 UUID。 |
|
||
| body | `productUrl` | string | 是 | Shopee 商品页 URL,必须匹配 `-i.<shopId>.<itemId>`。 |
|
||
| body | `label` | string\|null | 否 | 竞品备注,最多 80 字符。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/competitors" \
|
||
-H "Authorization: Bearer <sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"productUrl": "https://shopee.com.my/example-product-i.123456.987654321",
|
||
"label": "Main competitor"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------------------------|--------------|----------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.competitor.id` | string | 新竞品 UUID。 |
|
||
| `data.competitor.product_url` | string | Shopee 商品 URL。 |
|
||
| `data.competitor.label` | string\|null | 竞品备注。 |
|
||
| `data.competitor.created_at` | string\|null | 创建时间。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|-----------------|
|
||
| 400 | `invalid_payload` | URL 或备注格式错误。 |
|
||
| 400 | `duplicate` | 已登记相同 URL。 |
|
||
| 400 | `over_limit` | 超过每品牌 10 个竞品上限。 |
|
||
| 404 | `not_found` | 品牌不存在或不属于当前用户。 |
|
||
|
||
### DELETE `/api/brands/{id}/competitors/{competitorId}`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|----------------|--------|----|----------|
|
||
| path | `id` | string | 是 | 品牌 UUID。 |
|
||
| path | `competitorId` | string | 是 | 竞品 UUID。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X DELETE "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/competitors/00000000-0000-4000-8000-000000000201" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------|---------|-----------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|-----------------|
|
||
| 400 | `invalid_payload` | path UUID 格式错误。 |
|
||
|
||
备注:删除是幂等的,行不存在或无权删除也返回成功。
|
||
|
||
### PATCH `/api/brands/{id}/notifications`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|--------------------|---------|----|-------------------------------------------------|
|
||
| path | `id` | string | 是 | 品牌 UUID。 |
|
||
| body | `morningBriefHour` | integer | 否 | 早报本地小时,0-23。 |
|
||
| body | `eveningRecapHour` | integer | 否 | 晚报本地小时,0-23。 |
|
||
| body | `pushMode` | string | 否 | 兼容旧字段,只接受 `daily_digest` 或 `instant_alert`,不写库。 |
|
||
| body | `dailyReportHour` | integer | 否 | 兼容旧字段,仅校验,不写库。 |
|
||
| body | `silentStartHour` | integer | 否 | 兼容旧字段,仅校验,不写库。 |
|
||
| body | `silentEndHour` | integer | 否 | 兼容旧字段,仅校验,不写库。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X PATCH "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/notifications" \
|
||
-H "Authorization: Bearer <sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"morningBriefHour": 9,
|
||
"eveningRecapHour": 18,
|
||
"pushMode": "daily_digest",
|
||
"dailyReportHour": 9,
|
||
"silentStartHour": 22,
|
||
"silentEndHour": 8
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---------------------------|---------|--------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.morning_brief_hour` | integer | 更新后的早报小时,有实际更新时返回。 |
|
||
| `data.evening_recap_hour` | integer | 更新后的晚报小时,有实际更新时返回。 |
|
||
| `data.noop` | boolean | 没有可写字段时返回 `true`。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|----------------------------------|
|
||
| 400 | `invalid_payload` | 小时值不是整数、范围不在 0-23,或同一请求内早晚报小时相同。 |
|
||
| 404 | `not_found` | 品牌不存在或不属于当前用户。 |
|
||
|
||
### PATCH `/api/brands/{id}/telegram`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|----------|--------------|----|---------------------------------------------------|
|
||
| path | `id` | string | 是 | 品牌 UUID。 |
|
||
| body | `chatId` | string\|null | 是 | Telegram chat id,必须匹配 `^-?\d{1,20}$`;传 null 表示解绑。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X PATCH "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/telegram" \
|
||
-H "Authorization: Bearer <sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"chatId": "-1001234567890"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------------------|--------------|------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.telegramChatId` | string\|null | 更新后的 Telegram chat id。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|-------------------|
|
||
| 400 | `invalid_payload` | `chatId` 缺失或格式错误。 |
|
||
| 404 | `not_found` | 品牌不存在或不属于当前用户。 |
|
||
|
||
### POST `/api/brands/{id}/telegram/pair`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|--------|----|----------|
|
||
| path | `id` | string | 是 | 品牌 UUID。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/telegram/pair" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------------|---------|-------------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.token` | string | 一次性配对 token,格式 `pair_<36hex>`。 |
|
||
| `data.expiresAt` | string | 过期时间。 |
|
||
| `data.deeplink` | string | Telegram `https://t.me/<bot>?start=<token>` 链接。 |
|
||
| `data.ttlSeconds` | integer | 固定 900 秒。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-----------------|---------------------------|
|
||
| 404 | `not_found` | 品牌不存在或不属于当前用户。 |
|
||
| 500 | `misconfigured` | 缺少 `TELEGRAM_BOT_HANDLE`。 |
|
||
|
||
### PATCH `/api/brands/{id}/thresholds`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|--------------|--------|----|-----------------------------------|
|
||
| path | `id` | string | 是 | 品牌 UUID。 |
|
||
| body | `thresholds` | object | 是 | 阈值覆盖对象;未知 key 会忽略;值为 null 表示移除覆盖。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X PATCH "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/thresholds" \
|
||
-H "Authorization: Bearer <sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"thresholds": {
|
||
"SALES_01_GMV_DECLINE": 0.25,
|
||
"SALES_01_MIN_ORDERS": 5,
|
||
"SALES_02_ZERO_HOURS": 3,
|
||
"SALES_04_DAYS_LEFT": 4,
|
||
"SALES_05_RATING_MAX": 3,
|
||
"AD_01_MIN_SPEND": 50,
|
||
"AD_02_KEYWORD_SHARE": 0.5,
|
||
"AD_03_ROAS_MIN": 2.5,
|
||
"COMP_01_GAP": 0.12,
|
||
"COMP_02_DROP": null
|
||
}
|
||
}'
|
||
```
|
||
|
||
允许的 `thresholds` key:
|
||
|
||
| key | 类型 | 范围 | 说明 |
|
||
|------------------------|---------|----------|-----------|
|
||
| `SALES_01_GMV_DECLINE` | number | 0.1-0.5 | GMV 下降阈值。 |
|
||
| `SALES_01_MIN_ORDERS` | integer | 3-20 | 最小订单数。 |
|
||
| `SALES_02_ZERO_HOURS` | integer | 1-6 | 连续零单小时数。 |
|
||
| `SALES_04_DAYS_LEFT` | integer | 1-7 | 库存剩余天数。 |
|
||
| `SALES_05_RATING_MAX` | integer | 1-4 | 低评分上限。 |
|
||
| `AD_01_MIN_SPEND` | integer | 10-200 | 广告最低花费。 |
|
||
| `AD_02_KEYWORD_SHARE` | number | 0.3-0.8 | 关键词花费占比。 |
|
||
| `AD_03_ROAS_MIN` | number | 1.5-5.0 | 最低 ROAS。 |
|
||
| `COMP_01_GAP` | number | 0.05-0.3 | 竞品价差阈值。 |
|
||
| `COMP_02_DROP` | number | 0.05-0.3 | 竞品降价阈值。 |
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------------|---------|-----------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.thresholds` | object | 合并保存后的品牌阈值覆盖对象。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|-----------------------------------------------|
|
||
| 400 | `invalid_payload` | `thresholds` 不是对象、值不是数字/null、整数 key 传小数或超出范围。 |
|
||
| 404 | `not_found` | 品牌不存在或不属于当前用户。 |
|
||
|
||
## 告警接口
|
||
|
||
### POST `/api/alerts/{id}/ack`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|-------|---------|----|------------------|
|
||
| path | `id` | string | 是 | Alert UUID。 |
|
||
| body | `ack` | boolean | 否 | 是否确认;缺省为 `true`。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/alerts/00000000-0000-4000-8000-000000000301/ack" \
|
||
-H "Authorization: Bearer <sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"ack": true
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---------------------|---------|-------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.id` | string | Alert UUID。 |
|
||
| `data.acknowledged` | boolean | 更新后的确认状态。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|----------------------|
|
||
| 400 | `invalid_payload` | Alert id 不是 UUID。 |
|
||
| 404 | `not_found` | Alert 不存在或不属于当前用户品牌。 |
|
||
|
||
## 扫描上传接口
|
||
|
||
### POST `/api/ingest/scan`
|
||
|
||
鉴权:扩展机器 token。
|
||
必需 header:`Authorization: Bearer <extension_sanctum_token>`、`X-Ext-Version`。
|
||
|
||
顶层入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|------|---------------------|--------------|----|---------------------------------------------|
|
||
| body | `brandId` | string | 是 | 品牌 UUID。 |
|
||
| body | `storeId` | string | 是 | 店铺 UUID。 |
|
||
| body | `scannedAt` | string | 是 | 扫描时间,可解析为 datetime。 |
|
||
| body | `payload` | object | 是 | 扫描业务数据,见下方 schema。 |
|
||
| body | `extractorStatus` | string | 是 | `ok`、`partial` 或 `failed`。 |
|
||
| body | `extractorErrors` | string[] | 否 | 提取错误列表,最多 50 项,缺省 `[]`。 |
|
||
| body | `trigger` | string | 否 | `manual` 或 `scheduled`,缺省 `manual`。 |
|
||
| body | `platformAccountId` | string\|null | 否 | Shopee seller id,1-64 字符;4 位以上纯数字才会参与绑定/比对。 |
|
||
| body | `fieldMeta` | object | 否 | 字段采集来源元数据,缺省 `{}`。 |
|
||
|
||
`payload.today`:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---------------------------------------|---------------|----|---------------|
|
||
| `orders` | integer | 是 | 今日订单数,>=0。 |
|
||
| `gmv_cents` | integer | 是 | 今日 GMV 分,>=0。 |
|
||
| `cancel_rate` | number | 是 | 取消率,0-1。 |
|
||
| `return_rate` | number | 是 | 退货率,0-1。 |
|
||
| `gmv_delta_yesterday_pct` | number\|null | 否 | GMV 较昨日变化比例。 |
|
||
| `gmv_net_cents` | integer\|null | 否 | 净 GMV 分,>=0。 |
|
||
| `conversion_rate` | number\|null | 否 | 转化率。 |
|
||
| `visitors` | integer\|null | 否 | 访客数,>=0。 |
|
||
| `product_clicks` | integer\|null | 否 | 商品点击数,>=0。 |
|
||
| `aov_cents` | integer\|null | 否 | 客单价分,>=0。 |
|
||
| `orders_delta_yesterday_pct` | number\|null | 否 | 订单较昨日变化比例。 |
|
||
| `conversion_rate_delta_yesterday_pct` | number\|null | 否 | 转化率较昨日变化比例。 |
|
||
| `visitors_delta_yesterday_pct` | number\|null | 否 | 访客较昨日变化比例。 |
|
||
| `product_clicks_delta_yesterday_pct` | number\|null | 否 | 商品点击较昨日变化比例。 |
|
||
| `aov_delta_yesterday_pct` | number\|null | 否 | 客单价较昨日变化比例。 |
|
||
|
||
`payload.skus[]`:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|--------------------|---------------|----|----------------------------------------|
|
||
| `sku_id` | string | 是 | SKU id。 |
|
||
| `name` | string | 是 | SKU 名称。 |
|
||
| `price_cents` | integer\|null | 否 | 价格分,>=0。 |
|
||
| `l30d_sales` | integer\|null | 否 | 近 30 天销量,>=0。 |
|
||
| `l30d_impressions` | integer\|null | 否 | 近 30 天曝光,>=0。 |
|
||
| `impressions` | integer | 是 | 曝光,>=0。 |
|
||
| `clicks` | integer | 是 | 点击,>=0。 |
|
||
| `add_to_cart` | integer | 是 | 加购,>=0。 |
|
||
| `orders` | integer | 是 | 订单,>=0。 |
|
||
| `gmv_cents` | integer | 是 | GMV 分,>=0。 |
|
||
| `stock` | integer | 是 | 库存,>=0。 |
|
||
| `status` | string | 是 | `on_sale`、`unlisted` 或 `out_of_stock`。 |
|
||
|
||
`payload.ads[]`:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|--------------------------|---------------|----|----------------------------------------------|
|
||
| `campaign_id` | string | 是 | 广告活动 id。 |
|
||
| `campaign_name` | string | 是 | 广告活动名称。 |
|
||
| `type` | string | 否 | 广告类型,缺省 `unknown`。 |
|
||
| `state` | string | 否 | `ongoing`、`paused` 或 `unknown`,缺省 `unknown`。 |
|
||
| `spend_cents` | integer | 是 | 花费分,>=0。 |
|
||
| `clicks` | integer | 是 | 点击,>=0。 |
|
||
| `orders` | integer | 是 | 订单,>=0。 |
|
||
| `revenue_cents` | integer | 是 | 收入分,>=0。 |
|
||
| `impressions` | integer | 否 | 曝光,>=0,缺省 0。 |
|
||
| `roas` | number | 否 | ROAS,>=0,缺省 0。 |
|
||
| `target_roas` | number\|null | 否 | 目标 ROAS,>=0。 |
|
||
| `daily_budget_cents` | integer\|null | 否 | 日预算分,>=0。 |
|
||
| `keywords` | array | 是 | 关键词列表。 |
|
||
| `keywords[].keyword` | string | 是 | 关键词。 |
|
||
| `keywords[].spend_cents` | integer | 是 | 关键词花费分,>=0。 |
|
||
| `keywords[].clicks` | integer | 是 | 关键词点击,>=0。 |
|
||
| `keywords[].orders` | integer | 是 | 关键词订单,>=0。 |
|
||
|
||
其他 `payload` 字段:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|----------------------|--------------|----|-----------------------------------------------------------------------------------------------------|
|
||
| `store_id` | string | 是 | 店铺 UUID。 |
|
||
| `scanned_at` | string | 是 | 扫描时间。 |
|
||
| `recent_3h[]` | array | 是 | 元素 `{ hour:string, orders:integer>=0, gmv_cents:integer>=0 }`。 |
|
||
| `reviews[]` | array | 是 | 元素 `{ review_id, sku_id:null\|string, rating:1-5, text, replied:boolean, created_at }`。 |
|
||
| `competitors[]` | array | 是 | 元素 `{ product_url:url, price_cents:integer>=0, rating:null\|number 0-5, sold_label:null\|string }`。 |
|
||
| `review_summary` | object\|null | 否 | 包含 `shop_rating`、`total_reviews_l30d`、`unanswered_negative`、`rating_histogram`。 |
|
||
| `product_aggregates` | object\|null | 否 | 数值字典,值可为 number 或 null。 |
|
||
| `traffic_sources` | array\|null | 否 | 流量来源数组,含 source、sales、clicks、conversion 等字段。 |
|
||
| `shop_health` | object\|null | 否 | 店铺健康对象;传 null 表示本次未采集,传 object 时内部字段见下表。 |
|
||
|
||
`payload.shop_health`:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---------------------------------|---------------|----|----------------------------------------------------|
|
||
| `rating_overall` | string\|null | 是 | 店铺健康评级,允许 `excellent`、`good`、`fair`、`poor` 或 null。 |
|
||
| `penalty_points` | integer\|null | 是 | 当前罚分,>=0,未知传 null。 |
|
||
| `penalty_threshold` | integer\|null | 是 | 触发风险提示的罚分阈值,>=1,未知传 null。 |
|
||
| `metrics` | object | 是 | 健康指标字典,key 为指标名,value 见下方 `metrics.*`。 |
|
||
| `metrics.*.value` | number\|null | 是 | 指标当前值。 |
|
||
| `metrics.*.target` | number\|null | 是 | 指标目标值。 |
|
||
| `metrics.*.target_op` | string\|null | 是 | 达标比较符,允许 `lt`、`lte`、`gt`、`gte` 或 null。 |
|
||
| `metrics.*.counts_toward_score` | boolean | 是 | 是否参与店铺健康评分。 |
|
||
|
||
`fieldMeta` 元素:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|-------------------|--------|----|-----------------------------------------------------------------|
|
||
| `source` | string | 是 | `fetch_hook`、`dom`、`json_ld`、`og_meta`、`aggregate` 或 `missing`。 |
|
||
| `selectorMatch` | string | 否 | `primary`、`fallback` 或 `text_anchor`。 |
|
||
| `shopeeDataStamp` | string | 否 | Shopee 数据戳。 |
|
||
| `ageMinutes` | number | 否 | 数据年龄分钟数。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/ingest/scan" \
|
||
-H "Authorization: Bearer <extension_sanctum_token>" \
|
||
-H "X-Ext-Version: 0.1.0" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"brandId": "00000000-0000-4000-8000-000000000101",
|
||
"storeId": "00000000-0000-4000-8000-000000000102",
|
||
"scannedAt": "2026-05-08T10:15:00Z",
|
||
"extractorStatus": "ok",
|
||
"extractorErrors": [],
|
||
"trigger": "manual",
|
||
"platformAccountId": "1234567890",
|
||
"fieldMeta": {
|
||
"today.orders": {
|
||
"source": "fetch_hook",
|
||
"selectorMatch": "primary",
|
||
"shopeeDataStamp": "seller-center-v1",
|
||
"ageMinutes": 2
|
||
},
|
||
"ads[0].spend_cents": {
|
||
"source": "dom",
|
||
"selectorMatch": "fallback",
|
||
"ageMinutes": 5
|
||
}
|
||
},
|
||
"payload": {
|
||
"store_id": "00000000-0000-4000-8000-000000000102",
|
||
"scanned_at": "2026-05-08T10:15:00Z",
|
||
"today": {
|
||
"orders": 18,
|
||
"gmv_cents": 258000,
|
||
"cancel_rate": 0.02,
|
||
"return_rate": 0.01,
|
||
"gmv_delta_yesterday_pct": -0.18,
|
||
"gmv_net_cents": 252000,
|
||
"conversion_rate": 0.042,
|
||
"visitors": 1200,
|
||
"product_clicks": 260,
|
||
"aov_cents": 14333,
|
||
"orders_delta_yesterday_pct": -0.12,
|
||
"conversion_rate_delta_yesterday_pct": -0.08,
|
||
"visitors_delta_yesterday_pct": 0.06,
|
||
"product_clicks_delta_yesterday_pct": -0.03,
|
||
"aov_delta_yesterday_pct": -0.04
|
||
},
|
||
"recent_3h": [
|
||
{ "hour": "2026-05-08T08:00:00Z", "orders": 3, "gmv_cents": 42000 },
|
||
{ "hour": "2026-05-08T09:00:00Z", "orders": 0, "gmv_cents": 0 },
|
||
{ "hour": "2026-05-08T10:00:00Z", "orders": 1, "gmv_cents": 12000 }
|
||
],
|
||
"skus": [
|
||
{
|
||
"sku_id": "SKU-001",
|
||
"name": "Hydrating Serum 30ml",
|
||
"price_cents": 6900,
|
||
"l30d_sales": 180,
|
||
"l30d_impressions": 12000,
|
||
"impressions": 2800,
|
||
"clicks": 210,
|
||
"add_to_cart": 36,
|
||
"orders": 12,
|
||
"gmv_cents": 82800,
|
||
"stock": 24,
|
||
"status": "on_sale"
|
||
}
|
||
],
|
||
"ads": [
|
||
{
|
||
"campaign_id": "AD-001",
|
||
"campaign_name": "Hero SKU Search Ads",
|
||
"type": "search",
|
||
"state": "ongoing",
|
||
"spend_cents": 4200,
|
||
"clicks": 96,
|
||
"orders": 5,
|
||
"revenue_cents": 34500,
|
||
"impressions": 3600,
|
||
"roas": 8.21,
|
||
"target_roas": 5,
|
||
"daily_budget_cents": 10000,
|
||
"keywords": [
|
||
{ "keyword": "serum", "spend_cents": 1600, "clicks": 36, "orders": 1 },
|
||
{ "keyword": "hydrating serum", "spend_cents": 2600, "clicks": 60, "orders": 4 }
|
||
]
|
||
}
|
||
],
|
||
"reviews": [
|
||
{
|
||
"review_id": "REV-001",
|
||
"sku_id": "SKU-001",
|
||
"rating": 2,
|
||
"text": "Bottle leaked during shipping",
|
||
"replied": false,
|
||
"created_at": "2026-05-08T04:00:00Z"
|
||
}
|
||
],
|
||
"competitors": [
|
||
{
|
||
"product_url": "https://shopee.com.my/example-product-i.123456.987654321",
|
||
"price_cents": 6200,
|
||
"rating": 4.8,
|
||
"sold_label": "1.2k sold"
|
||
}
|
||
],
|
||
"review_summary": {
|
||
"shop_rating": 4.7,
|
||
"total_reviews_l30d": 42,
|
||
"unanswered_negative": 3,
|
||
"rating_histogram": { "1": 1, "2": 2, "3": 4, "4": 12, "5": 23 }
|
||
},
|
||
"product_aggregates": {
|
||
"avg_price_cents": 7200,
|
||
"active_sku_count": 18,
|
||
"out_of_stock_count": 2
|
||
},
|
||
"traffic_sources": [
|
||
{
|
||
"source": "search",
|
||
"sales": 12,
|
||
"clicks": 180,
|
||
"conversion_rate": 0.066,
|
||
"gmv_cents": 168000
|
||
}
|
||
],
|
||
"shop_health": {
|
||
"rating_overall": "good",
|
||
"penalty_points": 0,
|
||
"penalty_threshold": 3,
|
||
"metrics": {
|
||
"late_shipment_rate": {
|
||
"value": 0.01,
|
||
"target": 0.03,
|
||
"target_op": "lte",
|
||
"counts_toward_score": true
|
||
},
|
||
"chat_response_rate": {
|
||
"value": 0.92,
|
||
"target": 0.85,
|
||
"target_op": "gte",
|
||
"counts_toward_score": true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------------|---------|---------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.scanId` | string | 新 scan UUID。 |
|
||
| `data.alertsEmitted` | integer | 本次规则引擎产生的 alert 数量。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-----------------------------|-------------------------------------------|
|
||
| 400 | `invalid_payload` | JSON 非法或 schema 校验失败。 |
|
||
| 401 | `unauthorized` | 扩展 token 缺失或无效。 |
|
||
| 402 | `subscription_required` | 当前订阅状态不允许扫描。 |
|
||
| 403 | `brand_mismatch` | 品牌不属于 token 账号,或店铺不属于品牌。 |
|
||
| 409 | `platform_account_mismatch` | 当前 Shopee seller id 与品牌已绑定 seller id 不一致。 |
|
||
| 426 | `ext_outdated` | 扩展版本过旧。 |
|
||
| 429 | `rate_limited` | 手动扫描达到每日上限。 |
|
||
|
||
备注:成功后会写入 scans、SKU/ad/competitor snapshots,执行规则告警和 AI 派生数据生成;手动扫描会尝试发送 Telegram
|
||
回声,scheduled 扫描会尝试补发 catch-up brief。
|
||
|
||
## Cron 定时报表接口
|
||
|
||
### GET|POST `/api/cron/scheduled-report`
|
||
|
||
鉴权:Cron secret。生产环境要求 `CRON_SECRET` 已配置并通过 `Authorization: Bearer <CRON_SECRET>` 传入。非生产环境未配置
|
||
secret 时开放。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/cron/scheduled-report" \
|
||
-H "Authorization: Bearer <CRON_SECRET>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|--------------------------------------|---------|---------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.runAt` | string | 本次 cron UTC 执行时间。 |
|
||
| `data.report.scannedBrands` | integer | 扫描的品牌数量。 |
|
||
| `data.report.fired` | integer | 实际发送报表数量。 |
|
||
| `data.report.skipped.wrong_hour` | integer | 未到品牌本地发送窗口数量。 |
|
||
| `data.report.skipped.cooldown` | integer | 仍在 22 小时冷却期或并发抢占失败数量。 |
|
||
| `data.report.skipped.no_channel` | integer | 无 Telegram/WhatsApp 渠道数量。 |
|
||
| `data.report.skipped.no_store` | integer | 无店铺数量。 |
|
||
| `data.report.skipped.invalid_config` | integer | 报表小时配置非法数量。 |
|
||
| `data.report.errors` | integer | 单品牌处理异常数量。 |
|
||
| `data.trialReport.scanned` | integer | 扫描试用到期提醒的品牌数量。 |
|
||
| `data.trialReport.sent4h` | integer | 发送 4 小时试用提醒数量。 |
|
||
| `data.trialReport.sent1h` | integer | 发送 1 小时试用提醒数量。 |
|
||
| `data.trialReport.errors` | integer | 试用提醒异常数量。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-----------------|------------------------|
|
||
| 401 | `unauthorized` | Cron secret 不匹配。 |
|
||
| 503 | `misconfigured` | 生产环境未配置 `CRON_SECRET`。 |
|
||
|
||
备注:服务端按品牌本地 `morning_brief_hour` / `evening_recap_hour` 判断发送窗口,并使用 `last_morning_brief_at` /
|
||
`last_evening_recap_at` 做跨入口防重复。
|
||
|
||
### GET `/api/cron/daily-digest`
|
||
|
||
鉴权:同 `/api/cron/scheduled-report`。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/cron/daily-digest" \
|
||
-H "Authorization: Bearer <CRON_SECRET>"
|
||
```
|
||
|
||
响应参数:完全复用 `/api/cron/scheduled-report`。
|
||
|
||
备注:这是旧 daily digest URL 的兼容入口,当前直接复用 twice-daily scheduled-report 业务服务。
|
||
|
||
## Stripe 接口
|
||
|
||
### POST `/api/stripe/checkout`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|--------|---------------------|--------|------|---------------------------------------------------------|
|
||
| body | `agreement_version` | string | 条件必填 | 当前品牌首次订阅且尚未接受 SaaS 协议时必填,必须等于 `SAAS_AGREEMENT_VERSION`。 |
|
||
| header | `x-forwarded-for` | string | 否 | 审计 IP,优先取第一段。 |
|
||
| header | `User-Agent` | string | 否 | 协议接受审计 UA,最多保存 500 字符。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/stripe/checkout" \
|
||
-H "Authorization: Bearer <sanctum_token>" \
|
||
-H "Content-Type: application/json" \
|
||
-H "x-forwarded-for: 203.0.113.10, 10.0.0.1" \
|
||
-H "User-Agent: StoreAI Web/1.0" \
|
||
-d '{
|
||
"agreement_version": "2"
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------------|---------|----------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.url` | string | Stripe Checkout URL。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|----------------------|------------------------|
|
||
| 400 | `no_brand` | 当前账号没有品牌。 |
|
||
| 400 | `agreement_required` | 首次订阅缺少协议版本。 |
|
||
| 409 | `agreement_stale` | 协议版本不是当前版本。 |
|
||
| 500 | `config_error` | 缺少 Stripe price id 配置。 |
|
||
| 500 | `stripe_error` | Stripe API 调用失败。 |
|
||
| 500 | `server_error` | Stripe 未返回 URL。 |
|
||
|
||
### POST `/api/stripe/portal`
|
||
|
||
鉴权:Sanctum。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/stripe/portal" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------------|---------|----------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.url` | string | Stripe Billing Portal URL。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|----------------|----------------------------|
|
||
| 400 | `no_customer` | 当前品牌没有 Stripe customer id。 |
|
||
| 500 | `stripe_error` | Stripe API 调用失败。 |
|
||
| 500 | `server_error` | Stripe 未返回 URL。 |
|
||
|
||
### POST `/api/stripe/webhook`
|
||
|
||
鉴权:Stripe 签名。请求必须保留 Stripe 原始 body。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|----------|--------------------|--------|----|--------------------|
|
||
| header | `Stripe-Signature` | string | 是 | Stripe webhook 签名。 |
|
||
| raw body | event | object | 是 | Stripe event JSON。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/stripe/webhook" \
|
||
-H "Stripe-Signature: t=1715150000,v1=<stripe_signature>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"id": "evt_123",
|
||
"type": "checkout.session.completed",
|
||
"data": {
|
||
"object": {
|
||
"id": "cs_test_123",
|
||
"customer": "cus_123",
|
||
"metadata": {
|
||
"brand_id": "00000000-0000-4000-8000-000000000101"
|
||
},
|
||
"subscription": "sub_123"
|
||
}
|
||
}
|
||
}'
|
||
```
|
||
|
||
支持事件:
|
||
|
||
| 事件 | 处理 |
|
||
|----------------------------------------------------|--------------------------------------------|
|
||
| `checkout.session.completed` | 写回品牌 `stripe_customer_id`。 |
|
||
| `customer.subscription.created` | 更新订阅状态、subscription id、customer id、预约取消时间。 |
|
||
| `customer.subscription.updated` | 同上;首次进入 `past_due` 会尝试 Telegram 提醒。 |
|
||
| `customer.subscription.deleted` | 标记 canceled,清空 subscription id 和预约取消时间。 |
|
||
| `charge.dispute.created` | 写入 dispute 审计,尝试通知管理员 Telegram。 |
|
||
| `charge.dispute.updated` / `charge.dispute.closed` | 更新 dispute 状态。 |
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------------|---------|-----------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.received` | string | 已处理事件类型。 |
|
||
| `data.deduped` | boolean | 重复已完成事件返回 `true`。 |
|
||
| `data.eventId` | string | 被去重的 Stripe event id。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|---------------------|--------------------------------------------------|
|
||
| 400 | `missing_signature` | 缺少 `Stripe-Signature`。 |
|
||
| 400 | `bad_signature` | 签名校验失败、webhook secret 缺失或 body 非法。 |
|
||
| 500 | `handler_error` | handler 异常,HTTP 仍返回 200;后端记录 failed 审计行,响应体表达失败。 |
|
||
|
||
## Telegram 接口
|
||
|
||
### POST `/api/telegram/webhook`
|
||
|
||
鉴权:Telegram webhook secret。
|
||
|
||
入参:
|
||
|
||
| 位置 | 字段 | 类型 | 必填 | 说明 |
|
||
|--------|-----------------------------------|---------|------|-----------------------------------|
|
||
| header | `X-Telegram-Bot-Api-Secret-Token` | string | 是 | 必须等于后端 `TELEGRAM_WEBHOOK_SECRET`。 |
|
||
| body | `message.chat.id` | integer | 条件必填 | Telegram chat id;无 chat id 会忽略。 |
|
||
| body | `message.text` | string | 条件必填 | 配对命令,格式 `/start pair_<hex>`。 |
|
||
| body | `message.from.first_name` | string | 否 | 成功提示中使用。 |
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X POST "{BACKEND_API_BASE_URL}/api/telegram/webhook" \
|
||
-H "X-Telegram-Bot-Api-Secret-Token: <TELEGRAM_WEBHOOK_SECRET>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"update_id": 100001,
|
||
"message": {
|
||
"message_id": 10,
|
||
"text": "/start pair_1234567890abcdef1234567890abcdef1234",
|
||
"chat": {
|
||
"id": -1001234567890,
|
||
"type": "group",
|
||
"title": "StoreAI Alerts"
|
||
},
|
||
"from": {
|
||
"id": 123456,
|
||
"first_name": "Owner"
|
||
}
|
||
}
|
||
}'
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|----------------|---------|---------------------------------------------------------------------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.brandId` | string | 成功消费配对 token 后返回品牌 UUID。 |
|
||
| `data.ignored` | string | 非配对或无法消费时返回原因:`no_chat_id`、`not_pair_command`、`unknown_token`、`already_consumed`、`expired`、`race_lost`。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|-------------------|--------------------------------|
|
||
| 400 | `invalid_payload` | 请求体不是合法 JSON。 |
|
||
| 401 | `unauthorized` | Secret header 不匹配。 |
|
||
| 503 | `misconfigured` | 后端未配置 Telegram webhook secret。 |
|
||
|
||
## Debug 接口
|
||
|
||
Debug 路由默认关闭。必须配置 `ENABLE_DEBUG_ROUTES=true` 后才可访问;关闭时返回 404
|
||
`{ "code":0, "message":"Not found.", "data": { "error":"not_found" } }`。开启后仍要求 Sanctum 鉴权。
|
||
|
||
### GET `/api/debug/last-scan`
|
||
|
||
鉴权:Debug 门禁 + Sanctum。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/debug/last-scan" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-------------------------------|--------------|-----------------------------------------------------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.diagnosis` | string | 无品牌、无店铺、无扫描等状态下返回,例如 `no_brand`、`no_store`、`no_scans`。 |
|
||
| `data.message` | string | 诊断说明。 |
|
||
| `data.brand` | object | 品牌摘要,含时区、报表小时、Telegram 是否存在。 |
|
||
| `data.latest_scan` | object | 最新 scan 摘要 `{ id, scanned_at, status, error }`。 |
|
||
| `data.field_presence` | object\|null | payload 关键字段是否存在的统计。 |
|
||
| `data.audit_dump` | object\|null | 最近 scan 的 today、商品、广告、竞品等排障摘录。 |
|
||
| `data.latest_store_brief` | object\|null | 最近 scan 的 AI brief 原始摘要。 |
|
||
| `data.sku_matrix` | object\|null | 最近 SKU Matrix,含 `computed_for`、`generated_at`、`confidence`、`quadrant_counts`、`entries`。 |
|
||
| `data.history_depth` | object | 历史 scan 深度统计。 |
|
||
| `data.competitor_diag` | object | 竞品配置和最近 10 次 scan 竞品字段统计。 |
|
||
| `data.rules_dry_run` | object\|null | 规则引擎只读 dry-run 结果。 |
|
||
| `data.ai` | object | AI provider 与 key 存在性诊断,不返回真实 key。 |
|
||
| `data.live_verdict_reattempt` | object | 只读 AI verdict 重试结果。 |
|
||
| `data.live_growth_reattempt` | object | 只读 AI growth 重试结果。 |
|
||
| `data.diagnostics` | string[] | 额外排障建议。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|----------------|--------------|
|
||
| 401 | `unauthorized` | 未登录。 |
|
||
| 404 | `not_found` | Debug 门禁未开启。 |
|
||
|
||
备注:该接口只读,不写 `alerts`、`store_briefs`、`sku_matrix` 或业务表。
|
||
|
||
### GET `/api/debug/test-verdict`
|
||
|
||
鉴权:Debug 门禁 + Sanctum。
|
||
|
||
入参:无。
|
||
|
||
请求示例:
|
||
|
||
```bash
|
||
curl -X GET "{BACKEND_API_BASE_URL}/api/debug/test-verdict" \
|
||
-H "Authorization: Bearer <sanctum_token>"
|
||
```
|
||
|
||
响应参数:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----------------------------------|--------------|------------------------------------------------------------------------------------|
|
||
| `code` | integer | 成功为 `1`。 |
|
||
| `message` | string | 成功为 `ok`。 |
|
||
| `data.diagnosis` | string | 诊断失败原因:`no_store`、`no_scan_payload`、`no_api_key`、`unsupported_provider_for_debug`。 |
|
||
| `data.provider` | string | 当前 `DIAGNOSIS_PROVIDER`。 |
|
||
| `data.model` | string | 实际请求 DeepSeek 的模型;`deepseek-v4-flash`、`deepseek-reasoner` 会改写为 `deepseek-chat`。 |
|
||
| `data.api_key_length` | integer | API key 长度,不返回真实 key。 |
|
||
| `data.request.endpoint` | string | DeepSeek endpoint。 |
|
||
| `data.request.tool_name` | string | 固定 `emit_verdict`。 |
|
||
| `data.request.user_prompt_chars` | integer | prompt 字符数。 |
|
||
| `data.response.http_status` | integer | DeepSeek HTTP 状态码。 |
|
||
| `data.response.duration_ms` | integer | 请求耗时毫秒。 |
|
||
| `data.response.network_error` | string\|null | 网络异常摘要。 |
|
||
| `data.response.body_preview` | string | 响应前 2000 字符。 |
|
||
| `data.response.body_json_present` | boolean | 响应是否可解析为 JSON。 |
|
||
| `data.tool_call_summary` | object\|null | tool call 摘要,含 finish reason、tool name、raw args、usage 等。 |
|
||
|
||
错误:
|
||
|
||
| 响应 code | data.apiCode | 说明 |
|
||
|--------:|----------------|--------------|
|
||
| 401 | `unauthorized` | 未登录。 |
|
||
| 404 | `not_found` | Debug 门禁未开启。 |
|
||
|
||
备注:该接口只支持 DeepSeek wire format 诊断;Anthropic provider 请使用 `/api/debug/last-scan` 查看 provider 包装层诊断。
|