Files
smt/docs/api.md
T
2026-04-26 22:05:21 +08:00

12 KiB
Raw Blame History

戒烟/抽烟记录 API

所有接口前缀:/api/v1/smoke
除登录外都需要:Authorization: Bearer <session_key>(见:docs/common/auth.md

1) 新增记录

POST /api/v1/smoke/logs

请求体:

{
  "smoke_time": "2025-12-31",
  "smoke_at": "2025-12-31 08:30:00",
  "remark": "压力大",
  "level": 2,
  "num": 3
}

说明:

  • smoke_time 可选;不传则默认“当天”。
  • smoke_at 可选;真实抽烟时间(格式 YYYY-MM-DD HH:MM:SS)。用于“按时间节点分析/AI 建议”;不传则可用 createtime 近似。
  • level/num 可选;不传时后端会按 1 处理。
  • “想抽但忍住了”请传 level=0num=0,系统以 num=0 作为“忍住”的判断条件(计入忍住次数,但不计入抽烟支数)。

curl 示例:

curl -X POST 'http://127.0.0.1:8080/api/v1/smoke/logs' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer wx-session-key' \
  -d '{"smoke_time":"2025-12-31","smoke_at":"2025-12-31 08:30:00","remark":"压力大","level":2,"num":3}'

成功响应示例(字段以实际为准):

{
  "code": 200,
  "message": "success",
  "data": {
    "id": 5202,
    "smoke_time": "2025-12-31T00:00:00+08:00",
    "smoke_at": "2025-12-31T08:30:00+08:00",
    "remark": "压力大",
    "createtime": 1735600000,
    "updatetime": 1735600000,
    "deletetime": null,
    "level": 2,
    "num": 3
  }
}

2) 列表查询(分页)

GET /api/v1/smoke/logs?page=1&page_size=20&start=2025-12-01&end=2025-12-31&type=all

参数:

  • page:页码,默认 1
  • page_size:每页数量,默认 20,最大 200
  • start/end:可选,按 smoke_time 过滤(格式 YYYY-MM-DD
  • type:可选,默认 allsmoke 表示抽烟记录(num>0),resisted 表示忍住记录(num=0

说明:

  • 列表按时间倒序返回(优先 smoke_at,其次 createtime,最后 smoke_time)。

成功响应示例:

{
  "code": 200,
  "message": "success",
  "data": {
    "items": [],
    "total": 0,
    "page": 1,
    "page_size": 20
  }
}

3) 更新记录

POST /api/v1/smoke/logs/:id

请求体(字段可选,按需传):

{
  "smoke_time": "2026-01-01",
  "smoke_at": "2026-01-01 21:10:00",
  "remark": "聚会",
  "level": 3,
  "num": 1
}

注意:

  • 如果你想“清空 smoke_time”,请传空字符串:{"smoke_time":""}
  • 如果你想“清空 smoke_at”,请传空字符串:{"smoke_at":""}
  • 如果传 null 或者不传 smoke_time,后端会认为你没有修改该字段。

4) 删除记录(软删除)

DELETE /api/v1/smoke/logs/:id

成功响应:

{
  "code": 200,
  "message": "success",
  "data": {
    "deleted": true
  }
}

5) 首页整合接口(Home

GET /api/v1/smoke/home

此接口只返回首页、AI 时间页和 AI 日总结页正在消费的核心字段,避免生成或传递无用模块。返回示例:

{
  "code": 200,
  "message": "success",
  "data": {
    "timer": {
      "seconds_since_last": 9900,
      "next_suggested_at": "2026-01-05T10:30:00+08:00",
      "next_suggested_clock": "10:30",
      "suggestion_source": "default"
    },
    "summary": {
      "today_count": 3,
      "daily_target": 10,
      "resisted_count": 1,
      "reduced_from_yesterday": 2,
      "exceeded_yesterday": false
    },
    "motivation": {
      "message": "太棒了!你刚刚成功抵抗了一次烟瘾",
      "type": "praise"
    }
  }
}

字段说明:

  • timer.seconds_since_last:距上次抽烟的秒数(无记录返回 -1)。
  • timer.next_suggested_at:建议下次抽烟时间(RFC3339)。
  • timer.next_suggested_clock:仅时分显示(如“16:30”)。
  • timer.suggestion_source:建议来源(default/ai)。
  • summary.today_count:今日吸烟支数累加。
  • summary.daily_target:每日目标。
  • summary.resisted_count:今日忍住次数。
  • summary.reduced_from_yesterday:与昨日的绝对差值(非负)。
  • summary.exceeded_yesterday:是否比昨天多。
  • daily_summary:当天已缓存的 AI 日总结;无缓存时为 null
  • motivation.message:激励语文案。
  • motivation.type:激励语类型。

如需生成 AI 时间节点,请调用 GET /api/v1/smoke/ai/next_smoke_time;首页接口只读取缓存,不主动生成 AI 建议,避免额外性能成本。

10) 看广告解锁(用于非会员)

