# 傻瓜比价 数据表字典（产品参考）

> 面向产品/运营的全量数据表说明，按业务域分组。来源：`app/models/` 下全部 ORM 模型（共 **40 张表**）。
> 字段名为数据库真实列名；本文档由模型源码整理，后端改表后以模型为准。

## 通用约定（先读）

1. **金额单位**：所有 `*_cents` 字段单位是「**分**」，展示时 ÷100 为「元」。金币（coin）是「个数」，另有自己的兑换比例。
2. **时间**：存储统一为带时区的 UTC；展示按北京时间。部分「按天聚合」字段直接存北京日期串 `'YYYY-MM-DD'` 或日期类型，避免跨时区比较。
3. **JSON 字段**：线上 PostgreSQL 用 JSONB、本地/测试 SQLite 用普通 JSON，业务无差别。
4. **device_id 有两种含义**（重要）：
   - **per-install**（存手机本地，卸载重装会变）：领券、比价、设备存活用。
   - **硬件级稳定**（ANDROID_ID，卸载重装不变）：新手引导用。
5. **审计字段**：多数表有 `created_at`（创建时间）/ `updated_at`（更新时间），下文仅在有特殊含义时展开。
6. **约束标记**：说明里 **主键** / **唯一** / **外键→表** / **可空** 表示该列的关键约束。

---

## 表总览

| 业务域 | 表名 | 中文名 | 一句话用途 |
|---|---|---|---|
| 用户 | `user` | 用户 | App 用户主表 |
| 钱包 | `coin_account` | 金币账户 | 每用户余额快照（金币/现金） |
| 钱包 | `coin_transaction` | 金币流水 | 金币每笔变动账本 |
| 钱包 | `cash_transaction` | 现金流水 | 现金（分）每笔变动账本 |
| 钱包 | `withdraw_order` | 提现单 | 现金→微信零钱提现 |
| 钱包 | `wechat_transfer_authorization` | 微信转账免确认授权 | 用户授权后转账免逐笔确认 |
| 激励 | `signin_record` | 签到记录 | 每日签到 |
| 激励 | `user_task` | 一次性任务完成 | 只能领一次的任务 |
| 激励 | `comparison_milestone_claim` | 比价战绩领取 | 比价次数里程碑奖励 |
| 比价 | `comparison_record` | 比价记录 | 用户视角「我的比价记录」 |
| 比价 | `savings_record` | 省钱记录 | 「累计帮你省了」数据源 |
| 比价 | `price_observation` | 价格观测 | 平台/门店价格事实沉淀（后端） |
| 比价 | `store_mapping` | 平台店铺映射 | 跨平台同店身份映射（后端） |
| 比价 | `price_report` | 上报更低价 | 用户举报更低价、审核奖励 |
| 广告 | `ad_reward_record` | 激励视频发奖 | 穿山甲 S2S 回调发金币 |
| 广告 | `ad_ecpm_record` | 广告 eCPM 上报 | 展示收益统计/对账 |
| 广告 | `ad_feed_reward_record` | 信息流广告奖励 | 比价等待/领券信息流广告金币 |
| 广告 | `ad_watch_log` | 看广告时长 | 观看秒数记录（旧版/排查） |
| 领券 | `coupon_claim_record` | 领券记录 | App 内自动领券每张券结果 |
| 领券 | `coupon_daily_completion` | 领券每日完成 | 今日是否跑完整轮领券（置灰源） |
| 领券 | `coupon_prompt_engagement` | 领券弹窗频控 | 控制领券引导窗弹不弹 |
| CPS | `cps_group` | CPS 推广群 | 微信群（渠道追踪位 sid） |
| CPS | `cps_activity` | CPS 活动池 | 可推广的券（美团/淘宝/京东） |
| CPS | `cps_link` | CPS 群发短链 | `/c/{code}` 短链 |
| CPS | `cps_click` | CPS 点击事件 | 落地页点击/复制 |
| CPS | `cps_order` | CPS 对账订单 | 美团订单/佣金 |
| CPS | `cps_wx_user` | CPS 落地页微信用户 | 群发落地页授权用户 |
| CPS | `meituan_coupon` | 美团 CPS 券缓存 | 定时抓券本地缓存排序 |
| 邀请 | `invite_relation` | 邀请关系 | 邀请绑定与发奖 |
| 邀请 | `invite_fingerprint` | 邀请指纹归因 | 剪贴板失效时的兜底归因 |
| 设备 | `device_liveness` | 设备无障碍存活 | 无障碍掉线检测+推送提醒 |
| 设备 | `onboarding_completion` | 新手引导完成 | 引导只跑一次 |
| 反馈 | `feedback` | 用户反馈 | 帮助与反馈 |
| 后台 | `admin_user` | 管理员账号 | 后台账号（角色） |
| 后台 | `admin_audit_log` | 管理员操作审计 | 后台写操作留痕 |
| 配置 | `app_config` | 运营可配置项 | 把硬编码规则挪到 DB |
| 配置 | `ops_marquee_seed` | 首页轮播种子 | 滚动条兜底假数据规则 |
| 配置 | `ops_stat_config` | 首页三统计配置 | 门面数字展示模式 |
| 技术 | `launch_confirm_sample` | 启动确认窗样本 | 比价 agent 兜底样本（内部） |

