# 30 · 完整数据模型 · API · 错误 · 缓存 > Design Complete · 路径前缀统一 `/api/v1`(下文简称 `/v1`) --- ## 1. 领域实体(逻辑模型) ``` User ──< Profile (self|other) User ── Membership User ── AiQuota User ──< Order ── OrderItem User ──< Coupon User ── DevicePush User ──< Conversation ──< Message User ──< ConsultOrder ── Consultant User ──< TestSession ── TestReport User ──< NatalReport / SynastryReport User ──< TarotDraw / SandboxWork / MoodCheckin User ── GeoPref (geo_visible, lat, lng, updated_at) SynastryInvite (token, host_user, guest?, status) PlazaPost ──< Comment ``` ### 1.1 关键字段定稿 | 实体 | 必填字段 | |---|---| | Profile | id, user_id, kind, name, gender?, birth_date, birth_time?, birth_place, lat, lng, tz | | Membership | user_id, plan(enum), status, starts_at, expires_at, auto_renew | | Order | id, user_id, channel, amount_fen, status, product_type, product_ref | | Conversation | id, user_id, mode(ai\|human), status, consultant_id? | | SynastryReport | id, profile_a, profile_b, as_of?, summary_json, detail_json, unlocked | | AiQuota | user_id, remaining, reset_at, source | 计划枚举:`trial_9_9` · `month_25` · `season_58` · `year_178`(展示价,分单位存储另表) --- ## 2. API 目录(完整设计表) ### 2.1 账号 | Method | Path | 说明 | |---|---|---| | POST | `/v1/auth/sms/send` | 发码 | | POST | `/v1/auth/sms/login` | 登录/注册 | | POST | `/v1/auth/logout` | 登出 | | GET | `/v1/me` | 我的聚合 | | PATCH | `/v1/me` | 改昵称头像 | | POST | `/v1/account/delete` | 注销申请 | ### 2.2 档案 | Method | Path | 说明 | |---|---|---| | GET/POST | `/v1/profiles` | 列表/创建 | | PATCH/DELETE | `/v1/profiles/:id` | 更新/删 | | PATCH | `/v1/me/geo` | `{visible,lat?,lng?}` | ### 2.3 内容与报告 | Method | Path | 说明 | |---|---|---| | POST | `/v1/reports/natal` | 本命 | | POST | `/v1/reports/synastry` | 合盘 | | GET | `/v1/fortune/daily` | 日运 | | GET | `/v1/reports` | 我的报告库 | | GET | `/v1/reports/:id` | 单份(含锁态) | ### 2.4 合盘社交 | Method | Path | 说明 | |---|---|---| | POST | `/v1/synastry/invites` | 创建邀请 | | GET | `/v1/synastry/invites/:token` | 预览(无 host_profile_id 泄露) | | POST | `/v1/synastry/invites/:token/accept` | 接受 | | GET | `/v1/synastry/nearby` | 附近 | ### 2.5 AI / 咨询 | Method | Path | 说明 | |---|---|---| | GET | `/v1/ai/quota` | 余量 | | POST | `/v1/ask/conversations` | 开聊 | | GET | `/v1/ask/conversations` | 历史 | | POST | `/v1/ask/conversations/:id/messages` | 发消息(流式可选 SSE) | | GET | `/v1/consultants` | 达人列表 | | POST | `/v1/consult/orders` | 下咨询单 | | POST | `/v1/consult/orders/:id/cancel` | 取消 | ### 2.6 测评与沉浸 | Method | Path | 说明 | |---|---|---| | GET | `/v1/tests` | 列表 | | * | `/v1/tests/...` | 见 21 | | * | `/v1/plaza/...` | 见 21 | | * | `/v1/sandbox/...` | 见 21 | | * | `/v1/mood/...` | 见 21 | | * | `/v1/tarot/...` | 见 21 | ### 2.7 商业 | Method | Path | 说明 | |---|---|---| | GET | `/v1/membership/plans` | 套餐 | | POST | `/v1/pay/orders` | 创建支付单 | | POST | `/v1/pay/mock/confirm` | 仅开发 mock | | GET | `/v1/orders` | 订单列表 | | GET | `/v1/coupons` | 优惠券 | | POST | `/v1/pay/orders/:id/refund` | 申请退款 | ### 2.8 消息与配置 | Method | Path | 说明 | |---|---|---| | GET | `/v1/inbox` | 站内信聚合 | | POST | `/v1/inbox/:id/read` | 已读 | | GET | `/v1/home/feed` | 首页聚合 | | GET | `/v1/config/app` | 开关/词表版本/审核模式 | --- ## 3. 统一响应与错误 ### 3.1 成功包络 ```json { "code": 0, "message": "ok", "data": {}, "request_id": "..." } ``` ### 3.2 错误码定稿 | code | HTTP | 含义 | 客户端 | |---|---|---|---| | 0 | 200 | 成功 | — | | 40101 | 401 | 未登录 | 跳登录 | | 40301 | 403 | 无权限/未解锁 | Paywall | | 40302 | 403 | 会员权益不足 | Membership | | 40401 | 404 | 资源不存在 | Toast | | 40901 | 409 | 冲突(重复提交) | 忽略/提示 | | 42201 | 422 | 参数错误 | 字段红字 | | 42901 | 429 | 限流 | 稍后重试 | | 40201 | 402/403 | 配额用尽 | 次数 Paywall | | 50000 | 500 | 内部错误 | 通用错误页 | 锁态内容:**不**靠藏路由;返回 `unlocked:false` + `teaser`,完整 `detail` 仅解锁后下发(或同接口按 entitlement 裁剪)。 --- ## 4. 缓存与一致性 | 数据 | 策略 | |---|---| | 首页 feed | CDN/边缘 60s;登录个性化部分 API 直出 | | 日运 | 按 `profile_id+date` 缓存 1h | | 本命/合盘计算结果 | 按档案哈希 + as_of 缓存;档案变更失效 | | 我的聚合 `/me` | 客户端短缓存 30s;支付成功强制失效 | | Inbox 未读 | 推送 + 轮询 60s(前台) | | 词表/开关 | `/config/app` ETag;启动拉取 | 离线:仅缓存已解锁报告摘要;敏感档案不同步到未加密存储。 --- ## 5. 分页约定 `?cursor=&limit=20` · 响应 `{ items, next_cursor }` ## 6. 与模拟器校验的关系 抓包对照本章路径与字段;差异记入 `99-emulator-validation.md` 的 Diff 表,再回写 OpenAPI。