POST /api/v1/smoke/ai/advice_unlocks

请求体:

{
  "date": "2026-01-02",
  "ad_watched_at": "2026-01-03 09:00:00"
}

说明:

  • 该接口用于记录“已完成观看广告”,落库到 fa_smoke_ai_advice_unlocksuid + unlock_date 唯一)。
  • ad_watched_at 可由后端取当前时间;如需审计/对账可保留前端上报并做校验。
  • 解锁是“按天”的:观看一次广告解锁一天内的 AI 生成功能(可用于「每日 AI 建议」以及「AI 下次抽烟时间节点」)。
  • 如果你要生成“明天”的 AI 时间节点,请把 date 传为明天日期(例如 2026-01-06)。

11) 获取用户基础信息(首次进入:判断是否需要补全)

GET /api/v1/smoke/profile

说明:

  • 首次进入小程序建议先调用该接口:若 exists=falseis_completed=false,前端进入“信息补全”流程。
  • baseline_interval_minutes 用于建立初始基准:在用户清醒时段内的“平均间隔(分钟)”。计算:awake_minutes / baseline_cigs_per_day
  • 若未提供作息时间(起床/入睡),后端会用默认清醒时长 16*60=960 分钟参与计算。

成功响应(示例):

{
  "code": 200,
  "message": "success",
  "data": {
    "exists": true,
    "profile": {
      "id": 1,
      "created_at": "2026-01-05T10:00:00+08:00",
      "updated_at": "2026-01-05T10:00:00+08:00",
      "baseline_cigs_per_day": 20,
      "smoking_years": 8,
      "pack_price_cent": 2500,
      "smoke_motivations": ["压力大", "社交"],
      "quit_motivations": ["身体健康", "省钱"],
      "wake_up_time": "07:30",
      "sleep_time": "23:30",
      "quit_date": "2026-02-28T00:00:00+08:00",
      "onboarding_completed_at": "2026-01-05T10:00:00+08:00"
    },
    "is_completed": true,
    "awake_minutes": 960,
    "baseline_interval_minutes": 48
  }
}

exists=false(尚未补全)时,响应示例:

{
  "code": 200,
  "message": "success",
  "data": {
    "exists": false,
    "is_completed": false,
    "awake_minutes": 960,
    "baseline_interval_minutes": 0
  }
}

字段用途(补全页面可参考):

  • baseline_cigs_per_day(基础烟量/日均抽烟支数):建立初始基准,计算初始建议间隔时长。
  • smoking_years(烟龄/年)+ pack_price_cent(单包价格/分):用于看板计算“已省金额”和“恢复时长”等指标(公式可在看板端实现)。
  • smoke_motivations(抽烟动机):如 压力大/无聊/社交/提神,用于 AI 在分析 remark 时更有针对性。
  • quit_motivations(戒烟动力):如 身体健康/家人孩子/省钱,当用户产生动摇时 AI 可用这些信息做“情感阻断/自我提醒”。
  • wake_up_time + sleep_time(作息时间):用于自动规避睡眠时间,防止在用户睡觉时提醒其“坚持”。
  • quit_date(目标戒烟日期):用于阶段规划或到期提醒。

12) 补全/更新用户基础信息(Upsert)

POST /api/v1/smoke/profile

说明:

  • 字段按需传;首次进入建议一次性补全。
  • 作息时间格式:HH:MM24 小时制),例如 07:3023:10
  • pack_price_cent 为“分”;若前端用“元”,请乘以 100。

请求体(示例):