---

# 一、用户

## `user` — 用户表

App 用户主表。两种登录（极光一键 / 短信验证码）都映射到同一行，按 `phone` 唯一；注册即登录。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| phone | 字符串 | 手机号，**唯一**，登录主键 |
| username | 字符串 | 对外展示账号 ID：11 位纯数字、首位非 1、**唯一**、创建时随机生成、不可变、不参与登录 |
| register_channel | 字符串 | 注册渠道：jverify（极光）/ sms |
| nickname | 字符串 | 昵称，可空 |
| avatar_url | 字符串 | 头像 URL，可空 |
| invite_code | 字符串 | 邀请码，每用户一个稳定短码（懒生成），**唯一**、可空 |
| wechat_openid | 字符串 | 微信 openid（提现到零钱用），**唯一**、可空 |
| wechat_nickname | 字符串 | 微信昵称（提现绑定卡展示），与通用 nickname 分开 |
| wechat_avatar_url | 字符串 | 微信头像，同上 |
| status | 字符串 | 账号状态：active / disabled / deleted |
| debug_trace_enabled | 布尔 | 调试链接权限：开了的用户能看到「复制调试链接」按钮，默认 false |
| created_at | 时间 | 注册时间 |
| last_login_at | 时间 | 最后登录时间 |

---

# 二、钱包

## `coin_account` — 金币账户

一个用户一行，余额快照（读取用）。

| 字段 | 类型 | 说明 |
|---|---|---|
| user_id | 整数 | **主键 + 外键→user** |
| coin_balance | 整数 | 当前金币余额 |
| cash_balance_cents | 金额(分) | 当前现金余额 |
| total_coin_earned | 整数 | 累计赚取金币（只增不减），用于「历史总收益」展示 |
| updated_at | 时间 | 更新时间 |

## `coin_transaction` — 金币流水

金币每笔变动账本，每次余额变动写一笔并记变动后余额，可逐笔对账。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| amount | 整数 | 变动额：正=入账（赚），负=出账（花/兑换） |
| balance_after | 整数 | 本笔变动后金币余额（对账用） |
| biz_type | 字符串 | 业务类型：signin / task_<key> / exchange_out / … |
| ref_id | 字符串 | 关联业务 id（签到日期、任务 key 等），可空；可重复任务按它去重防双发 |
| remark | 字符串 | 备注，可空 |
| created_at | 时间 | 时间 |

## `cash_transaction` — 现金流水

现金（分）每笔变动账本。金币兑现金、提现记这里。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| amount_cents | 金额(分) | 正=入账（兑入），负=出账（提现） |
| balance_after_cents | 金额(分) | 本笔后现金余额 |
| biz_type | 字符串 | 业务类型：exchange_in（兑入）/ withdraw（提现）/ withdraw_refund（提现退回） |
| ref_id | 字符串 | 关联业务 id，可空 |
| remark | 字符串 | 备注，可空 |
| created_at | 时间 | 时间 |

## `withdraw_order` — 提现单

现金 → 微信零钱。状态机：reviewing（待审核）→ pending（打款在途）→ success / failed；reviewing →（拒绝）→ rejected（已退款）。同一用户同时只能有一笔进行中。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| out_bill_no | 字符串 | 商户单号（我方生成，微信查单用），**唯一** |
| amount_cents | 金额(分) | 提现金额 |
| user_name | 字符串 | 提现实名（微信达额转账要求），可空 |
| status | 字符串 | reviewing / pending / success / failed / rejected |
| wechat_state | 字符串 | 微信侧原始状态（WAIT_USER_CONFIRM / SUCCESS / FAIL / CANCELLED…），可空 |
| transfer_bill_no | 字符串 | 微信转账单号，可空 |
| package_info | 字符串 | 待用户确认时给 App 拉起确认页的参数，可空 |
| fail_reason | 字符串 | 失败原因，可空 |
| created_at / updated_at | 时间 | 创建 / 更新时间 |

## `wechat_transfer_authorization` — 微信转账免确认授权

用户授权「免确认收款」后，后续转账免逐笔确认。一个用户一条。状态机：pending（待用户确认）→ active（已生效可免确认）→ closed（已关闭需重开）。

| 字段 | 类型 | 说明 |
|---|---|---|
| user_id | 整数 | **主键 + 外键→user** |
| openid | 字符串 | 用户微信 openid |
| out_authorization_no | 字符串 | 商户侧授权单号（我方生成），**唯一** |
| authorization_id | 字符串 | 微信侧授权单号（生效后返回，免确认转账要用），可空 |
| state | 字符串 | pending / active / closed |
| created_at / updated_at | 时间 | 创建 / 更新时间 |

---

# 三、激励（签到 · 任务 · 比价奖励）

## `signin_record` — 签到记录

