Files
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

4.8 KiB
Raw Permalink Blame History

后端 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(公开,无需登录)

{"phone": "13800138000", "password": "pass123456"}

响应 data 为空对象。重复注册报错;已被运营手动授权(发卡占位行)的手机号可注册补密码,授权保留。

2. 登录

POST /api/v1/auth/login(公开,无需登录)

{"phone": "13800138000", "password": "pass123456"}

响应 data:

{"token": "<HMAC-SHA256 自签名 token>"}
  • token 无状态,有效期 auth.tokenTtl(默认 30 天),客户端存 secure storage
  • 后续所有接口携带 Authorization: Bearer <token>

3. 创建订单

POST /api/v1/orders(需登录)

{"planId": "day|week|month", "channel": "wechat|alipay"}

响应 data:

{
  "orderId": "O20260822001",
  "payParams": {
    "partnerId": "1900xxxxx", "prepayId": "wx...", "nonceStr": "...",
    "timeStamp": "1728000000", "sign": "...", "packageValue": "Sign=WXPay"
  }
}
  • 手机号由 token 识别,请求体不含 deviceId
  • channel == wechatpayParams 为微信 APP 支付下单参数
  • channel == alipaypayParams{"orderStr": "alipay_sdk=..."}

4. 支付结果确认

POST /api/v1/orders/{orderId}/confirm(需登录)

{}

客户端拉起 SDK 支付成功后调用(幂等)。服务端以微信/支付宝异步回调为准落授权;confirm 仅用于加速刷新。响应 data: {"status": "paid|created|closed"}

5. 查询授权

GET /api/v1/license(需登录)

响应 data:

{"active": true, "expiresAt": "2026-08-29T23:59:59+08:00"}
  • active: falseexpiresAt 已过期 → 客户端展示付费墙/充值入口
  • 识别入口(搜索按钮)必须调本接口做强制校验:网络失败视为不可用并提示,禁止用本地缓存放行
  • 主界面到期时间展示可用本地缓存(服务端为准,刷新时覆盖)

6. 套餐

GET /api/v1/plans(需登录)拉取价格方案,客户端不硬编码价格(进充值页时拉取,价格以服务端为准)。

响应 data:

{
  "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(无记录时字段为空串,视为无需更新):

{
  "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 相同)时,已更新完成的手机重启也不会再次弹更新
  • 网络异常/响应异常时静默跳过检查,不阻塞启动