{
  "baseline_cigs_per_day": 20,
  "smoking_years": 8,
  "pack_price_cent": 2500,
  "smoke_motivations": ["压力大", "社交"],
  "quit_motivations": ["身体健康", "省钱"],
  "wake_up_time": "07:30",
  "sleep_time": "23:30",
  "quit_date": "2026-02-28"
}

成功响应:同 GET /api/v1/smoke/profile(返回最新 profile + is_completed + baseline_interval_minutes)。

13) 获取 AI 下次抽烟建议

GET /api/v1/smoke/ai/next_smoke_time

说明:

  • 用于 AI 建议页生成当天时间节点。
  • 首页只通过 GET /smoke/home 读取已缓存的 AI 结果,不主动生成 AI,避免首页加载时产生额外性能成本。
  • 可选参数:
    • date:计划日期(默认今天),支持 YYYY-MM-DDtoday/tomorrow
    • mode(默认 auto
      • auto:只在已存在 AI 时间节点时使用 AI(不主动生成)
      • ai:生成该 date 的 AI 时间节点(需要先看广告解锁;生成一次缓存一天)
      • default:永远返回默认策略

默认策略(不使用 AI):

  • 基础间隔:优先使用 GET /api/v1/smoke/profile 返回的 baseline_interval_minutes;若不存在则默认 60 分钟。
  • 阶梯式延时:最近 7 天内每累计 5 条“忍住记录(level=0,num=0)”,在基础间隔上 +5 分钟(最多 +60 分钟)。
  • 间隔兜底:最终间隔会限制在 5~240 分钟之间。
  • 若用户已补全作息时间,会自动规避睡眠区间:若计算出的时间落在睡眠区间,顺延到下一次起床时间。

AI 生成说明:

  • mode=ai 时,会把最近 3 天的抽烟数据(含“忍住记录”)作为输入提供给 AI,用于更贴合近期模式生成时间节点。
  • 未解锁时会返回 403:提示需要观看广告解锁。

成功响应(示例):

{
  "code": 200,
  "message": "success",
  "data": {
    "source": "ai",
    "suggested_at": "2026-01-05T10:28:00+08:00",
    "time_nodes": ["10:30", "11:10", "14:00", "16:30"],
    "advice": "先把这次冲动延后到10:28,期间做一次5分钟快走+喝水,压力场景用深呼吸替代。"
  }
}

14) 数据统计分析(趋势 + 健康 + 省钱)

GET /api/v1/smoke/stats?range=week|month|year&date=2026-01-07

参数:

  • rangeweek|month|year,默认 week
  • date:锚点日期(YYYY-MM-DD),默认今天

说明:

  • 用于“统计页”一屏数据整合(趋势、均值、环比、健康恢复、省钱、连续记录、已拒绝次数)。
  • trend_unitdaymonth,用于前端图表横轴显示。

成功响应(示例):

{
  "code": 200,
  "message": "success",
  "data": {
    "range": "week",
    "start": "2026-01-01",
    "end": "2026-01-07",
    "trend_unit": "day",
    "trend": [
      { "label": "2026-01-01", "count": 2 },
      { "label": "2026-01-02", "count": 1 },
      { "label": "2026-01-03", "count": 0 },
      { "label": "2026-01-04", "count": 0 },
      { "label": "2026-01-05", "count": 3 },
      { "label": "2026-01-06", "count": 0 },
      { "label": "2026-01-07", "count": 0 }
    ],
    "daily_average": 4,
    "change_percent": -20,
    "money": {
      "available": true,
      "pack_price_cent": 2500,
      "cigs_per_pack": 20,
      "expected_total": 140,
      "actual_total": 92,
      "saved_cent": 6000
    },
    "health": {
      "available": true,
      "smoke_free_minutes": 420,
      "lung_recovery_percent": 12,
      "milestones": [
        { "name": "心率血压恢复正常", "minutes": 20, "reached": true },
        { "name": "血氧水平恢复", "minutes": 480, "reached": false }
      ]
    },
    "streak_days": 12,
    "resisted_total": 24
  }
}

字段说明:

  • change_percent:与上一个同周期对比的变化比例(可为负)。
  • money.available=false:表示缺少 baseline_cigs_per_daypack_price_cent
  • money.expected_total:按“统计周期内有记录的天数”×baseline_cigs_per_day 计算;不统计无日志的天数。
  • money.saved_cent:按 max(expected_total - actual_total, 0) 计算,避免出现负值。
  • health.available=false:表示无历史记录。