每次签到一行，(user_id, signin_date) 唯一，天然防一天签两次。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| signin_date | 日期 | 签到日期（北京时间），与 user_id 组成**唯一** |
| cycle_day | 整数 | 1..7，7 天循环里今天第几档（决定发多少金币），断签重置回 1 |
| streak | 整数 | 连续签到天数（不封顶），断签重置回 1 |
| coin_awarded | 整数 | 本次发放金币 |
| created_at | 时间 | 时间 |


## `user_task` — 一次性任务完成记录

「打开消息提醒」这类只能领一次的任务，完成写一行，(user_id, task_key) 唯一。可循环任务（签到）走专表。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| task_key | 字符串 | 任务标识，与 user_id 组成**唯一** |
| status | 字符串 | 状态，默认 completed |
| coin_awarded | 整数 | 发放金币 |
| completed_at | 时间 | 完成时间 |

## `comparison_milestone_claim` — 比价战绩里程碑领取

「记录比价战绩」每档（第 1~6 次）只能领一次。解锁进度由成功比价条数决定，本表只记「哪几档已领」。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| milestone | 整数 | 档位序号（1-based），与 user_id 组成**唯一** |
| coin_awarded | 整数 | 发放金币 |
| claimed_at | 时间 | 领取时间 |

---

# 四、比价 · 省钱

## `comparison_record` — 比价记录

用户视角的「我的比价记录」，每完成一次比价客户端登录后上报一条。(user_id, trace_id) 唯一（重试幂等）。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| device_id | 字符串 | 设备号，可空 |
| business_type | 字符串 | food（外卖，当前唯一）/ ecom（电商）/ coupon（领券） |
| trace_id | 字符串 | 比价追踪号，与 user_id 组成**唯一** |
| trace_url | 字符串 | 公网调试链接（按用户 debug 权限决定是否返回），可空 |
| source_platform_id / _name | 字符串 | 源平台（发起比价那家）id / 名 |
| source_package | 字符串 | 源平台 App 包名 |
| source_price_cents | 金额(分) | 源平台价 |
| best_platform_id / _name | 字符串 | 最优（最便宜）平台 id / 名 |
| best_price_cents | 金额(分) | 最优价 |
| best_deeplink | 字符串 | 最优平台深链（「再次比价」直达），可空 |
| saved_amount_cents | 金额(分) | 省下金额=源价−最优价（可为 0/负） |
| is_source_best | 布尔 | 源平台就是最便宜（这次没省到），可空 |
| store_name | 字符串 | 店名 |
| total_dish_count / skipped_dish_count | 整数 | 下单菜数 / 跳过菜数 |
| status | 字符串 | success（有效对比）/ failed |
| information | 字符串 | done 文案：成功的到手价说明 / 失败原因 |
| items | JSON | 下单菜品明细 |
| comparison_results | JSON | 逐平台对比明细（含优惠/名次） |
| skipped_dish_names | JSON | 未找到跳过的菜名 |
| raw_payload | JSON | 客户端原始上报全量（取数兜底），可空 |
| device_model / device_manufacturer | 字符串 | 机型 / 厂商（排障），可空 |
| rom_vendor / rom_name / rom_version | 字符串/整数 | ROM 厂商/名/版本（排障），可空 |
| android_version / android_sdk | 字符串/整数 | 安卓版本，可空 |
| app_version / app_version_code | 字符串/整数 | 我方 App 版本，可空 |
| source_app_version | 字符串 | 源平台 App 版本（排适配失效），可空 |
| longitude / latitude | 小数 | 比价时定位，可空 |
| total_ms / step_count | 整数 | 整场耗时 / agent 步数（排障），可空 |
| llm_call_count / retry_count | 整数 | LLM 调用 / 重试次数，可空 |
| input_tokens / output_tokens | 整数 | LLM 累计 token，可空 |
| llm_calls | JSON | 每次 LLM 调用明细，可空 |
| created_at | 时间 | 时间 |

## `savings_record` — 省钱记录

profile「累计帮你省了」「省钱战绩」的唯一数据源。(user_id, client_event_id) 唯一（真实上报幂等）。当前由 demo seeder 灌入，比价真接入后改上报写入。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| order_amount_cents | 金额(分) | 订单实付金额 |
| saved_amount_cents | 金额(分) | 省下金额 |
| platform | 字符串 | 平台，可空 |
| title | 字符串 | 标题，可空 |
| shop_name | 字符串 | 店铺名（订单卡标题），可空 |
| dishes | JSON | 菜品名列表（前 2 道直接展示） |
| source | 字符串 | demo（演示）/ compare（真实比价上报） |
| original_price_cents | 金额(分) | 源平台原价，可空（真实上报才有） |
| compared_price_cents | 金额(分) | 我们给出的比价价，可空 |
| pay_channel | 字符串 | 支付渠道：wechat / alipay，可空 |
| platform_package | 字符串 | 实际下单平台包名，可空 |
| source_platform_name | 字符串 | 源平台展示名（如「美团」），可空 |
| source_deeplink | 字符串 | 源平台重进链接（预留），可空 |
| client_event_id | 字符串 | 客户端幂等键（UUID），demo 行为空 |
| device_id | 字符串 | 设备号，可空 |
| created_at | 时间 | 时间 |

