Skip to content

前端联调 · 用户资料(user.ts) ​

归属:前端联调 API 手册 → src/api/user.ts 端点:7 个 · 全部为 user 域 action 协议(POST /api/v1/user) 错误码段:40301xxx(全表见 错误码注册表) 错误契约:HTTP 400 / 401 / 404 + {code, msg, data}(与 oauth 域不同 —— 不走 HTTP 200) 取值语义:取值速查 · user —— habits.* 每个取值打什么画像标签 / 怎么影响人群分型、audience A/B/C 的判据、planId 的人群相关合法集、各字段「不传 vs 传 0 vs 传 null」的区别 深入说明:User API Docs

本页 curl 变量 ​

bash
BASE_URL=https://api.qingmulife.cn   # 本地:http://localhost:3000
ACCESS=<access_token>
ENV=Prd                              # Dev | Prd —— 全程必须一致
USER_ID=<登录响应 data.user.id>       # 仅前端本地使用,请求体不需要(身份以 token 为准)

全部端点需 Bearer access + X-Env。请求体统一 {"action": "...", "payload": {...}}。

端点一览 ​

#action场景
1updateBasicProfile向导 step1:性别/年龄/身高/体重 4 维独立提交
2submitOnboarding向导 step2:生活习惯 → 返画像摘要(不标 done)
3onboardingSuggestion向导 step3:方案推荐(目标 + 3 档计划 + 安全提示)
4adoptPlanOption采用某档计划(服务端重算 deficit)
5completeOnboarding向导收尾:标记 onboardingDone=true
6myProfile我的页 / 个人资料回显
7updateProfile个人资料编辑保存

updateBasicProfile ​

一句话:向导第一步 —— 4 维基础资料独立提交(可先跑通这一段再进下一步)。

curl ​

bash
curl -s "$BASE_URL/api/v1/user" \
  -H 'Content-Type: application/json' \
  -H 'X-Env: Prd' \
  -H "Authorization: Bearer $ACCESS" \
  -d '{"action":"updateBasicProfile","payload":{
        "gender":"male","age":30,"height":175,"weight":70
      }}'

请求 ​

字段类型必填说明
gender'male' | 'female'✔Literal 两值,其他值 → 40301001
agenumber✔int,5 ≤ age ≤ 120
heightnumber✔float,50 ≤ height ≤ 250(写入时 cz_js_round 取整)
weightnumber✔float,10 ≤ weight ≤ 300

响应 data ​

字段类型可空说明
genderstring否回显
agenumber否int
heightnumber否float —— 取整后的值(如 175.0)
weightnumber否float

错误码 ​

业务码HTTP触发条件前端建议处置
40301001400任一字段超范围 / gender 非两值表单即时校验,提示具体字段
40301002401未登录 / token 无效走拦截器 401 分档
40301013400年龄非法(专用码)提示年龄范围

submitOnboarding ​

一句话:向导第二步 —— 提交生活习惯,返画像摘要(这一步不标 done,收尾靠 completeOnboarding)。

curl ​

bash
curl -s "$BASE_URL/api/v1/user" \
  -H 'Content-Type: application/json' \
  -H 'X-Env: Prd' \
  -H "Authorization: Bearer $ACCESS" \
  -d '{"action":"submitOnboarding","payload":{
        "gender":"male","age":30,"height":175,"weight":70,
        "habits":{
          "exerciseFreq":3,
          "exercisePrefs":["run","walk"],
          "exercisePrefRemark":"每周三次慢跑",
          "takeoutFreq":5,
          "sugarDrink":2,
          "lateDinner":false
        }
      }}'

请求 ​

