1. 基础信息
v1.0 2026-10 · PostgreSQL 14 + Node.js 16 + Express 4Base URL
https://ry.untra.cn/api/v1服务器
154.94.234.117 · 已配置 SSL协议
HTTPS / JSON(UTF-8)
认证
JWT Bearer:
Authorization: Bearer <token>Token 有效期
30 天
健康检查
GET /api/v1/health(无需鉴权)统一响应格式
{ "ok": true, "data": { }, "msg": "可选提示" }
{ "ok": false, "error": "错误信息" }
HTTP 状态码
| 码 | 含义 |
|---|---|
200 | 成功 |
400 | 参数错误(缺必填项、格式非法) |
401 | 未登录 / token 失效或过期 |
403 | 权限不足(如学员访问教练接口、删除教练布置的计划) |
404 | 资源不存在 |
500 | 服务器内部错误(error 字段含原因) |
2. 演示账号
密码统一为 123456,可直接用于 APP 联调。
| 手机号 | 角色 | 昵称 | 说明 |
|---|---|---|---|
13800000001 | coach | 张教练 | 已绑定 4 名会员,有待确认的饮食计划 |
13800000002 | student | 小明 | 有今日训练计划 + 待确认饮食计划(主演示账号) |
13800000003 | student | 李雷 | 有饮食计划(含海鲜过敏标注) |
13800000004 | student | 王芳 | 空数据,适合从零跑通流程 |
13800000005 | student | 赵强 | 走读学员 |
3. 离线同步协议(核心)
架构原则:服务端 PostgreSQL 是唯一权威数据源;本地 SQLite 只是缓存 + 离线暂存。断网时先写本地并压入
sync_queue,联网后批量 push,再用 pull 增量回写。3.1 POST /sync/push 需登录
// 请求
{
"items": [
{
"table": "weight_records", // 白名单内的表名
"record_id": "<uuid>",
"operation": "upsert" | "delete",
"base_version": 3, // 本地上次同步到的服务器 version
"data": { /* 记录字段 */ }
}
]
}
// 响应
{ "ok": true, "data": { "results": [
{ "record_id": "...", "status": "accepted", "server_version": 4 },
{ "record_id": "...", "status": "conflict_resolved", "resolution": "server_wins",
"server_version": 5, "final_data": { /* 服务器最终数据,客户端必须覆盖本地 */ } },
{ "record_id": "...", "status": "rejected", "error": "..." }
] }}
冲突判定:当
base_version < 服务器当前 version,说明这条记录在客户端离线期间被别人(或另一台设备)改过,判定为冲突,进入裁决。裁决策略(按表)
| 表 | 策略 | 说明 |
|---|---|---|
body_composition_tests | server | 体测数据服务器权威,不接受客户端覆盖 |
notifications | server | 除已读状态外均服务器权威 |
exercise_favorites | server | 收藏以服务器为准 |
training_plans / plan_items | merge | 时间戳差 < 5 秒时做字段级合并(field_merge) |
training_sessions / training_session_items | lww | 时间戳大者胜,5 秒容差内判服务器胜 |
meal_records / weight_records / checkins | lww | 同上 |
可同步表白名单:training_plans、plan_items、training_sessions、training_session_items、meal_records、weight_records、body_composition_tests、checkins、notifications、exercise_favorites。不在白名单的表会被 rejected。
3.2 GET /sync/pull?table=&since=&limit= 需登录
since 为毫秒时间戳,limit 默认 500(上限 1000)。返回 records / next_cursor / has_more。客户端把 next_cursor 写入本地 sync_cursor 表。
3.3 GET /sync/bootstrap 需登录
首次安装或本地数据损坏时全量拉取,一次性返回 plans / sessions / dietPlans / bodyTests / enrollments / server_time。
客户端同步流程
1. push 本地 sync_queue 中的所有待同步记录
2. 用返回的 final_data 覆盖本地冲突记录,回写 version / base_version
3. 对每张表 pull(since = 本地 sync_cursor 值)
4. 写入本地 SQLite,推进 sync_cursor
5. 清空已成功的 sync_queue 条目
3.1 认证 /auth
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /auth/register | 注册。body:phone, password, nickname, role, height_cm, gender, birth_date |
| POST | /auth/login | 登录,返回 token / role / nickname / avatar_url / is_coach_verified |
| GET | /auth/me 登录 | 我的资料 |
| PUT | /auth/me 登录 | 修改资料(nickname / avatar_url / height_cm / gender / birth_date) |
3.2 动作库 /exercises
服务器权威数据,APP 侧只读缓存。已内置 36 个三级分类 + 48 个动作,动作含 7 大板块(步骤 / 呼吸 / 感受 / 要点 / 常见问题 / 平替动作 / 禁忌人群)。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /exercises/categories | 三级分类树(大类 > 部位 > 子部位) |
| GET | /exercises?category_id&equipment&difficulty&q&page&size | 动作列表,支持关键词(名称/目标肌肉)+ 分页 |
| GET | /exercises/:id | 动作详情,alternatives 字段返回平替动作 |
| POST | /exercises/:id/favorite 登录 | 收藏 / 取消收藏(幂等切换,返回 favorited) |
| GET | /exercises/favorites/mine 登录 | 我的收藏 |
3.3 训练计划 /plans
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /plans?date&from&to&status 登录 | 计划列表(含 item_count) |
| GET | /plans/today?date 登录 | 今日计划(含动作项明细,首页最常用) |
| GET | /plans/:id 登录 | 计划详情 + items(含动作名称、器械、示范图) |
| POST | /plans 登录 | 学员自建计划,body 含 items 数组 |
| POST | /plans/:id/read 登录 | 标记已读(消除"教练改了计划"红点) |
| DELETE | /plans/:id 登录 | 软删除;教练布置的计划返回 403 |
3.4 训练记录 /sessions
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /sessions 登录 | 上传一次完整训练(session + items),支持客户端生成 id 幂等重传 |
| GET | /sessions?from&to&page&size 登录 | 历史训练列表 |
| GET | /sessions/:id 登录 | 训练详情(含每组明细 + 动作名称) |
| GET | /sessions/report/weekly 登录 | 周维度报表(次数 / 组数 / 时长 / 消耗 / 完成率) |
POST body 支持两种写法:
{session:{...}, items:[...]} 或扁平 {...session字段, items:[...]}。items 以 (session_id, set_number, exercise_id) 做唯一约束,重复上传会被覆盖而非产生重复数据。3.5 饮食计划 /diet
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /diet/current 登录 | 当前生效的饮食计划 |
| GET | /diet 登录 | 我的饮食计划列表 |
| GET | /diet/:id 登录 | 计划详情 |
| POST | /diet/:id/confirm 登录 | 学员确认/拒绝(accept + reason),结果会推送通知给教练 |
| GET | /diet/:id/deliveries?date 登录 | 某日配餐交付明细 |
饮食计划状态:pending(待学员确认)→ confirmed / rejected。只有 confirmed 的计划热量目标才会计入首页缺口环。
3.6 记餐 /meals
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /meals 登录 | 记一餐:meal_type, food_items[], total_calories, protein_g, fat_g, carb_g, photo_url, recorded_at |
| GET | /meals?date 或 ?from&to 登录 | 记餐列表 |
| GET | /meals/:id 登录 | 单条详情 |
| DELETE | /meals/:id 登录 | 软删除 |
| GET | /meals/foods/search?q&category&limit | 食材营养库搜索(含别名匹配) |
| GET | /meals/foods/pull?since | 食材库增量拉取(离线缓存用) |
3.7 体重 /weights
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /weights 登录 | 记录称重:weight_kg, body_fat_pct, source, weighed_at |
| GET | /weights?from&to&limit 登录 | 体重曲线数据 |
| GET | /weights/latest 登录 | 最新一次 + delta_vs_prev / delta_vs_first / start_weight_kg |
| DELETE | /weights/:id 登录 | 软删除 |
3.8 体测与身体状况档案 /body
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /body/tests 登录 | 提交体测(体重/体脂/水分/蛋白质/骨量/骨骼肌/BMI/BMR/内脏脂肪/评分) |
| GET | /body/tests 登录 | 体测列表(趋势图数据源) |
| GET | /body/tests/latest 登录 | 最近一次体测 |
| GET | /body/tests/:id 登录 | 单条详情 |
| GET | /body/profile 登录 | 身体状况档案(慢病 / 关节损伤 / 过敏 / 饮食红线 / 运动禁忌 / 经验) |
| PUT | /body/profile 登录 | 新建或更新档案(全量覆盖) |
3.9 营地与打卡 /camp
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /camp/sessions | 营期列表(含已报名人数) |
| GET | /camp/my 登录 | 我参加的营期 |
| POST | /camp/sessions/:id/enroll 登录 | 报名(幂等) |
| GET | /camp/checkins?from&to&type 登录 | 打卡记录 |
| POST | /camp/checkins 登录 | 打卡:type, ref_id, value, camp_session_id, checked_at |
| GET | /camp/courses | 团课库 |
| GET | /camp/gyms | 健身房 |
3.10 通知 /notifications
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /notifications 登录 | 我的通知(type:plan_changed / checkin_reminder / meal_confirm / body_test) |
| GET | /notifications/unread-count 登录 | 未读数(红点) |
| POST | /notifications/:id/read 登录 | 标记已读 |
| POST | /notifications/read-all 登录 | 全部已读 |
3.11 首页汇总 /summary
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /summary/today?date 登录 | 热量缺口环:target / consumed / burned / deficit / ring_percent + 今日计划 |
| GET | /summary/daily?from&to 登录 | 历史日汇总 |
| GET | /summary/weekly 登录 | 近 7 天周报(days + totals) |
缺口公式:
deficit = 目标摄入 − 实际摄入 + 运动消耗;ring_percent = deficit / target × 100(钳制在 0~100)。目标摄入取自已确认的饮食计划,未确认时为 0。3.12 教练工作台 /coach coach / admin
全部接口需教练或管理员角色,学员访问返回 403。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /coach/todos | 工作台待办:待确认饮食计划 / 待复核体测 / 今日配餐 / 3 天未打卡会员 |
| GET | /coach/members | 我的会员(含最新体重、起始体重、weight_delta、最后训练/打卡时间) |
| POST | /coach/members/assign | 绑定会员(member_id, camp_session_id, role) |
| GET | /coach/members/:id | 会员详情:档案 + 体测 + 体重 + 训练 + 饮食计划 |
| POST | /coach/members/:id/plan | 下发训练计划(含 items),自动推送通知给学员 |
| POST | /coach/members/:id/diet-plan | 下发饮食计划(状态 pending,等学员确认) |
| GET | /coach/body-tests/pending | 待复核体测列表 |
| POST | /coach/body-tests/:id/confirm | 复核体测(写入 AI 解读 + 下次体测日期) |
| GET | /coach/meal-deliveries?date | 某日配餐交付列表 |
| POST | /coach/meal-deliveries | 创建配餐(含食材、重量、做法、冷藏温度、标签、保质期、过敏核对) |
| POST | /coach/meal-deliveries/:id/status | 更新状态:preparing / delivered / rejected |
| GET | /coach/templates | 计划模板(我的 + 公开) |
| POST | /coach/templates | 创建模板 |
| PUT | /coach/templates/:id | 更新模板 |
| DELETE | /coach/templates/:id | 删除模板 |
3.13 上传 /upload
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /upload 登录 | base64 上传(filename, mime, base64,≤10MB),返回 /uploads/xxx.jpg,通过 https://ry.untra.cn/uploads/ 访问 |
| POST | /upload/food-annotation 登录 | 提交食物照片进 AI 标注队列(photo_url, ai_result),等教练复核 |
4. 服务端数据表清单
| 表 | 用途 | 同步方式 |
|---|---|---|
users | 用户(student / coach / admin) | 服务端 |
exercise_categories / exercises | 三级分类 + 48 个动作库 | 服务端权威,APP 只读缓存 |
exercise_favorites | 动作收藏 | 双向(server 优先) |
training_plans / plan_items | 训练计划与动作项 | 双向(merge) |
plan_templates | 教练计划模板 | 服务端 |
training_sessions / training_session_items | 训练记录与每组明细 | 双向(lww) |
diet_plans | 饮食计划(教练下发 → 学员确认) | 服务端 |
meal_deliveries | 配餐交付 | 服务端 |
meal_records | 记餐 | 双向(lww) |
weight_records | 称重 | 双向(lww) |
body_composition_tests | 体测 | 双向(server 优先) |
health_profiles | 身体状况档案 | 服务端 |
checkins | 打卡 | 双向(lww) |
camp_sessions / camp_enrollments | 营期与报名 | 服务端 |
coach_member_assignments | 教练-会员绑定 | 服务端 |
notifications | 通知提醒 | 双向(server 优先) |
daily_calorie_summary | 热量缺口日汇总 | 服务端计算 |
food_nutrition_db | 食材营养库(22 条) | 服务端权威 |
training_courses / gyms | 团课 / 健身房 | 服务端 |
food_ai_annotations | 食物 AI 标注队列 | 服务端 |
audit_log | 审计日志(不可变) | 服务端 |
同步元数据:服务端每条可同步记录都带
version(更新时数据库触发器自增)、updated_at、updated_by、deleted_at(软删除,永不物理删除)。本地 SQLite 额外带 base_version、server_updated_at、sync_status。5. 运维与部署
# 服务控制
systemctl status|restart|stop blazecamp-api
journalctl -u blazecamp-api -f
# 数据库直连
PGPASSWORD=<数据库密码> psql -h 127.0.0.1 -U blazecamp -d blazecamp
# 目录
/opt/blazecamp-api # server.js / src / .env / node_modules
/opt/blazecamp-api/sql # 01_schema.sql / 02_addendum.sql / 03_seed.sql
/opt/blazecamp-api/uploads # 上传文件
# nginx
/etc/nginx/conf.d/yunchuang.conf # /api/v1/ → 127.0.0.1:8788;/api/ 仍指向既有 Python 服务
一键重新部署(本地执行)
cd deploy
python deploy.py # 打包上传代码 + 执行 SQL + npm install + 重启服务 + 注入 nginx 配置
python verify.py # 17 项接口冒烟
python e2e.py # 端到端业务链路(记餐→称重→训练→同步→缺口环→教练下发)
上线前必做:① 修改
.env 里的 JWT_SECRET;② 修改数据库密码并同步 .env 的 DB_PASS;③ 删除或修改演示账号;④ 为 /api/v1/ 增加限流(当前未做);⑤ 确认 audit_log 定期归档策略。