## `price_observation` — 价格观测（后端数据资产）

平台/门店视角的「价格事实」。比价跑到 done 后由服务端无条件沉淀（与登录无关、匿名也记），是未来「查过同店秒回价」的源头。(trace_id, platform, scope) 唯一。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| observed_at | 时间 | 观测时刻（≈比价 done 时） |
| trace_id | 字符串 | 比价追踪号（溯源+幂等） |
| business_type | 字符串 | food / ecom |
| platform | 字符串 | 平台 |
| platform_store_id | 字符串 | 平台内门店 ID，可空 |
| store_name / city | 字符串 | 门店名 / 城市，可空 |
| geohash / lng / lat | 字符串/小数 | 地理位置，可空 |
| is_source | 布尔 | 是否源平台 |
| scope | 字符串 | order（整单到手价，当前唯一）/ dish（单菜价） |
| price_cents | 金额(分) | 该口径到手价；空=采集失败/打烊 |
| coupon_saved_cents | 金额(分) | 优惠额，可空 |
| coupon_name | 字符串 | 优惠名，可空 |
| store_closed | 字符串 | 打烊原因（非空时无有效价），可空 |
| rank | 整数 | 本次比价全局名次（1=最便宜），可空 |
| dishes | JSON | 菜篮明细，可空 |
| attrs | JSON | 灵活字段兜底，可空 |
| source_device_id / source_user_id | 字符串/整数 | 来源设备 / 用户（不鉴权时为空） |
| confidence | 小数 | 置信度，默认 1.0 |
| created_at | 时间 | 时间 |

## `store_mapping` — 平台店铺映射（后端数据资产）

跨平台「同一家店」在淘宝/美团/京东各自的店铺 id 与店名映射。每次淘宝比价沉淀一行。trace_id 唯一。⚠️ 数据来自 LLM 店铺匹配，可能匹配错。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| id_taobao / name_taobao | 字符串 | 淘宝店铺 id（shopId）/ 名，可空 |
| id_meituan / name_meituan | 字符串 | 美团店铺 id（预留稳定 poi）/ 名，可空 |
| id_jd / name_jd | 字符串 | 京东店铺 id（storeId）/ 名，可空 |
| city / geohash / lng / lat | 字符串/小数 | 地理位置，可空 |
| taobao_address | 字符串 | 淘宝门店地址，可空 |
| source_platform | 字符串 | 源平台，可空 |
| business_type | 字符串 | 业务类型，默认 food |
| trace_id | 字符串 | 比价追踪号（溯源+**唯一**） |
| source_device_id / source_user_id | 字符串/整数 | 来源设备 / 用户，可空 |
| taobao_share_url / taobao_resolved_url / taobao_deeplink | 字符串/文本 | 淘宝短链 / 解析 URL / 深链 |
| taobao_deeplink_invalid_at | 时间 | 淘宝深链失效标记（空=有效） |
| meituan_poi_id_str | 字符串 | 美团一次性票据（非稳定主键），可空 |
| meituan_share_url / meituan_resolved_url / meituan_deeplink | 字符串/文本 | 美团短链 / 解析 URL / 深链 |
| jd_vender_id | 字符串 | 京东商家 id（深链要用），可空 |
| jd_share_url / jd_resolved_url / jd_deeplink | 字符串/文本 | 京东短链 / 解析 URL / 深链 |
| jd_deeplink_invalid_at | 时间 | 京东深链失效标记（空=有效） |
| attrs | JSON | 灵活字段兜底，可空 |
| created_at | 时间 | 时间 |

## `price_report` — 上报更低价

用户在比价记录里举报「某平台有更低价」+ 截图，人工审核通过奖励金币。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| comparison_record_id | 整数 | **外键→comparison_record**，关联的比价记录，可空（记录删后保留上报） |
| store_name / dish_summary | 字符串 | 比价记录快照：店名 / 菜品摘要，可空 |
| original_platform_id / _name | 字符串 | 原最低价平台快照 |
| original_price_cents | 金额(分) | 原最低价快照 |
| reported_platform_id / _name | 字符串 | 用户上报的平台 |
| reported_price_cents | 金额(分) | 用户上报的更低价 |
| images | JSON | 截图 URL 列表 |
| status | 字符串 | pending（审核中）/ approved / rejected |
| reject_reason | 字符串 | 驳回原因，可空 |
| reward_coins | 整数 | 通过奖励金币，可空 |
| reviewed_at | 时间 | 审核时间，可空 |
| created_at | 时间 | 提交时间 |

---

# 五、广告变现

> 看广告有三条并列数据流：`ad_reward_record`（后端 S2S 发金币）、`ad_ecpm_record`（前端报展示收益）、`ad_watch_log`（前端报观看时长）；外加点位 2 的 `ad_feed_reward_record`（信息流）。

## `ad_reward_record` — 激励视频发奖记录

每条=穿山甲/GroMore 一次服务端激励回调。`trans_id` 唯一做幂等键。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| trans_id | 字符串 | 穿山甲交易号，**唯一**（幂等键） |
| user_id | 整数 | **外键→user** |
| coin | 整数 | 实发金币（超限为 0） |
| status | 字符串 | granted（已发）/ capped（当日超限）/ ecpm_missing（缺 eCPM） |
| reward_scene | 字符串 | reward_video（福利页看视频，当前唯一发币场景）/ withdrawal_ad（提现门槛视频，不发币）/ signin_boost（**历史值，2026-07 已下线**） |
| ad_session_id | 字符串 | 广告会话 id，可空 |
| ecpm_raw | 字符串 | 本次发奖采用的 eCPM 原始值，可空 |
| app_env | 字符串 | 应用环境 prod/test（回填），可空 |
| our_code_id | 字符串 | 我方配置代码位 104xxx（回填），可空 |
| reward_date | 字符串 | 北京日期串，按它统计当日发奖次数 |
| reward_name | 字符串 | 穿山甲上报奖励名（参考），可空 |
| raw | 字符串 | 回调原始参数（审计），可空 |
| created_at | 时间 | 时间 |

## `ad_ecpm_record` — 广告 eCPM 上报

每条=客户端一次广告展示后读到的 eCPM。`ad_session_id` 唯一，把展示收益与奖励完成绑定。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| ad_type | 字符串 | reward_video / draw 等 |
| ad_session_id | 字符串 | 广告会话 id，**唯一**、可空 |
| adn | 字符串 | 实际 ADN（pangle / gdt…），可空 |
| slot_id | 字符串 | 底层代码位 rit，可空 |
| app_env | 字符串 | 应用环境 prod/test，可空 |
| our_code_id | 字符串 | 我方配置代码位 104xxx，可空 |
| ecpm_raw | 字符串 | eCPM 原始字符串（单位：分/千次展示） |
| report_date | 字符串 | 北京日期串（按天聚合） |
| created_at | 时间 | 时间 |

## `ad_feed_reward_record` — 信息流广告奖励

点位 2：比价等待 / 领券信息流广告，每满 10 秒累计一份，视频完成一次性入账。`client_event_id` 唯一。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| client_event_id | 字符串 | 客户端事件 id，**唯一**（幂等） |
| ad_session_id | 字符串 | 广告会话 id，可空 |
| user_id | 整数 | **外键→user** |
| reward_date | 字符串 | 北京日期串 |
| duration_seconds | 整数 | 本次展示秒数 |
| unit_count | 整数 | 累计份数 |
| ecpm_raw | 字符串 | eCPM 原始值 |
| adn / slot_id | 字符串 | ADN / 代码位，可空 |
| feed_scene | 字符串 | comparison（比价等待）/ coupon（领券）/ welfare（福利页），可空 |
| trace_id | 字符串 | 比价场景的 trace_id（归属本场比价金币），可空 |
| app_env / our_code_id | 字符串 | 应用环境 / 代码位，可空 |
| coin | 整数 | 实发金币 |
| status | 字符串 | 状态，默认 granted |
| created_at | 时间 | 时间 |

## `ad_watch_log` — 看广告时长记录

每条=客户端上报的一次激励视频观看秒数。当前发奖以次数上限为准，本表保留作旧客户端兼容/排查。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| watch_seconds | 整数 | 本次观看秒数（服务端已夹区间） |
| watch_date | 字符串 | 北京日期串（按天聚合总时长） |
| created_at | 时间 | 时间 |

---

# 六、领券（App 内自动领券）

> 判断维度是 **device_id**（per-install，重装会变）；日期为北京自然日。

## `coupon_claim_record` — 领券记录

按 (设备, 券, 自然日) 记每张券领取结果，纯沉淀（资产/画像/排查），当前不参与「要不要领」判断。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| device_id | 字符串 | 设备号（与 coupon_id、claim_date 组成**唯一**） |
| user_id | 整数 | 登录态用户，可空（不进唯一键） |
| coupon_id | 字符串 | 券 id |
| claim_date | 日期 | 北京自然日 |
| status | 字符串 | success / already_claimed / failed / skipped |
| vendor | 字符串 | 券来源，可空 |
| coupon_name | 字符串 | 券名，可空 |
| claimed_count | 整数 | 领到几张，可空 |
| trace_id | 字符串 | 哪次任务领的（排查），可空 |
| reason | 字符串 | 失败/跳过原因，可空 |
| extra | JSON | 券结构化信息兜底，可空 |
| created_at / updated_at | 时间 | 创建 / 更新时间 |

## `coupon_daily_completion` — 领券每日完成

按 (设备, 自然日) 记「今天是否跑完整轮领券（到 done）」——首页「去领取」卡置灰源。到 done 即算（不管单券成败）。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| device_id | 字符串 | 设备号（与 complete_date 组成**唯一**） |
| user_id | 整数 | 登录态用户，可空 |
| complete_date | 日期 | 北京自然日 |
| trace_id | 字符串 | 哪次任务跑到 done（排查），可空 |
| created_at / updated_at | 时间 | 创建 / 更新时间 |

## `coupon_prompt_engagement` — 领券弹窗频控

按 (设备, App, 自然日) 记「今天这个 App 是否对领券引导窗表达过意向」——弹窗频控源。美团/淘宝/京东各自独立。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| device_id | 字符串 | 设备号（与 package、engage_date 组成**唯一**） |
| package | 字符串 | 目标 App 包名（频控维度，各 App 独立） |
| user_id | 整数 | 登录态用户，可空 |
| engage_date | 日期 | 北京自然日 |
| engage_type | 字符串 | claim_started（点一键领取）/ dismissed（拒绝/关闭）/ shown（自动弹出） |
| created_at / updated_at | 时间 | 创建 / 更新时间 |

---

# 七、CPS 群发分发（微信群发券）

## `cps_group` — CPS 推广群

每个微信群一条；`sid` 是美团联盟渠道追踪位（仅美团需要）。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| sid | 字符串 | 渠道追踪位，**唯一**、可空（纯淘宝/京东群无） |
| name | 字符串 | 群名 |
| platforms | JSON | 该群发哪些平台：["meituan","taobao","jd"] |
| member_count | 整数 | 群人数（转化率分母），可空 |
| status | 字符串 | active / archived |
| remark | 字符串 | 备注，可空 |
| created_at | 时间 | 时间 |

## `cps_activity` — CPS 活动池

可推广的券（运营预存）。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| platform | 字符串 | meituan / taobao / jd |
| name | 字符串 | 活动名 |
| act_id | 字符串 | 美团活动物料 ID，可空 |
| product_view_sign | 字符串 | 美团商品券标识（与 act_id 二选一），可空 |
| payload | 文本 | 淘宝整段淘口令 / 京东推广链接，可空 |
| image_url | 字符串 | 淘宝落地页主视觉图（绝对 URL），可空 |
| status | 字符串 | active / archived |
| remark | 字符串 | 备注，可空 |
| created_at | 时间 | 时间 |

## `cps_link` — CPS 群发短链

群发的 `/c/{code}` 短链，每个活动每次生成一条。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| code | 字符串 | 短码（`/c/{code}`），**唯一** |
| group_id | 整数 | 所属群 id |
| activity_id | 整数 | 所属活动 id |
| sid | 字符串 | 渠道追踪位（仅美团），可空 |
| platform | 字符串 | meituan / taobao / jd |
| target_url | 字符串 | 跳转目标：美团短链 / 京东链接 / 淘宝淘口令 |
| created_at | 时间 | 时间 |

## `cps_click` — CPS 点击事件

落地页点击/复制事件，统计 PV/UV 及用户级领券。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| link_id | 整数 | 所属短链 id |
| group_id | 整数 | 所属群 id（冗余，按群聚合快） |
| sid | 字符串 | 渠道追踪位（冗余），可空 |
| event_type | 字符串 | visit（进页/被跳转）/ copy（淘宝点复制口令） |
| ip / ua | 字符串 | 客户端 IP / UA（UV 近似去重），可空 |
| openid | 字符串 | 微信授权用户标识（非微信/未授权为空） |
| clicked_at | 时间 | 点击时间 |

## `cps_order` — CPS 对账订单

从美团联盟拉回、按 sid 归群的订单明细。`order_id` 唯一（upsert 幂等）。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| order_id | 字符串 | 美团订单号，**唯一** |
| sid | 字符串 | 渠道追踪位（按它归群），可空 |
| act_id | 字符串 | 活动物料 id，可空 |
| biz_line | 整数 | 业务线（1=外卖），可空 |
| trade_type | 整数 | 1=cps 2=cpa，可空 |
| pay_price_cents | 金额(分) | 付款金额，可空 |
| commission_cents | 金额(分) | 预估佣金，可空 |
| commission_rate | 字符串 | 佣金率（"300"=3%），可空 |
| refund_price_cents / refund_profit_cents | 金额(分) | 退款金额 / 退款佣金，可空 |
| mt_status | 字符串 | 美团状态：2 付款 3 完成 4 取消 5 风控 6 结算 |
| invalid_reason | 字符串 | 失效原因，可空 |
| product_name | 字符串 | 商品名，可空 |
| pay_time / mt_update_time | 时间 | 付款 / 美团更新时间，可空 |
| raw | JSON | 原始订单返回 |
| first_seen / updated_at | 时间 | 首次拉到 / 更新时间 |

## `cps_wx_user` — CPS 落地页微信用户

群发落地页经服务号网页授权拿到的用户。`openid` 唯一。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| openid | 字符串 | 服务号下用户唯一标识，**唯一** |
| unionid | 字符串 | 开放平台 unionid，可空 |
| nickname / headimgurl | 字符串 | 昵称 / 头像（userinfo 授权后才有），可空 |
| first_code | 字符串 | 首次进入来源短码，可空 |
| first_group_id | 整数 | 首次授权来源群，可空 |
| first_seen / last_seen | 时间 | 首次 / 最近出现时间 |