字段类型必填说明
gender'male' | 'female' | 'unknown'✔后端比前端更宽(含 unknown)
agenumber✔int,5–120
heightnumber✔float,50–250
weightnumber✔float,10–300
habits.exerciseFreqnumber✖int,0–7(每周运动次数)。0 是有效值= 不运动(久坐)→ tag「久坐少动」+ 分型走 A 类;1~3 偶尔运动;≥4 运动规律;≥3 才有「运动基础」(分型判据)。不传 → 后端按 0(2026-09-20 起改可选 — 前端不再采集,后端按打卡数据校正)。→ 取值语义
habits.exercisePrefsstring[]✔值域 'run' | 'cycling' | 'walk' | 'strength' | 'other'。⚠️ 本 action 无 1–4 长度限制(CzHabitsPayload 无 min/max)—— 空数组 [] 合法;1–4 的限制在 updateProfile
habits.exercisePrefRemarkstring✖含 other 时必填(≤200)—— 含 other 却为空 → 40301001
habits.takeoutFreqnumber✖int,0–21
habits.sugarDrinknumber✖int,0–20
habits.lateDinnerboolean✖是否常吃宵夜
nickname / zodiac / bodyFat—✖schema 支持但向导链路不收集

响应 data ​

字段类型可空说明
audiencestring否人群分型
bminumber否float
bmiLabelstring否BMI 文案标签
bmrnumber否int,基础代谢
tagsstring[]否画像标签
summarystring否画像摘要文案
minorModeboolean否未成年模式

错误码 ​

业务码HTTP触发条件前端建议处置
40301001400字段超范围 / exercisePrefs 含 other 但无 exercisePrefRemark表单即时校验(前端注释已对齐此规则 ✅)
40301002401未登录走拦截器 401 分档

onboardingSuggestion ​

一句话:向导第三步 —— 拉取目标建议 + 3 档计划选项 + 安全提示。

curl ​

bash
curl -s "$BASE_URL/api/v1/user" \
  -H 'Content-Type: application/json' \
  -H 'X-Env: Prd' \
  -H "Authorization: Bearer $ACCESS" \
  -d '{"action":"onboardingSuggestion","payload":{}}'

请求 ​

字段类型必填说明
payloadobject✖忽略 —— user_id 来自 token

响应 data ​

字段类型可空说明
audiencestring否人群分型
minorModeboolean否未成年模式
goalobject | null是目标建议块;null 时前端需兜底展示
goal.targetWeightMin / targetWeightMaxnumber否float
goal.weeksnumber否int
goal.maxWeeklyLossPctnumber否float,周最大减重比例
goal.notestring否说明文案
plans[]object[]否3 档计划(gentle / standard / intense)
plans[].idstring否gentle | standard | intense —— 原样回传给 adoptPlanOption
plans[].name / composition / reasonstring否展示文案
plans[].sessionsPerWeek / deficitKcalnumber否int
plans[].recommendedboolean否是否推荐档
safetyNotestring | null是安全提示;null 时不展示该区块

错误码 ​

业务码HTTP触发条件前端建议处置
40301002401未登录走拦截器 401 分档
40301010400画像不完整(未先走 step1/2)引导回向导前一步

adoptPlanOption ​

一句话:采用某档计划 —— 服务端重算 deficit,不信任前端传的数值。

curl ​

bash
curl -s "$BASE_URL/api/v1/user" \
  -H 'Content-Type: application/json' \
  -H 'X-Env: Prd' \
  -H "Authorization: Bearer $ACCESS" \
  -d '{"action":"adoptPlanOption","payload":{"planId":"standard"}}'

请求 ​

字段类型必填说明
planIdstring✔Literal 'gentle' | 'standard' | 'intense'(TS 标注为 string,实际值域受限)

响应 data ​

字段类型可空说明
planOptionstring否回显采用的档位
dailyDeficitKcalnumber否服务端重算 —— 不是前端传的值
sessionsPerWeeknumber否int

错误码 ​

业务码HTTP触发条件前端建议处置
40301011400planId 非法(PLAN_OPTION_INVALID)用 onboardingSuggestion 返回的 plans[].id,别硬编码
40301010400画像不完整引导回向导

completeOnboarding ​

一句话:向导收尾 —— 标记 onboardingDone = true(新用户下次登录不再弹向导)。

curl ​

bash
curl -s "$BASE_URL/api/v1/user" \
  -H 'Content-Type: application/json' \
  -H 'X-Env: Prd' \
  -H "Authorization: Bearer $ACCESS" \
  -d '{"action":"completeOnboarding","payload":{}}'

请求 ​

字段类型必填说明
payloadobject✖忽略

响应 data ​

字段类型可空说明
okboolean否恒 true

错误码 ​

业务码HTTP触发条件前端建议处置
40301002401未登录走拦截器 401 分档

myProfile ​

一句话:拉当前用户完整画像 —— 我的页 / 个人资料页回显。

curl ​

bash
curl -s "$BASE_URL/api/v1/user" \
  -H 'Content-Type: application/json' \
  -H 'X-Env: Prd' \
  -H "Authorization: Bearer $ACCESS" \
  -d '{"action":"myProfile","payload":{}}'

请求 ​

字段类型必填说明
payloadobject✖忽略(user_id 来自 token)

响应 data ⚠️ 动态 dict(无 schema) ​

响应 = 画像行 dict 合并 account 段动态组出,键集无法静态枚举。已知分组:

分组字段类型可空说明
画像 · 身份userId / nickname / avatarUrl / signature / signatureSourcestring是signatureSource = 'custom' | 'agent'
画像 · 偏好exercisePrefs / exercisePrefRemark / profileTagsarray是jsonb → list,默认 []
画像 · 4 维gender / age / height / weight / audience / zodiac—是agent 未分析时可能全为 null
画像 · 计划habits / planOption / dailyDeficitKcal / sessionsPerWeek / fitness / agentAnalyzedAt—是同上
account 段points / totalPointsEarned / memberExpireAt / growthLevel / onboardingDone—是user_core 未装配 / 异常时 5 键整体消失
时间戳updatedAtstring ISO是⚠️ 仅 PG 实现有;InMemory 默认建档无此键

⚠️ 待确认 #4:无 schema,只能按字段存在性消费,不要做全等断言。前端类型是 Record<string, any> —— 读 p.exercisePrefs 这类画像段字段是安全的(account 段缺席不影响)。

错误码 ​

业务码HTTP触发条件前端建议处置
40301002401未登录走拦截器 401 分档
40301004404用户不存在强登出

updateProfile ​

一句话:个人资料编辑保存(昵称 / 头像 / 签名 / 运动偏好)。

curl ​

bash
curl -s "$BASE_URL/api/v1/user" \
  -H 'Content-Type: application/json' \
  -H 'X-Env: Prd' \
  -H "Authorization: Bearer $ACCESS" \
  -d '{"action":"updateProfile","payload":{
        "nickname":"前端E2E用户",
        "signature":"跑起来就对了",
        "exercisePrefs":["run","cycling"],
        "exercisePrefRemark":"每周 4 跑 1 骑"
      }}'

请求 ​

字段类型必填说明
nicknamestring✖≤ 32。✅ 2026-09-20 起双写 oauth 主表(此前只写画像表 → 重登显示旧昵称)
avatarUrlstring | null✖🔴 传 null = 不修改(无法置空) —— 后端「仅非 None 字段写」
signaturestring | null✖≤ 60。🔴 传 null = 不修改(无法清空);写非空值时 signatureSource='custom'
exercisePrefsstring[]✖min 1 / max 4 —— ⚠️ 空数组 → 40301001(前端已规避:页面空数组时不传)
exercisePrefRemarkstring | null✖≤ 200;exercisePrefs 含 other 时必填

🔴 坑位 · 传 null 无法清空:前端 TS 类型写的是 string \| null(暗示可清空),但后端语义是「仅非 None 字段写」→ 传 null 被静默忽略。 当前调用点用 undefined(不传)所以没踩;一旦实现「清空签名」传 null 就会静默失效。台账 · 潜在问题 #3

响应 data ​

字段类型可空说明
user.nicknamestring | null是
user.avatarUrlstring | null是
user.signaturestring | null是
user.signatureSource'custom' | 'agent'否PG server_default 保证非 null
user.exercisePrefsstring[]是jsonb default []
user.exercisePrefRemarkstring | null是
user.profileTagsstring[]是jsonb default []

⚠️ 待确认 #5:响应 user 是画像行 dict 直出(含 userId / gender / age / … / updatedAt 全列,远超上面声明的 7 键)。上面 7 键都在 ✅,其余键未定义。

错误码 ​

业务码HTTP触发条件前端建议处置
40301001400nickname > 32 / signature > 60 / exercisePrefs 空数组 / 含 other 无 remark表单即时校验;空数组不传该字段
40301002401未登录走拦截器 401 分档