Files
observer/flutter_app/docs/PaymentApi.md
T
admin a0b115d954 训练体系整合与标注单阶段化
- 标注:AI 预标注直写 labels_json(去候选确认两阶段);重叠去重(minIoU);全量标注按钮
- 训练:脚本迁移入 server/training/(Go 化 prepare_yolo/analyze_rfdetr,保留 train_server.py);tflite 产物自检并入训练流程(check_tflite)
- 数据目录/权重不进 git;.gitignore 迁移至仓库根
2026-08-26 18:22:56 +08:00

122 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 后端 API 契约(账号 + 支付/授权)
客户端(Flutter)与后端之间的 REST 契约。账号体系:手机号 + 密码注册登录,登录返回自签名 token,后续接口携带 `Authorization: Bearer <token>`;**识别入口(搜索按钮)必须强制服务端校验授权**,不走本地缓存。
- Base URL: `AppConfig.apiBaseUrl`(占位 `https://YOUR_BACKEND.example.com`
- 响应统一格式: `{"code": 0, "message": "ok", "data": {...}}``code != 0` 视为失败;登录失效返回 `code 61`,客户端应回登录页
- 授权语义: 自然日(当天 24:00 失效 / 7 天 / 30 天),以服务端为准
- 账号标识: 手机号(服务端 license 表即账号表,手机号为主键;无独立用户表)
## 1. 注册
`POST /api/v1/auth/register`(公开,无需登录)
```json
{"phone": "13800138000", "password": "pass123456"}
```
响应 `data` 为空对象。重复注册报错;已被运营手动授权(发卡占位行)的手机号可注册补密码,授权保留。
## 2. 登录
`POST /api/v1/auth/login`(公开,无需登录)
```json
{"phone": "13800138000", "password": "pass123456"}
```
响应 `data`:
```json
{"token": "<HMAC-SHA256 自签名 token>"}
```
- token 无状态,有效期 `auth.tokenTtl`(默认 30 天),客户端存 secure storage
- 后续所有接口携带 `Authorization: Bearer <token>`
## 3. 创建订单
`POST /api/v1/orders`(需登录)
```json
{"planId": "day|week|month", "channel": "wechat|alipay"}
```
响应 `data`:
```json
{
"orderId": "O20260822001",
"payParams": {
"partnerId": "1900xxxxx", "prepayId": "wx...", "nonceStr": "...",
"timeStamp": "1728000000", "sign": "...", "packageValue": "Sign=WXPay"
}
}
```
- 手机号由 token 识别,请求体不含 deviceId
- `channel == wechat``payParams` 为微信 APP 支付下单参数
- `channel == alipay``payParams``{"orderStr": "alipay_sdk=..."}`
## 4. 支付结果确认
`POST /api/v1/orders/{orderId}/confirm`(需登录)
```json
{}
```
客户端拉起 SDK 支付成功后调用(幂等)。服务端以微信/支付宝异步回调为准落授权;confirm 仅用于加速刷新。响应 `data: {"status": "paid|created|closed"}`
## 5. 查询授权
`GET /api/v1/license`(需登录)
响应 `data`:
```json
{"active": true, "expiresAt": "2026-08-29T23:59:59+08:00"}
```
- `active: false``expiresAt` 已过期 → 客户端展示付费墙/充值入口
- **识别入口(搜索按钮)必须调本接口做强制校验**:网络失败视为不可用并提示,禁止用本地缓存放行
- 主界面到期时间展示可用本地缓存(服务端为准,刷新时覆盖)
## 6. 套餐
`GET /api/v1/plans`(需登录)拉取价格方案,**客户端不硬编码价格**(进充值页时拉取,价格以服务端为准)。
响应 `data`:
```json
{
"list": [
{"planId": "day", "days": 1, "priceCents": 1000},
{"planId": "week", "days": 7, "priceCents": 5600},
{"planId": "month", "days": 30, "priceCents": 18000}
]
}
```
- `priceCents` 为整数分,客户端展示 ÷100 转元
- 展示名由客户端按 `days` 派生「N天」,接口无 label 字段
- 后端套餐来自 `config.yml` `plans` 节点(静态定价,改价改配置重启生效)
## 7. 版本更新检查
`GET /api/v1/app/update`(**公开接口,无需登录**)在 App 启动时调用(协议层见 `lib/update/update_checker.dart`)。
响应 `data`(无记录时字段为空串,视为无需更新):
```json
{
"version": "1.1.0",
"notes": "修复识别准确率问题"
}
```
- **仅 Android 调用**`UpdateChecker.fetch()` 在非 Android 平台直接返回空(iOS 不做版本下发,用户从 App Store 自行更新)
- 客户端以「语义化版本号」按数字段比较(`1.10.0 > 1.9.9`),**服务器版本 > 已装版本即强制更新**:弹全屏阻塞页(禁返回,仅「立即更新」),**App 内直接下载安装**(不跳浏览器):http 流式下载固定地址 `{apiBaseUrl}/download/observer-latest.apk`(后端静态托管,永远是最新 APK)到缓存目录(`.part` 原子落盘后改名,避免半包)→ 原生 `PackageInstaller` 会话安装(`InstallerChannel.kt`),页面实时显示**下载进度与安装进度**;首次安装需在系统设置允许「安装未知应用」(未开启时自动拉起系统设置页,APK 已缓存、返回后再次点击直达安装)
- **双版本比较防反复提示**:用户点「立即更新」时把服务器版本号写入本地(`SessionStore.accepted_update_version`);判定条件是服务器版本 > 已装版本 **或** 服务器版本 > 已接受版本。APK 版本号不递增(每次打的包 versionName 相同)时,已更新完成的手机重启也不会再次弹更新
- 网络异常/响应异常时静默跳过检查,不阻塞启动