## `meituan_coupon` — 美团 CPS 券本地缓存

定时把美团联盟券抓进本地库，供本地排序（销量/佣金）。(source, product_view_sign) 唯一；查询用 `dedup_key` 跨源去重。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| source | 字符串 | search_waimai / search_meishi / store_supply（与 product_view_sign 组成**唯一**） |
| platform | 整数 | 1 到家/外卖，2 到店 |
| biz_line | 整数 | 到店细分：1 到餐 2 到综 3 酒店 4 门票，可空 |
| city_id | 字符串 | 城市 id |
| product_view_sign | 字符串 | 召回标识（换推广链用） |
| sku_view_id | 字符串 | sku 标识，可空 |
| name / brand_name | 字符串 | 商品名 / 品牌名，可空 |
| sell_price_cents / original_price_cents | 金额(分) | 售价 / 原价，可空 |
| head_url | 字符串 | 头图 URL，可空 |
| image_size / image_type | 整数/字符串 | 头图字节大小 / MIME（分析用），可空 |
| sale_volume | 字符串 | 销量档位文本（如「热销 1w+」），可空 |
| sale_volume_num | 整数 | 销量排序用数值，可空 |
| commission_percent | 小数 | 佣金率（1.4=1.4%），可空 |
| commission_amount_cents | 金额(分) | 佣金额，可空 |
| poi_name | 字符串 | 门店名，可空 |
| available_poi_num | 整数 | 可用门店数，可空 |
| delivery_distance_m | 小数 | 配送距离（米），可空 |
| dedup_key | 字符串 | 跨源去重键 md5(brand\|name\|price) |
| raw | JSON | 原始返回 |
| first_seen / last_seen / updated_at | 时间 | 首次 / 最近 / 更新时间 |

---

# 八、邀请裂变

## `invite_relation` — 邀请关系

一行=一次成功邀请绑定。`invitee_user_id` 唯一（一人只能被邀一次，防重复发奖）。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| inviter_user_id | 整数 | **外键→user**，邀请人 |
| invitee_user_id | 整数 | **外键→user**，被邀请人，**唯一** |
| channel | 字符串 | clipboard（剪贴板自动）/ manual（手动填码）/ fingerprint（指纹兜底） |
| status | 字符串 | effective（注册即生效），预留 pending |
| inviter_coin / invitee_coin | 整数 | 各自发放金币 |
| created_at | 时间 | 时间 |

## `invite_fingerprint` — 邀请指纹归因

剪贴板归因失效时的兜底：落地页访问留指纹，装机后用 (IP, 屏幕, 机型) 7 天内反查。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| inviter_user_id | 整数 | **外键→user**，邀请人 |
| ip | 字符串 | 落地页访问者 IP |
| device_model | 字符串 | 机型（Build.MODEL / UA 解析） |
| screen | 字符串 | 屏幕分辨率（如 1080x2400） |
| user_agent | 文本 | 完整 UA（debug 用） |
| created_at | 时间 | 时间 |

---

# 九、设备 · 新手引导

## `device_liveness` — 设备无障碍存活监控

每条=一个用户的一台设备。无障碍存活时上报心跳，掉线超时则极光推送提醒。(user_id, device_id) 唯一。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| device_id | 字符串 | per-install 设备号（与 user_id 组成**唯一**） |
| registration_id | 字符串 | 极光推送目标 id，可空 |
| platform | 字符串 | 平台，默认 android |
| app_version | 字符串 | App 版本，可空 |
| ever_protected | 布尔 | 是否开过无障碍（功能对它有意义） |
| last_heartbeat_at | 时间 | 最近心跳时间（超时=掉线），可空 |
| last_report_protection_on | 布尔 | 最近上报的无障碍开关状态 |
| liveness_state | 字符串 | unknown / alive / silent / notified |
| notified_at | 时间 | 最近推送告警时间，可空 |
| kill_alert_pending | 布尔 | 掉线告警待客户端提醒标记 |
| created_at / updated_at | 时间 | 创建 / 更新时间 |

## `onboarding_completion` — 新手引导完成

同一台设备+同一账号，新手引导只跑一次。device_id 用**硬件级稳定标识**（重装不变）。(user_id, device_id) 唯一。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | 登录态用户（与 device_id 组成**唯一**） |
| device_id | 字符串 | 硬件级稳定设备号（ANDROID_ID） |
| completed_at | 时间 | 完成时间 |

---

# 十、反馈

## `feedback` — 用户反馈

帮助与反馈，人工审核可奖励金币。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| user_id | 整数 | **外键→user** |
| content | 文本 | 反馈内容（必填） |
| contact | 字符串 | 联系方式（改版后客户端不再采集，新数据存空串） |
| images | JSON | 截图 URL 列表，可空 |
| status | 字符串 | pending（审核中）/ adopted（已采纳）/ rejected |
| reject_reason | 字符串 | 驳回原因，可空 |
| reward_coins | 整数 | 采纳奖励金币，可空 |
| review_note | 字符串 | 审核批注，可空 |
| reviewed_by_admin_id | 整数 | **外键→admin_user**，审核人，可空 |
| reviewed_at | 时间 | 审核时间，可空 |
| created_at | 时间 | 提交时间 |

