# 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 ` | Dashboard / 当前用户接口 | Laravel Sanctum Web 用户 token。邮箱已验证账号可由 `/api/auth/login` 返回;邮箱未验证账号必须先通过 `/api/auth/email/verify-code`,验证码校验成功后才返回 token;重置密码仅在账号邮箱已验证时返回 token。Dashboard 路由会拒绝扩展 token。 | | `Authorization: Bearer ` | 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: ` | 扩展扫描相关接口 | 用于扩展版本门禁,低于 `MIN_EXT_VERSION` 会返回 `ext_outdated`。 | | `Authorization: Bearer ` | Cron 接口 | 配置 `CRON_SECRET` 后必填;生产环境未配置会返回 `misconfigured`。 | | `Stripe-Signature: ` | Stripe webhook | Stripe webhook 原始 body 签名。 | | `X-Telegram-Bot-Api-Secret-Token: ` | 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 " ``` 响应参数: | 字段 | 类型 | 说明 | |-------------|--------------|----------------| | `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 " ``` 响应参数: | 字段 | 类型 | 说明 | |-----------|---------|-----------| | `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 " ``` 响应参数: | 字段 | 类型 | 说明 | |----------------|---------|--------------------------------------------------------| | `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 " \ -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 " \ -H "Content-Type: application/json" \ -d '{ "label": "Chrome - Store Owner Laptop" }' ``` 响应参数: | 字段 | 类型 | 说明 | |-----------------------------------|--------------|---------------------------------------------------------------| | `code` | integer | 成功为 `1`。 | | `message` | string | 成功为 `ok`。 | | `data.token` | string | 扩展专用 Sanctum plaintext token,格式为 Sanctum 标准 `\|`。 | | `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: