Files
2026-07-09 14:23:59 +08:00

30 KiB
Raw Permalink Blame History

数据库总览 — 表 × 功能 × 关系

跨表视角。单表字段级细节看同目录 <表名>.md(索引见 README)。 本文专门回答三件「跨表」的事:① 每块 App 功能用到哪些表 ② 什么操作往哪张表写 ③ 表和表怎么连(join key,含没有外键约束、靠业务字段对齐的语义关联)范围:业务表全部在 shaguabijia-app-server(SQLAlchemy 2.0 + SQLite 开发 / PostgreSQL 生产)。pricebot-backend(比价/领券 Agent)是纯内存态、无任何表;Android 客户端只有 EncryptedSharedPreferences / SharedPreferences、无关系库。共 45 张业务表 + alembic_version(框架的迁移版本指针)。注意「比价/领券过程」始终在 pricebot 内存态跑、不落库——只有结果回 app-server 才落库:领券结果落 coupon_* 三张今日状态表 + coupon_session 任务流水(#99);比价记录 2026-07 起由 app-server 透传壳 harvest 直接落库(comparison_record:帧0 建 running 行 → done/finalize 写终态,老客户端带 JWT 的 POST /compare/record 仅作兜底),pricebot 另经 app/api/internal/ server→server 把客观价格/门店事实落 price_observation/store_mapping(不鉴权、匿名也记)+ 启动确认窗兜底样本落 launch_confirm_sample。此外好友邀请(invite_* 2 张 + 独立账本 invite_cash_transaction)与 CPS 群发联盟(cps_* 6 张,含落地页微信身份 cps_wx_user;cps_order 已含京东联盟单)是两个独立子系统。无障碍存活监控 device_liveness(#65)按用户设备维度记心跳、检掉线召回;客户端埋点落 analytics_event(#83)。


一、按功能域看表(每张表用在 App 哪里)

比价(核心业务)

App 位置 / 动作 说明
比价/领券过程(看屏→决策→操作) (无) 在 pricebot-backend 内存态跑,过程不落库;只有结果回到 app-server 才落库
「我的比价记录」列表 / 详情 comparison_record app-server 透传壳 harvest 落库(2026-07 起):帧0 建 running 行 → done 帧写 success/failed → trace/finalize 写 cancelled/failed;老客户端带 JWT 的 POST /compare/record 兜底
比价战绩里程碑(逐档领金币) comparison_milestone_claim 累计成功比价 N 次解锁;进度读 comparison_record 计数
profile「累计省了 / 省钱战绩 / 省钱明细」 savings_record 真实下单归因(source=compare)+ 无真实数据时 demo 兜底
「上报更低价」提交 / 列表 price_report 众包纠偏:用户举证某平台更便宜,人工审核发奖
(无 App UI)比价 done 后客观价格沉淀 price_observation pricebot server→server 内部上报;平台/门店视角的到手价事实,匿名也记,与 comparison_record(用户视角)互补
(无 App UI)跨平台门店身份映射 store_mapping pricebot 内部上报;同店在淘宝/美团/京东的 id/名/deeplink,供下次比价 lookup 反查省掉现场搜店
(无 App UI)启动确认窗兜底样本 launch_confirm_sample pricebot 内部上报;LLM 兜底放行「想要打开 XX」跨 App 启动窗后回写样本(host 包 + 弹窗树 + plan + locale),供研发人工沉淀进 pricebot 规则

领券(每日领券联动 · 今日状态)

App 位置 / 动作 说明
领券过程(看屏→领券) (无) 在 pricebot-backend 内存态跑,过程不落库;结果回 app-server 才落下面三张表
切外卖 App 时是否弹领券引导窗 coupon_prompt_engagement 今天 engage 过(点领/点拒)就不再弹;判断维度 device_id
首页「去领取」卡是否置灰 coupon_daily_completion 今天跑完整轮(到 done)就置灰;判断维度 device_id
每张券领取结果留痕 coupon_claim_record 资产/画像/排查/CPS;当前不参与判断
(admin 领券看板)一次领券任务全程流水 coupon_session 客户端 POST /coupon/session 两段上报(发起建行/收尾更新);发起数、完成率、中途流失、平均耗时都从这算(#99)

钱包 / 福利(看广告赚钱闭环)

App 位置 / 动作 说明
资产卡 / 钱包余额 coin_account 一用户一行的金币+现金余额快照
金币明细 coin_transaction 每次金币变动一笔流水
现金明细 cash_transaction 每次现金变动一笔流水(分)
每日签到 signin_record + signin_boost_record 7 天循环发币;签到后看广告可膨胀一次
一次性任务(开消息提醒等) user_task 领一次发币
看激励视频赚金币 ad_reward_record + ad_watch_log + ad_ecpm_record 独立数据流:发奖 / 旧版观看时长 / 收益对账
信息流/Draw 广告结算 ad_feed_reward_record 每展示满 10 秒累计一份奖励,完成后一次性入账;ad_type(feed/draw)+feed_scene(compare/coupon)分形态/场景
(无 App UI)穿山甲后台收益对账 ad_pangle_daily_revenue 定时脚本拉 GroMore 数据 API 落日表(#92);admin 收益报表/大盘的「真实收益」侧
好友比价并下单发奖 / 邀请奖励金 invite_cash_transaction + coin_account.invite_cash_balance_cents 与现金物理隔离的第二本账(#82/#113):POST /order/report 触发 invite_reward 入账;source=invite_cash 提现出账
金币兑现金 coin_account + coin_transaction + cash_transaction exchange_out + exchange_in 两笔流水
提现到微信零钱 withdraw_order + wechat_transfer_authorization + cash_transaction 人工审核 + 微信商家转账
绑定微信(提现前置) user.wechat_* openid 唯一,一微信一账号

账号 / 反馈

App 位置 / 动作 说明
登录(极光/短信)/ 改资料 / 注销 user 登录主体,注册即登录
新手引导是否再展示 onboarding_completion 按 设备+账号 去重;登录响应回 onboarding_completed,走完引导时标记,跨卸载重装
帮助与反馈 feedback 含截图,后台人工处理
无障碍存活监控 / 掉线召回 device_liveness 无障碍服务周期心跳;worker 检出心跳超时 → 推送/客户端进 App 弹「开启自启动」引导

好友邀请(注册增长)

App 位置 / 动作 说明
输入/剪贴板邀请码绑定 invite_relation 注册即生效(绑定不发奖,#113);invitee_user_id 唯一;compare_reward_granted 幂等闸=好友比价并下单后才给邀请人发奖励金
落地页访问指纹(剪贴板归因兜底) invite_fingerprint 剪贴板没拿到码时,用 (ip+机型+屏幕) 7 天内反查邀请人
邀请奖励金入账/提现 invite_cash_transaction 独立账本(见上钱包节);余额在 coin_account.invite_cash_balance_cents

美团 CPS 群发联盟(私域社群比价,运营后台驱动 · 群发选品→点击→对账漏斗)

后台/用户动作 说明
运营建可推广活动 cps_activity 预存券/物料(美团 actId / 淘宝淘口令 / 京东链接)
运营建微信推广群 cps_group 一群一行,sid=美团二级渠道追踪位
后台批量生成群发短链 cps_link 群×活动 → /c/{code},运营复制到微信群
用户点短链(落地/复制口令) cps_click 记 visit/copy 事件,统计 PV/UV
微信内打开落地页授权 cps_wx_user 服务号网页授权拿 openid(base 静默)/ 昵称头像 unionid(userinfo,点领券触发),记首次来源群
定时拉美团联盟订单对账 cps_order query_order 按 sid 归群,串成点击→下单→佣金漏斗

埋点 / 首页门面 / 选品缓存

位置 / 动作 说明
客户端行为埋点 analytics_event POST /analytics/events 批量上报,一事件一行(#83);admin「埋点日志」检索
首页三统计数字 ops_stat_config real/manual/random 三模式,admin 配
首页轮播(省钱动态) ops_marquee_seed 种子条目,与真实 comparison_record 混播(数据源模式 mixed/real/seed 可切)
首页推荐/销量榜离线选品 meituan_coupon 美团 CPS 券缓存,定时 ETL 灌入;feed?tab=rectop-sales 纯库出、不实时打美团

运营后台 admin(独立子应用 app/admin/,端口 8771,独立鉴权)

后台模块 说明
管理员账号 / 登录 admin_user 与 C 端 user 完全隔离,独立 JWT + RBAC;pages_override 个人可见页覆盖
角色 / 可见页配置 admin_role 内建三角色 + 自定义角色(#117/#126);admin_user.role 按名引用
操作审计 admin_audit_log 每个写操作落一条,只增不改不删
运营可配置项(改奖励常量) app_config 空表 = 用代码默认;后台改了即覆盖
用户/钱包/提现/反馈管理 跨读写上面的 C 端表 见下「写入路径」admin 段

二、写入路径(什么操作 → 写哪张表)

C=插入新行 · U=更新已有行 · 同一行内多张表表示同一事务原子写

C 端(App 用户触发)

触发(用户动作 / endpoint / 回调) 写入 操作
登录 POST /auth/jverify-login/auth/sms/login user C(首次=注册)/ U(last_login_at);并 onboarding_completiononboarding_completed
走完新手引导 POST /user/onboarding/complete onboarding_completion(C) (user_id, device_id) 幂等,撞唯一约束即忽略
改昵称 PATCH /user/profile、传头像 POST /user/avatar user U
注销 DELETE /user user U(软删:phone→deleted_<id>status=deleted)
绑/解绑微信 POST /wallet/bind-wechat/unbind-wechat user.wechat_* U
签到 POST /signin/do signin_record(C) + coin_account(U) + coin_transaction(C signin) 同事务
签到膨胀 POST /signin/boost signin_boost_record(C) + coin_account(U) + coin_transaction(C signin_boost) 同事务;同日一次
领任务 POST /tasks/claim user_task(C) + coin_account(U) + coin_transaction(C task_<key>) 同事务
金币兑现金 POST /wallet/exchange coin_account(U) + coin_transaction(C exchange_out ) + cash_transaction(C exchange_in +) 同事务
发起提现 POST /wallet/withdraw withdraw_order(C reviewing,记 source) + coin_account(U 按 source 扣对应余额) + 流水(C :cash_transaction.withdrawinvite_cash_transaction.invite_withdraw) 同事务,不打款;#121 按 source 分账
查提现状态 / 用户取消 GET /wallet/withdraw/status withdraw_order(U) + 失败→对应账本退款流水(C withdraw_refund / invite_withdraw_refund +)
穿山甲发奖 S2S 回调 POST /ad/pangle-callback ad_reward_record(C)+ granted→coin_account(U)+coin_transaction(C reward_video/signin_boost) trans_id 幂等
看广告时长上报 POST /ad/watch-report ad_watch_log(C)
广告 eCPM 上报 POST /ad/ecpm-report ad_ecpm_record(C)
信息流广告结算 POST /ad/feed-reward ad_feed_reward_record(C)+ granted→coin_account(U)+coin_transaction(C feed_ad_reward) client_event_id 幂等
注册设备 / 更新 push token POST /device/register device_liveness(C/U upsert) (user_id, device_id) 幂等;只在传入非空时更新 registration_id/版本
无障碍服务心跳 POST /device/heartbeat device_liveness(C/U) 心跳也能自注册;accessibility_enabled=true 时刷 last_heartbeat_at、置 alive、清 notified_at
客户端 ack 掉线提醒 POST /device/liveness/ack device_liveness(U) kill_alert_pending(幂等;下次真掉线 worker 再置)
比价透传(帧0 / done / finalize)POST /intent/recognize/price/step/trace/finalize comparison_record(C running → U 终态) harvest 三段式(2026-07):帧0 mint trace_id 建 running 行 → done 写 success/failed → finalize 写 cancelled/failed(不降级 success);best-effort,写失败不连累透传
比价 done 上报 POST /compare/record(老客户端兜底) comparison_record(C 或 U) trace_id 幂等覆盖(唯一键已从 (user_id,trace_id) 改单列)
领里程碑 POST /compare/milestone/claim comparison_milestone_claim(C) 当前不发币(coin_awarded=0)
支付归因上报 POST /order/report savings_record(C source=compare)+ 触发邀请发奖:invite_relation(U compare_reward_granted)+coin_account(U invite_cash)+invite_cash_transaction(C invite_reward) (user_id, client_event_id) 幂等;#113 邀请发奖口径=好友比价并下单,compare_reward_granted 幂等闸,同事务
批量埋点 POST /analytics/events analytics_event(C 批量) 不强制登录;每批 ≤200 条
领券任务流水 POST /coupon/session coupon_session(C/U upsert) trace_id 幂等:发起建行、收尾更新同一行(#99)
重置新手引导 POST /user/onboarding/reset onboarding_completion(D 该 设备+账号 行) #114,删完成标记 → 下次登录重走引导
首次进 profile 省钱页且无真实记录 savings_record(C source=demo) 懒种子,ensure_seeded 按 user 幂等
上报更低价 POST /report price_report(C) comparison_record.best_price_cents 校验
提交反馈 POST /feedback feedback(C)
绑定邀请 POST /invite/bind invite_relation(C effective) invitee_user_id 唯一幂等。#113 起绑定不发奖——发奖延后到好友比价并下单(POST /order/report 行),发的是邀请奖励金非金币
落地页归因 POST /invite/landing-track invite_fingerprint(C) 剪贴板归因兜底线索,登录后用 (ip+机型+屏幕) 反查
领券首帧 POST /api/v1/coupon/step(step=0) coupon_prompt_engagement(C/U claim_started) (device_id, package, 北京日) 幂等;best-effort
领券每帧结果 POST /api/v1/coupon/step coupon_claim_record(C/U) (device_id, coupon_id, 北京日) 幂等;best-effort
领券跑完 POST /api/v1/coupon/step(action.command=done) coupon_daily_completion(C/U) (device_id, 北京日) 幂等;best-effort
拒绝领券引导窗 POST /api/v1/coupon/prompt/dismiss coupon_prompt_engagement(C/U dismissed) 同上;客户端通知(透传链路看不到拒绝)
重置今日弹窗 POST /api/v1/coupon/prompt/reset(开发) coupon_prompt_engagement(D 今日条) 删后今天又能弹

领券三表写库全 best-effort:/coupon/step 里写失败只 logger.warning、不连累领券返回;判断只看 device_id,user_id 可空旁路(资产留痕)。

admin 端(管理员触发,均额外写一条 admin_audit_log)

后台操作 写入 操作
手动增减金币 coin_account(U)+coin_transaction(C admin_grant/admin_deduct)+admin_audit_log(C) 同事务
手动增减现金(#95 支持目标账户) coin_account(U 按 account 选列)+ 流水(C:cash_transactioninvite_cash_transaction,admin_grant/admin_deduct)+admin_audit_log(C) 同事务;account=coin_cash/invite_cash 两本账各调各
改用户状态(禁用/启用)/ 调试链接权限 user(U status / debug_trace_enabled)+admin_audit_log(C) 同事务
审核通过提现(含批量) withdraw_order(U→pending/success/failed)+wechat_transfer_authorization(C/U)+失败时按 source 退款流水+admin_audit_log(C)
审核拒绝提现(含批量) withdraw_order(U→rejected)+按 source 退款流水(C withdraw_refund/invite_withdraw_refund)+admin_audit_log(C)
反馈采纳/拒绝/标记处理 feedback(U→adopted/rejected/handled + admin_reply/reject_reason)+采纳发币时 coin_account(U)+coin_transaction(C)+admin_audit_log(C) 同事务(#94/#105)
上报更低价审核 price_report(U→approved/rejected)+通过发币 coin_account(U)+coin_transaction(C)+admin_audit_log(C) 同事务
改运营配置 / 广告配置 / 反馈页二维码 app_config(C/U)+admin_audit_log(C) 同事务
轮播种子/数据源模式 ops_marquee_seed(C/U/D)+app_config(模式 mixed/real/seed)+admin_audit_log(C) #122/#123 预览/浏览只读
角色管理(#117/#126) admin_role(C/U/D)+admin_audit_log(C) 内建/在用角色不可删
管理员管理 admin_user(C/U/D #126)+admin_audit_log(C) 删除为物理删,不可删自己
CPS 建群/活动/生成短链/拉单对账 cps_group/cps_activity(C/U/D)、cps_link(C)、cps_order(C/U upsert;美团 query_order + 京东联盟 #90) 见 CPS 段
任意写操作 admin_audit_log(C,永不 U/D)

没有任何表会被业务流程物理 DELETE。注销是软删(改 user 行),其余只 C/U(领券 /prompt/reset/completed-today/reset 是开发用删除,非业务流程)。

CPS 群发联盟 & pricebot 内部上报(非 C 端用户、非 admin 常规管理触发)

触发 写入 操作
运营后台建活动 / 建群 cps_activity(C/U) / cps_group(C/U) app/admin/routers/cps.py;群含美团平台才分配 sid
后台批量生成短链 POST /admin/api/cps/referral-links cps_link(C) 每 群×活动 一条;美团经 sid 转链拿 target_url(同群同活动重复生成产生多条)
用户点群发短链 GET /c/{code} / POST /c/{code}/copy cps_click(C visit/copy) 公开端点不鉴权;group_id/sid 从 link 冗余进来免 join
微信落地页授权回调 GET /wx/oauth/cb cps_wx_user(C/U upsert) openid 幂等;base 拿 openid,userinfo 补昵称/unionid(非 None 才覆盖);任何失败兜底回落地页不阻断领券
定时拉联盟订单对账(美团 query_order + 京东联盟 #90) cps_order(C/U upsert) sid 归群;order_id 幂等(状态会变,重复拉则更新);京东单 platform='jd' + jd_valid_code 判有效
定时拉穿山甲 GroMore 后台收益(#92,systemd 每天 10:30) ad_pangle_daily_revenue(C/U upsert) 收益报表/大盘「真实收益」侧;与客户端上报的 eCPM 侧互为对照
定时 ETL 灌美团 CPS 选品库 meituan_coupon(C/U) feed?tab=rec / top-sales 纯库出的数据源
pricebot 比价 done 内部上报 POST /internal/price-observation price_observation(C 批量) (trace_id,platform,scope) 幂等;不走 JWT、靠 X-Internal-Secret(未配→503)
pricebot 比价 done 内部上报 POST /internal/store-mapping store_mapping(C/U 填空合并) trace_id 幂等;另有 lookup 反查 + invalidate deeplink 失效标记(淘宝/京东)
pricebot LLM 兜底放行启动确认窗后上报 POST /internal/launch-confirm-sample launch_confirm_sample(C) 都上报、不去重;靠 X-Internal-Secret(未配→503)
后台 worker 检出心跳超时 heartbeat_monitor_worker device_liveness(U mark_notified) notified + notified_at + kill_alert_pending=True;非 C 端、非 admin,定时扫 list_overdue

CPS 6 表与本 App user 无关(被推广群的用户在美团下单 / 在微信落地页授权,不是本 App 注册用户;cps_wx_user 是微信 openid 维度的另一套用户身份);price_observation/store_mapping/launch_confirm_samplesource_user_id/device_id 是可空旁路(链路不鉴权,匿名也记)。


三、表间关系 & Join Key

硬外键(数据库 FK 约束)

  • 19 张用户维度表 .user_iduser.id:coin_account(同时是 PK)、coin_transactioncash_transactioninvite_cash_transactionwithdraw_orderwechat_transfer_authorization(同时是 PK)、signin_recordsignin_boost_recorduser_taskcomparison_record(2026-07 起 user_id 可空——harvest 帧0 建行时软鉴权可能拿不到)、comparison_milestone_claimsavings_recordad_reward_recordad_watch_logad_ecpm_recordad_feed_reward_recordprice_reportfeedbackdevice_liveness
  • admin_audit_log.admin_idadmin_user.id
  • price_report.comparison_record_idcomparison_record.id(可空:关联记录被删后仍留上报历史)。
  • 邀请两表user.id:invite_relation.inviter_user_idinvite_relation.invitee_user_id(唯一)、invite_fingerprint.inviter_user_id——注意 FK 列名是 inviter/invitee_user_id,不是 user_id
  • CPS 6 表 + 比价沉淀 2 表 + launch_confirm_sample 均无硬 FK(全靠 sid/trace_id/openid 语义对齐,见下)。

语义 join key(无 FK 约束,靠业务字段对齐 —— 排障/对账必看)

  • coin_transaction.ref_id 指向随 biz_type:

    biz_type ref_id 指向 amount 符号
    signin 当天日期串(= signin_record.signin_date 的 ISO YYYY-MM-DD) +
    signin_boost 当天日期串(= signin_boost_record.signin_date 的 ISO YYYY-MM-DD) +
    task_<key> user_task.task_key +
    reward_video / ad_reward(历史) ad_reward_record.trans_id +
    feed_ad_reward ad_feed_reward_record.client_event_id +
    exchange_out null(兑现金,无单据)
    admin_grant / admin_deduct null(原因记在 remark=admin:<reason>) + /
  • cash_transaction.ref_id:

    biz_type ref_id 指向 amount 符号
    withdraw / withdraw_refund withdraw_order.out_bill_no(source=coin_cash 的单) / +
    exchange_in null +
    admin_grant / admin_deduct null(原因在 remark=admin:<reason>) + /
  • invite_cash_transaction.ref_id(邀请奖励金账本,#82):

    biz_type ref_id 指向 amount 符号
    invite_reward 被邀请人 user.id(字符串) +
    invite_withdraw / invite_withdraw_refund withdraw_order.out_bill_no(source=invite_cash 的单) / +
    admin_grant / admin_deduct null + /
  • comparison_record.store_namesavings_record.shop_name:无 id 关联,按店名字符串相等给比价记录打「已下单」标记(瞬态,不写库)。两边店名同源 = 比价意图识别阶段的门店 query,语义=店级(同店比价多次会一并标已下单)。

  • 广告流会话关联:ad_reward_record.ad_session_id 可与 ad_ecpm_record.ad_session_id 对齐;ad_watch_log 仍是旧版兼容统计,不逐条参与发奖。

  • 里程碑解锁进度不存库:comparison_milestone_claim 只记「哪几档已领」;进度 = comparison_recordstatus='success'count

  • 领券三表无硬 FK,全靠软关联:coupon_prompt_engagement / coupon_daily_completion / coupon_claim_recorduser_id 软指 user.id(可空、有登录态才记、不进唯一键、不阻塞判断);trace_id 软指 pricebot work_logs(排查回指);唯一键都以 device_id + 北京自然日为主(详见 coupon_state.md)。coupon_session(#99)同口径:trace_id 唯一 upsert、user_id 软指(admin 明细 LEFT JOIN 出手机号)。

  • analytics_event(#83)与 coupon_session 均无硬 FK:埋点/流水不鉴权也收,device_id/user_id 只作维度。

  • admin_user.role 按名语义引用 admin_role.name(无硬 FK,#117):删除保护在应用层(在用/内建角色不可删);个人 pages_override 优先于角色 pages

  • onboarding_completion.(user_id, device_id):user_id 语义关联 user.id(无硬 FK,同 coupon_* 设备表),device_id = 客户端硬件级 ANDROID_ID(≠ 领券 per-install device_id)。登录读、走完引导写,决定是否再展示新手引导。

  • CPS 群发 6 表全靠 sid / id / openid 语义串联(无硬 FK):cps_link.group_idcps_group.idcps_link.activity_idcps_activity.idcps_click.link_idcps_link.idcps_wx_user.first_group_idcps_group.id;点击与订单无法对到单笔,只在群维度(sid)汇合——cps_order.sidcps_group.sidcps_link.sidcps_click.sid(仅美团有 sid,淘宝/京东无)。统计按群聚合,故 group_id/sid 冗余进 cps_click 免 join。cps_wx_useropenid 自成用户身份维度,与 cps_click/cps_order 无 id 级 join。

  • 比价沉淀两表(price_observation / store_mapping):trace_id 软指 pricebot work_logs(与 comparison_record.trace_id 同源但不互 join,各存各视角);source_user_id / source_device_id 软指用户/设备(可空,匿名也记)。store_mapping.lookup店名字符串精确相等 + geo 取最近,非 id 级 join。

ER 关系(文字版)

user ─1:1─ coin_account
user ─1:1─ wechat_transfer_authorization
user ─1:N─ { coin_transaction, cash_transaction, invite_cash_transaction, withdraw_order,
            signin_record, signin_boost_record, user_task, comparison_record(user_id 可空),
            comparison_milestone_claim, savings_record, ad_reward_record, ad_watch_log,
            ad_ecpm_record, ad_feed_reward_record, price_report, feedback, device_liveness }
                                             (device_liveness 硬 FK; (user_id,device_id) 唯一)
user ─1:N─ onboarding_completion             (user_id, 无硬 FK; (user_id,device_id) 去重)
comparison_record ─1:N─ price_report        (comparison_record_id, 可空)
admin_user ─1:N─ admin_audit_log
app_config                                   (独立, 无外键, key 为主键)
coupon_prompt_engagement / coupon_daily_completion / coupon_claim_record
                                             (独立, 无硬 FK; 维度=device_id+北京日, user_id/trace_id 仅软关联)
coupon_session                               (独立, 无硬 FK; trace_id 唯一=一次领券任务一行, #99)
analytics_event                              (独立, 无硬 FK; 埋点事件流, device/user 仅维度, #83)
admin_role ◀──语义(role 按 name 引用, 无FK)── admin_user   (pages_override 个人覆盖, #117/#126)
ops_stat_config / ops_marquee_seed / meituan_coupon / ad_pangle_daily_revenue
                                             (独立运营/缓存/对账表, 无外键)
user ─1:N─ invite_relation                   (inviter_user_id 硬 FK);  invitee_user_id ─1:1─ user (唯一硬 FK)
user ─1:N─ invite_fingerprint                (inviter_user_id 硬 FK)
cps_activity / cps_group ──语义(无FK)──▶ cps_link ─1:N─ cps_click
                                             (CPS 6 表自成子系统; cps_order 经 sid 归群对账, 与本 App user 无关)
cps_group ◀──语义(无FK)── cps_wx_user        (first_group_id; openid 维度的微信落地页用户身份)
price_observation / store_mapping            (独立, 无硬 FK; 维度=trace_id, source_user/device 仅软关联; pricebot 内部上报)
launch_confirm_sample                        (独立, 无硬 FK; 都上报不去重; pricebot 内部上报启动确认窗兜底样本)

四、资金模型(金币 / 现金 / 提现,三层)

  1. 余额快照 coin_account:coin_balance(金币个数)+ cash_balance_cents(现金分)+ invite_cash_balance_cents(邀请奖励金分,#82),一用户一行,读取展示用。
  2. 流水账本 coin_transaction / cash_transaction / invite_cash_transaction:每次变动写一笔,balance_after* 记变动后余额,可逐笔回溯对账。现金与邀请奖励金是两本物理隔离的账——发放口径与提现对账各自独立。
  3. 唯一变动入口:金币走 repositories/wallet.grant_coins,邀请奖励金走 grant_invite_cash——都是「更新快照 + 写流水,不 commit,由调用方同一事务 commit」。signin / signin_boost / task / ad_reward / feed_ad_reward / exchange / admin 走 grant_coins;invite_reward / admin 调整走 grant_invite_cash,靠 biz_type 区分来源。
  • 汇率:10000 金币 = 1 元 = 100 分(rewards.COIN_PER_YUAN);兑换额必须是整分倍数。
  • 提现状态机:reviewing(发起即原子扣款、待人工审核、不打款)→ 审核通过 pending(微信转账在途)→ success / failed(失败自动退款);审核拒绝 rejected(退款)。withdraw_order.source 分账(#121):coin_cash 单的扣款/退款写 cash_transaction,invite_cash 单写 invite_cash_transaction;out_bill_no 幂等,孤儿 pending 单由 reconcile_pending_withdraws 对账兜底,admin withdraws/ledger-check 分账校验「单 ↔ 流水」。
  • 防超额:扣款用带条件 UPDATE ... WHERE <对应余额列> >= amount(按 source 选 cash_balance_cents / invite_cash_balance_cents),并发/重试不会双扣。

五、通用约定

README → 通用约定:金额存整数(金币计数 / 现金存分)、业务「今天」按北京时间日期串等值聚合(不在 SQL 跨时区比 date)、JSON 列 PG 用 JSONB·SQLite 退化 JSON、改表必写 alembic 迁移并保持单 head + 同步更新本目录文档。