---

# 十一、运营后台 · 配置

## `admin_user` — 管理员账号

后台账号，与 App 用户完全隔离（独立 JWT）。账号密码（bcrypt）登录。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| username | 字符串 | 用户名，**唯一** |
| password_hash | 字符串 | bcrypt 密码哈希 |
| role | 字符串 | super_admin（全权）/ finance（钱）/ operator（用户+反馈+大盘） |
| status | 字符串 | active / disabled |
| created_at | 时间 | 创建时间 |
| last_login_at | 时间 | 最后登录时间，可空 |

## `admin_audit_log` — 管理员操作审计

每个后台写操作落一条，记前后值，不可删，用于追溯「谁在何时改了谁的钱/状态」。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| admin_id | 整数 | **外键→admin_user** |
| admin_username | 字符串 | 操作者用户名（冗余存，改名后仍可追溯） |
| action | 字符串 | 操作类型（如 user.coins.grant / withdraw.refresh） |
| target_type | 字符串 | 被操作对象类型 |
| target_id | 字符串 | 被操作对象 id（字符串以兼容非整型主键），可空 |
| detail | JSON | 上下文 + 前后值，可空 |
| ip | 字符串 | 操作 IP，可空 |
| created_at | 时间 | 时间 |

## `app_config` — 运营可配置项

把硬编码规则（奖励数值等）挪到 DB，运营后台可改。空表=现有行为完全不变（fallback 到代码默认）。

| 字段 | 类型 | 说明 |
|---|---|---|
| key | 字符串 | **主键**，配置标识 |
| value | JSON | 配置值（任意 int/list/dict） |
| updated_by_admin_id | 整数 | 最后修改管理员，可空 |
| updated_at | 时间 | 更新时间 |

## `ops_marquee_seed` — 首页轮播种子

首页「用户 xxx 比价省了 xx 元」滚动条的兜底假数据**规则**：真实记录不够时用它补齐混播。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| masked_user | 字符串 | 脱敏用户名；留空则展示时随机合成，可空 |
| min_cents / max_cents | 金额(分) | 节省金额区间（展示时区间内随机取值） |
| enabled | 布尔 | 是否启用（停用不参与混播） |
| sort_order | 整数 | 仅后台列表排序（feed 公平随机，不看此字段） |
| created_at | 时间 | 时间 |

## `ops_stat_config` — 首页三统计配置

首页门面三数字（帮助用户/完成比价/累计节省），每指标独立选展示模式。一行一指标。

| 字段 | 类型 | 说明 |
|---|---|---|
| metric | 字符串 | **主键**：help_users / total_compares / total_saved |
| mode | 字符串 | real（真实）/ manual（手填固定值）/ random（只增不减伪增长） |
| manual_value | 整数 | manual 模式固定值（total_saved 为分），可空 |
| random_mult_min / random_mult_max | 整数 | random 倍率区间（千分比，≥1000 只增不减） |
| random_tick_seconds | 整数 | random 刷新周期（秒） |
| random_anchor_minutes | 整数 | 刷新触发时刻对齐偏移（距北京 0 点分钟数） |
| random_current | 整数 | random 当前值，可空 |
| random_last_tick_at | 时间 | 上次 tick 时刻，可空 |
| random_kind | 字符串 | mult（×倍率）/ add（+绝对增量） |
| random_step_min / random_step_max | 整数 | add 模式增量区间 |
| real_offset | 整数 | real 模式保底值（展示=max(真实,保底)） |
| allow_decrease | 布尔 | 是否允许下调（默认只增不减） |
| updated_by_admin_id | 整数 | 最后修改管理员，可空 |
| updated_at | 时间 | 更新时间 |

---

# 十二、技术沉淀（内部）

## `launch_confirm_sample` — 启动确认窗样本

比价 agent 用 LLM 兜底放行跨 App 启动确认窗时落的样本，研发人工沉淀进静态规则。内部排障用，不去重。

| 字段 | 类型 | 说明 |
|---|---|---|
| id | 整数 | **主键** |
| created_at | 时间 | 时间 |
| trace_id | 字符串 | 比价追踪号（溯源），可空 |
| device_id | 字符串 | 设备号，可空 |
| host_package | 字符串 | 弹窗宿主包（聚合键），可空 |
| target_app | 字符串 | 被打开的目标 App 包名，可空 |
| system_locale | 字符串 | 系统语言地区（zh-TW / en-US…），可空 |
| exec_success | 布尔 | 本次兜底是否成功放行（研发只沉淀 true 的） |
| dialog_title | 字符串 | 弹窗标题全文，可空 |
| payload | JSON | 完整样本（弹窗树 + LLM plan + 设备信息），可空 |

---

*文档生成基于 `app/models/` 全量模型。如需单表更详细的索引/迁移信息，见 `docs/database/` 下对应同名文件。*
