marco
5f6593eaf2
fix(alembic): 缩短 withdraw 迁移 revision id 至 varchar(32) 内
...
drop_withdraw_active_unique_index (33 字符) 超过 alembic_version.version_num
的 varchar(32) 上限,生产 alembic 写版本号时 StringDataRightTruncation 报错、
部署中断。改短为 drop_withdraw_active_uniq_idx (29 字符):迁移逻辑(drop_index)
一字不变、无下游引用、仍为唯一 head。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-07-24 20:32:38 +08:00
guke
e11f506e1e
docs: 提现允许在途时继续提交申请 设计spec ( #173 )
...
## 摘要
取消「同一用户同时仅一笔在途提现」限制:已有 reviewing/pending 提现单时可继续发起新申请。
- 删应用层在途单检查(WithdrawTooFrequentError)+ 删 DB 分区唯一索引 ux_withdraw_order_user_active(含迁移)
- 清理失效死代码;IntegrityError 兜底瘦身为仅处理 out_bill_no 幂等
- 既有约束不变:建单先扣款(防超提)、coin_cash 每日档位次数、out_bill_no 幂等、解绑退款、admin 审核/对账均按单号维度
## 测试
- 新增:多笔在途并存放行(coin_cash & invite_cash)、第二笔仅受余额约束(409 现金余额不足)
- 迁移 upgrade→downgrade→upgrade 回环验证
- 提现域全绿(test_withdraw / test_invite_cash_withdraw / test_withdraw_ledger_check)
## 注意
- 客户端:每次提交需生成新的 out_bill_no;未开免确认时多笔 pending 各返回一个微信确认页,App 需能处理多笔待确认
- 无并发硬上限(产品拍板):coin_cash 由每日档位次数天然封顶,invite_cash 仅受余额约束
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #173
2026-07-24 16:40:57 +08:00
guke
addc30817f
修复:coupon_claim_event 回填按 (trace_id, coupon_id) 去重,避免上线迁移唯一约束冲突 ( #172 )
...
旧回填 INSERT ... SELECT 从 coupon_claim_record 整表 1:1 复制进 coupon_claim_event。
两表唯一键不同:源表按 (device_id, coupon_id, claim_date) 去重,trace_id 可空且不在
唯一键里;新表按 (trace_id, coupon_id) 唯一。一次会话的 /step 帧跨零点(Asia/Shanghai)
时,同一 (trace_id, coupon_id) 会落在相邻两天两行,复制时撞 uq_coupon_claim_event_trace_coupon,
整个迁移事务回滚,线上发车中断(线上命中 (1581db0e..., mt_banjia_zhoumo))。
回填改为按 (trace_id, coupon_id) 只取 id 最大(最近写入)的一行,SQLite/PostgreSQL 通用。
运行期写入路径(record_claims 按 (trace_id, coupon_id) upsert)本就无重复,仅此一次性回填未做防御。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #172
2026-07-24 15:12:20 +08:00
linkeyu
e165fcfc5e
修复:自动查单关闭不再标记为支付异常 ( #170 )
...
## 改动说明
- 自动查单关闭时不再写入微信提现配置异常列表。
- 保留自动查单状态字段、部署开关和运营开关,不改变 worker 实际行为。
- 明确自动查单属于非阻断运维能力,避免与“无法微信打款”混淆。
## 验证
- `pytest tests/test_withdraw_ledger_check.py tests/test_admin_config.py -q`
- 8 项测试全部通过。
- 本地管理 API 健康检查确认不再返回自动查单关闭问题。
---------
Co-authored-by: guke <guke@wonderable.ai >
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #170
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-24 14:51:31 +08:00
zuochenyong
3f7b5167fa
功能:新手引导视频 + 美团券首页分页索引 ( #167 )
...
Co-authored-by: guke <guke@wonderable.ai >
Co-authored-by: 左辰勇 <exinglang@gmail.com >
Reviewed-on: #167
Co-authored-by: zuochenyong <zuochenyong@wonderable.ai >
Co-committed-by: zuochenyong <zuochenyong@wonderable.ai >
2026-07-24 14:48:40 +08:00
linkeyu
9e88ca72d3
修复提现审核详情点击用户后提示操作失败 ( #171 )
...
## 问题现象
在“提现审核”页点击用户所在行后,提现单主详情可以打开,但用户统计和金币记录区域为空,页面连续提示“操作失败”。
## 原因说明
打开抽屉时前端会继续请求两个子接口:
- `/admin/api/users/{user_id}/reward-stats`
- `/admin/api/users/{user_id}/coin-records`
这两个接口原来都使用 `select(AdRewardRecord)` 加载完整 ORM 对象。SQLAlchemy 会把模型映射的所有列自动展开到 SQL 中,其中包括后来新增的 `boost_round_id`。当旧本地数据库或滚动发布中的数据库尚未补齐该列时,即使提现详情本身完全不使用这个字段,查询仍会报 `no such column: ad_reward_record.boost_round_id`,两个接口均返回 500。
前端的统一错误处理只会展示响应 JSON 中字符串类型的 `detail`;该 500 返回的是普通 `Internal Server Error`,因此最终回退成通用文案“操作失败”。本地前端开启了 React Strict Mode,初始化副作用在开发环境会执行两次,所以两个失败接口会形成截图中的四条“操作失败”提示。
## 修复方案
- 用户奖励统计只查询实际需要的 `ecpm_raw`、`coin` 等字段。
- 金币记录只查询页面展示、排序所需字段。
- 同步缩小信息流广告和签到记录的字段投影,避免将来新增无关 ORM 列再次拖垮详情页。
- 增加 SQL 级回归测试:主动拦截任何包含 `ad_reward_record.boost_round_id` 的详情查询,并验证两个接口仍返回 200。
该改动不会改变统计口径或返回结构。数据库迁移仍应正常执行;这里增加的是旧库及滚动发布期间的向后兼容保护。
## 验证结果
- `pytest tests/test_admin_read.py -q`:17 passed
- 新增回归测试覆盖 `reward-stats` 与 `coin-records`
- `git diff --check`:通过
- 本地实际提现用户接口验证:两个接口均返回 200
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #171
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-24 14:04:04 +08:00
linkeyu
21a4d0af5b
后台审核新增批量处理接口 ( #164 )
...
## 改动
- 新增低价审核与用户反馈的批量通过、批量拒绝接口
- 单条仍保持独立事务、审计、发奖和通知;单项失败不影响同批其它记录
- 批量响应返回每条记录的成功状态或失败原因,供前端保留失败项重试
- 反馈审核补充行锁,降低并发重复发奖风险
## 验证
- `ruff check`(相关路由、Schema、测试)
- `pytest tests/test_admin_write.py -q`:22 passed
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #164
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-24 12:04:41 +08:00
guke
f7a7a49281
fix(withdraw): 免确认授权已开启判定加 authorization_id 非空,与打款一致 ( #168 )
...
免确认授权已开启判定加 authorization_id 非空,与打款一致
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #168
2026-07-24 11:17:09 +08:00
linkeyu
71aef455f4
修复:签到膨胀金币归入看视频分类 ( #169 )
...
## 修改内容
- 将历史 `signin_boost` 金币从“常规任务金币”分类移出。
- 将 `signin_boost` 与 `reward_video/ad_reward` 一起计入“看视频金币”。
- 保留独立的 `signin_boost_coin_total` 历史审计字段。
- 增加不重不漏回归测试,确认分类调整前后本期发放总额保持不变。
## 本地验证
- `tests/test_admin_read.py`:16 项通过。
- Ruff 与 `git diff --check`:通过。
- 全量后端测试:505 项通过;8 项为 `main` 现有无关失败。
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #169
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-24 11:16:38 +08:00
linkeyu
66527f6cdc
修复:领券单券成功率按任务独立统计 ( #166 )
...
## 修改内容
- 新增按 `trace_id + coupon_id` 幂等的逐次单券事件表。
- 保留每日资产记录,同时独立保存每次领券事件,避免同设备同日重跑串场。
- 后台逐场成功率和单券明细改为读取逐次事件。
- 增加历史数据回填迁移、本地 mock 脚本和回归测试。
## 验证结果
- 领券相关测试:22 项通过。
- 全新 SQLite 数据库执行 `alembic upgrade head`:通过。
- 全量测试:508 项通过;另外 8 项为现有无关失败。
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #166
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-24 11:15:09 +08:00
linkeyu
b4a2a8c31d
功能:限制每位用户每天最多发起 100 次比价 ( #165 )
...
## 变更内容
- 登录用户按北京时间自然日计算比价发起次数,每人每天最多 100 次。
- 第 101 次起返回 HTTP 429,并提示“今日已比价超过100次,请明天再试”。
- 同一 trace_id 的网络重试按幂等处理,不会重复计数。
- 使用用户行锁串行化同一账号的并发请求,避免并发突破上限。
- 复用现有 comparison_record 的 running 记录,无需新增数据库迁移。
## 本地验证
- 比价额度专项测试 11 项通过。
- 本次修改涉及文件的 Ruff 检查通过。
- 已覆盖未登录、重复 trace_id、跨自然日、第 100 次放行及第 101 次拒绝。
---------
Co-authored-by: CodexSandboxOffline <798648091@qq.com >
Reviewed-on: #165
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-24 11:14:31 +08:00
marco
ceceeb3458
fix(db): 合并 3 个 alembic 迁移 head(发车 0.4.4 前置,no-op merge 节点)
2026-07-23 17:55:35 +08:00
linkeyu
31f61f6aad
增强:CPS 每日自动对账增加可回查日志 ( #163 )
...
## 背景
PR #162 已合并。本 PR 为其后续日志增强,方便人工按一次任务完整回查美团、京东每日自动对账。
## 日志内容
- 每次运行生成唯一 `run_id`,记录 `scheduled` / `startup_catchup` 触发来源
- 记录北京时间计划日期、近 3 天回拉窗口、环境、数据库方言、主机名和 PID
- 分平台记录开始、成功、跳过、失败及执行耗时
- 成功结果记录 fetched / inserted / updated / pages / api_requests,京东额外记录小时窗口数
- 京东上游失败记录具体小时窗口、页码和请求序号;美团记录失败页码
- 最终汇总记录 success / partial_success / failed、失败平台、是否需要人工补跑和下次执行时间
- 日志使用现有 `extra` 结构化字段写入 JSON 日志,便于 SLS/人工检索
## 兼容性
- 手动对账接口、事务和返回模型不变
- 不记录密钥、Token、完整上游响应或订单明细
- 仓储层仅增加可选审计上下文和请求计数;不改变拉取及 upsert 逻辑
## 验证
- `pytest tests/test_cps_reconcile_worker.py tests/test_cps_admin.py tests/test_admin_read.py tests/test_observe.py -q`:44 passed
- 相关文件 Ruff 检查通过(忽略文件原有 UP017 提示)
- `git diff --check` 通过
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #163
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-23 11:57:37 +08:00
linkeyu
b7cfcf7495
功能:美团和京东 CPS 每日自动对账 ( #162 )
...
## 需求
- 保留现有后台手动对账逻辑不变
- 每天北京时间 05:00 自动刷新 CPS 对账
- 当前仅处理美团和京东
- 每次按更新时间回拉近 3 天,覆盖延迟更新和订单状态变化
## 实现
- 新增进程内 CPS 自动对账 worker,并接入应用生命周期
- 美团使用更新时间查询类型 2,京东使用更新时间查询类型 3
- 复用现有仓储层对账及 order_id 幂等更新逻辑
- 美团和京东独立会话、独立异常处理,单个平台失败不阻塞另一平台
- 增加单实例锁、开关、执行小时、回拉天数和轮询间隔配置
- 服务在 05:00 后重启时会补跑当天任务
## 验证
- `pytest tests/test_cps_reconcile_worker.py tests/test_cps_admin.py tests/test_admin_read.py -q`:27 passed
- 相关文件 Ruff 检查通过
- `git diff --check` 通过
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #162
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-23 10:51:20 +08:00
linkeyu
77f772f47c
性能:比价和领券分位数改为 PostgreSQL 聚合 ( #161 )
...
## 背景
- 比价记录页和领券记录页前端已经只拉当前页,并直接展示后端 summary。
- 原后端仍会将筛选区间内的全部耗时值取回 Python 计算分位数,生产数据量增大后会放大数据库读取和应用内存开销。
## 修改内容
- 比价记录:成功耗时 P5/P50/P95/P99、平均耗时以及中途退出耗时 P5/P50/P95 改用 PostgreSQL percentile_cont/AVG 聚合。
- 领券记录:发起数、完成数、平均耗时和完成耗时 P5/P50/P95/P99 合并为一条 PostgreSQL 聚合查询。
- 日期、用户、环境、状态、店铺和商品等筛选条件继续与列表共用,统计口径不变。
- SQLite 不支持 percentile_cont,仅在本地和测试环境回退读取耗时单列;不加载完整业务记录。
- API 字段与前端展示保持不变,无需前端改动。
## 验证
- 比价/领券及关联广告收益、点位、按券统计测试:26 passed。
- 本次涉及文件 ruff 检查通过。
- PostgreSQL SQL 编译测试确认使用 ordered-set percentile_cont 聚合。
- 全量测试:497 passed;8 个现有失败集中在邀请奖励、提现档位和代理转发等无关模块。
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #161
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-23 10:35:41 +08:00
linkeyu
cb8e8ccc1d
修复:激励视频未完成时预估收益归零 ( #160 )
...
## 问题
激励视频在 onAdShow 时已经上报 eCPM,用户随后提前关闭或播放时长不足时,报表仍按 eCPM/1000 计入预估收益,导致明细、合计、趋势和分类统计虚高。
## 修改
- reward_video 的 closed_early / too_short 有效预估收益统一归零
- capped / granted 保持原收益口径
- 更新 API 字段说明
- 增加明细、日汇总、小时汇总、类型汇总回归测试
## 验证
- ruff check(本次修改文件)通过
- pytest tests/test_admin_ad_revenue_scope.py tests/test_admin.py -q:10 passed
- 全量 pytest:489 passed,7 个失败已在未修改的 origin/main 基线复现,与本次改动无关
## 关联前端
WonderableAI/shaguabijia-admin-web#64
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #160
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-22 17:38:51 +08:00
linkeyu
fda82fe313
后台:新增监控审计权限分组并加强接口鉴权 ( #159 )
...
## 变更
- 权限目录新增一级分组“监控审计”,统一设备存活、埋点成功率、埋点日志和审计日志。
- 补齐 `analytics-health` 页面权限,技术角色默认拥有四项监控审计权限;运营默认仅保留设备存活。
- 新增服务端 `require_page` 守卫,四组 API 不再只依赖前端隐藏导航,直接调用也会校验角色或个人页面权限。
- 增加迁移,为存量技术角色补上 `analytics-health` 权限,并同步接口文档。
## 验证
- 改动文件 `ruff check` 通过。
- `tests/test_admin_roles.py tests/test_analytics_health.py`: 24 passed。
- Alembic 从空库 upgrade 到 head,再 downgrade 本迁移:通过。
- 全量测试:443 passed、6 failed;6 项失败在干净 `origin/main` 上原样复现(主干为 442 passed、6 failed),与本 PR 无关。
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #159
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-22 17:22:09 +08:00
zuochenyong
28a86c3b2c
feat(compare): 比价记录列表增加分页 ( #156 )
...
GET /api/v1/compare/records 新增 ordered / keyword 两个查询参数,过滤全部下推到 SQL。
不能分页之后再由客户端 filter —— 一页里可能一条都不命中,列表看着就是空的,
得翻很多页才蹦出一条。
顺带修掉这条链路上几处随数据量线性变慢的地方:
- 列表查询 defer raw_payload / llm_calls / llm_price_snapshot 三个重型 JSON 列。
出参 ComparisonRecordOut 根本不读,却是每页几百 KB~几 MB 的白读 + 白反序列化,
是「比价记录/全部记录」页慢的主要来源;详情接口不 defer,raw_payload 照常返回。
- 「已下单」标记改为只按本页店名(≤ limit 条)反查 savings,不再把该用户全部下单
店名捞进内存跟 50 条记录取交集。
- 新增 (user_id, created_at, id) 复合索引:反向扫恰好等于列表的
ORDER BY created_at DESC, id DESC,PG 免排序直接取前 n 条。
迁移走 CREATE INDEX CONCURRENTLY,不阻塞线上 harvest 写入。
- keyword 转义 LIKE 通配符后再匹配,避免搜一个「%」把整表拉回来。
- nginx 对 application/json 开 gzip:此前 gzip off + gzip_types 只含 text/html
+ gzip_proxied off 三个默认值凑一起,等于所有接口都在裸奔;记录列表这种
字段名和中文店名高度重复的 JSON 压缩比稳定 8~10 倍。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: 左辰勇 <exinglang@gmail.com >
Reviewed-on: #156
Co-authored-by: zuochenyong <zuochenyong@wonderable.ai >
Co-committed-by: zuochenyong <zuochenyong@wonderable.ai >
2026-07-22 17:18:26 +08:00
Ghost
0717c09721
基于 main 接入各厂商直推服务端 ( #118 )
...
改动:新增厂商推送配置、设备 push_vendor/push_token 字段、device push-test 接口、心跳超时厂商直推发送逻辑和对应测试。
验证:python -m pytest tests/test_device_push.py tests/test_auth.py tests/test_health.py 通过。
---------
Co-authored-by: guke <guke@wonderable.ai >
Co-authored-by: 左辰勇 <exinglang@gmail.com >
Co-authored-by: lowmaster-chen <1119780489@qq.com >
Reviewed-on: #118
Co-authored-by: Ghost <>
Co-committed-by: Ghost <>
2026-07-22 15:42:25 +08:00
linkeyu
2eb36b44c8
fix(admin): 按任务白名单聚合常规任务金币 ( #157 )
...
## 背景
大盘“常规任务金币”原先采用“全部正向金币减排除清单”的反向口径。线上新增 `feed_ad_reward_coupon` / `feed_ad_reward_comparison` 后未同步加入排除清单,导致领券和比价奖励误计入常规任务金币。
## 修改
- 改为明确白名单:`signin`、历史 `signin_boost`、全部 `task_` 任务、`price_report_reward`、`feedback_reward`
- 未知新 `biz_type` 默认不进入常规任务桶
- 增加覆盖领券、比价、广告、邀请、管理员及未知类型的回归测试
- 顺带修复改动文件已有的 Ruff `UP017`
## 验证
- 线上只读 PostgreSQL:新口径全量为 108,022,领券/比价误计差额为 450,675
- Ruff:通过
- `pytest tests/test_admin_read.py tests/test_cps_admin.py -q`:23 passed
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #157
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-22 12:09:06 +08:00
linkeyu
510df176b3
feat(admin): 返回逐场领券点位分数与明细 ( #153 )
...
## 变更内容
- 按 trace_id 批量统计每场领券成功数/尝试数
- success、already_claimed 计成功,failed 计尝试,skipped 排除
- 返回每个点位的名称、ID、状态和失败原因
- 无有效逐券埋点时返回空值,不伪造 0/0
- 用户领券记录抽屉同步返回点位分数及明细
## 性能
- 当前页全部 trace_id 一次批量查询,不产生逐行请求
## 验证
- 16 项后端测试通过
- 覆盖成功、已领、失败、跳过及失败原因
---------
Co-authored-by: guke <guke@wonderable.ai >
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #153
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-22 11:46:00 +08:00
zuochenyong
73970087ff
feat(ad): 膨胀弹窗改用服务端权威金额 + 本轮累计口径,下线 signin_boost ( #154 )
...
Co-authored-by: guke <guke@wonderable.ai >
Co-authored-by: 左辰勇 <exinglang@gmail.com >
Reviewed-on: #154
Co-authored-by: zuochenyong <zuochenyong@wonderable.ai >
Co-committed-by: zuochenyong <zuochenyong@wonderable.ai >
2026-07-22 10:53:21 +08:00
linkeyu
9286b82b6d
feat(admin): 优化比价记录概览统计与加载 ( #152 )
...
## 变更内容
- 新增比价记录概览聚合接口,主耗时均值及 P5/P50/P95/P99 仅统计 success
- 比价列表支持按北京自然日过滤,概览和列表复用同一筛选口径
- 后端聚合成功率、成本、低价率及中途退出指标
- 修复单条样本分位数错误返回 0 的边界问题
- 增加成功/失败/退出及日期分页回归测试
## 验证
- Python compileall
- SQLite 聚合、日期边界和分页检查
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #152
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-22 10:42:24 +08:00
linkeyu
f9a62bffbe
功能:广告收益支持环境与业务代码位筛选 ( #155 )
...
## 修改内容
- 客户端预估与穿山甲汇总统一支持正式、测试、全部环境筛选
- 支持业务代码位与全部代码位两种对账范围
- 保留已上线正式业务代码位,避免配置切换后历史报表漏数
- 补充接口文档和筛选口径测试
## 本地验证
- 相关后端测试 10 项通过
- Ruff 检查通过
- 本地后台页面联调通过
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #155
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-22 10:41:52 +08:00
linkeyu
beadce31ed
fix(ad): 服务端强制不足一秒曝光收益为零 ( #150 )
...
## 改动
- eCPM 上报新增可选 exposure_ms,兼容旧客户端
- exposure_ms < 1000 时保留展示记录并强制有效 eCPM 为 0
- 失败领券任务允许保留短曝光零收益 trace,后台显示 0 而不是未填充
- 其他失败后的迟到曝光仍按原规则解绑 trace
## 验证
- 相关 pytest:10 passed
- Ruff:通过
- compileall:通过
依赖:先合并 Server #149。
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #150
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-21 13:53:10 +08:00
guke
f39467ec08
docs(welfare): 15天不活跃清零金币/现金 设计文档(spec) ( #151 )
...
对齐前端首页可见事件home_visible
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #151
2026-07-21 13:52:40 +08:00
linkeyu
1f874819fd
fix(ad): 失败领券任务不再归属迟到广告收益 ( #149 )
...
## 修复内容
- coupon 广告上报到达时校验对应领券 session 状态
- session 已 failed 时保留全局广告收益记录,但清空 trace 归属,失败明细不再显示收益
- 增加失败 trace、其他场景和未知 trace 的回归测试
## 验证
- 相关 pytest:7 passed
- Ruff:通过
- compileall:通过
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #149
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-21 13:41:28 +08:00
zuochenyong
c53ce896f7
feat(huawei-review): 华为审核开关(admin 可切 + 客户端下发 + 审计) ( #147 )
...
华为应用市场审核要求新手引导的「快速设置」权限步必须可被用户关闭,平时又要
保住权限开启率,故做成后台可切的两态开关,送审期间切开、过审后收回。
- app_config 新增 huawei_review 行(default / review),空库与脏值一律回退
default = 上线至今的现状,宁可不给退出按钮也不误放开
- admin: GET/PATCH /admin/api/huawei-review,权限 operator/tech,切换写审计
- 客户端: GET /api/v1/platform/huawei-review 不鉴权(引导页在登录前就展示),
下发 onboarding_closable;机型 gate 由客户端做,故此处不判 ROM
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: 左辰勇 <exinglang@gmail.com >
Reviewed-on: #147
Co-authored-by: zuochenyong <zuochenyong@wonderable.ai >
Co-committed-by: zuochenyong <zuochenyong@wonderable.ai >
2026-07-21 10:43:58 +08:00
linkeyu
ed26935b14
feat(admin): 数据大盘比价指标改为后端聚合 ( #146 )
...
## 修改内容
- 数据大盘比价指标改由后端按日期区间聚合
- 增加完成数、中途退出数、成功率、中位数、P95 和 TOKEN 总成本
- 成功率分母排除中途退出,耗时仅统计 success/failed
- 增加后端聚合口径测试
---------
Co-authored-by: unknown <798648091@qq.com >
Reviewed-on: #146
Co-authored-by: linkeyu <linkeyu@wonderable.ai >
Co-committed-by: linkeyu <linkeyu@wonderable.ai >
2026-07-21 10:11:39 +08:00
guke
48037f03fd
docs: OpenObserve 接口 QPS/耗时可观测设计 spec ( #145 )
...
openobserve上报
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #145
2026-07-20 18:55:38 +08:00
guke
130a7dff29
docs(welfare): 15天不活跃清零金币/现金 设计文档(spec) ( #144 )
...
修改INACTIVITY_RESET_ENABLED语义,为false时清空金币操作只记录审计日志,不执行操作。
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #144
2026-07-19 00:14:03 +08:00
guke
a86688ccfb
feat(welfare): 15 天不活跃清零(金币+折算现金,邀请金不清)+ 预警 + admin 活跃口径统一 ( #143 )
...
背景 / 目标
连续 15 天(北京自然日)没有「首页可见 / 发起比价 / 发起领券」行为的用户判定为流失,每日自动清零其金币 + 折算现金;清零前按可配置节奏预警;全程留审计。纯登录不算活跃;邀请奖励金物理隔离、不清(产品红线)。
改了什么
活跃口径共享模块 app/repositories/activity.py:worker(清零/预警)与 admin(最近活跃)单一真源,防漂移。
清零业务逻辑 inactivity.py:选取 / 逐用户清零(行锁 + 幂等)/ 预警分档 + streak 去重 / run_once 组合,预警故障逐用户隔离、绝不阻塞清零。
每日 worker inactivity_reset_worker.py(仿 daily_exchange:文件锁 + 北京日守卫 + RUN_HOUR 门槛 + 总闸)+ main.py lifespan 接线。
可插拔通知器 notifier.py(v1 LogNotifier 日志占位,预留 JPush/短信)。
配置 INACTIVITY_*(阈值 / 预警档 / 执行点 / 通道 / 开关)。
admin 最近活跃口径改用共享模块(移除 last_login_at、纳入 show/home、以 created_at 为基线)。
审计:inactivity_reset_log + inactivity_notification_log 两表 + 钱包流水双写(biz_type=inactivity_reset,ref_id 交叉)。
文档:设计 spec / 实现 plan / docs/database/ 两表字典。
关键产品决策
邀请金不清:只清金币 + 折算现金,invite_cash_balance_cents 原封(仅快照入审计)。
活跃口径 = max(created_at, 首页可见, 比价, 领券),不含 last_login_at;首页可见 = event=show + page=home。
时间边界:北京自然日 0 点对齐(见 activity.reset_cutoff)。
总闸默认关,灰度验证后再开。
数据库变更
新表:inactivity_reset_log、inactivity_notification_log。
analytics_event 新增复合覆盖索引 ix_analytics_event_active (event, page, user_id, created_at)(活跃口径聚合热点)。
修复了 base 上的迁移多头(135e79414fd0 与 phone_rebind_log 同从 comparison_llm_cost 分叉)→ 加空 merge 修订,alembic upgrade head 恢复单头正常。
⚠️ 上线注意(合并后 / 开总闸前)
INACTIVITY_RESET_ENABLED 默认 false;开启前提 = show/home 埋点全量铺满——否则"只登录不操作"且注册满 15 天的老用户会落到 created_at 基线被误清。
前端依赖:Android 端需在首页可见上报 event=show + page=home(携带登录后的 user_id)。
admin「最近活跃」口径变化(去登录、纳入 home_view、created_at 基线):属预期变化,需产品/运营知会;与 DAU(_period_active_user_ids,仍含登录)是两套指标。
清零对 C 端「金币流水」可见(biz_type=inactivity_reset,备注「15天不活跃清零」)。
灰度:先只看预警/清零名单对不对,再开总闸。
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #143
2026-07-18 19:11:45 +08:00
guke
061f6baaf1
feat(auth): 微信登录与手机号绑定 / 占用冲突处理(M2 + M3 §10) ( #139 )
...
## 背景 / 需求
新增「微信登录」链路:用户用微信授权登录 App。openid 已绑账号即直接登入;
未绑则走「绑手机号」建号,并处理「手机号已被别的账号占用」的冲突(M2),
以及绑微信后展示昵称/头像的替换策略(M3 §10)。
关键取舍:微信登录**不套用**钱包提现里 bind-wechat 的「撞号即 409」逻辑
——登录场景 openid 命中就应登入,不能因撞号把用户挡在门外。
## 改动概览:新增 5 个端点(前缀 `/api/v1/auth`)
| 方法 | 路径 | 作用 |
|---|---|---|
| POST | `/wechat-login` | code→openid。命中已绑用户→直接登入;未命中→签发 `bind_ticket`,返回 `need_bind_phone`(**此刻不建号**);未配 APP_ID/SECRET→503 |
| POST | `/wechat/bind-phone/sms` | 持 `bind_ticket` + 手机号 + 短信码绑号 |
| POST | `/wechat/bind-phone/jverify` | 持 `bind_ticket` + 极光本机号一键取号绑号 |
| POST | `/wechat/conflict/continue` | 占用冲突·继续绑定=**登录老账号**(老号没绑微信则把 openid 绑上;已绑别的微信则只登入、丢弃本次 openid) |
| POST | `/wechat/conflict/rebind` | 占用冲突·换绑=**单事务**注销老账号 X(腾号)+ 用该号重建新账号 Y + 写换绑台账;受 30 天限制 |
绑号两条取号路径(sms / jverify)尾段共用 `_finish_wechat_bind`:
手机号未注册→建微信账号(channel=wechat,昵称头像取微信)登入;
已被占用→返回 `phone_occupied`(带占用账号昵称/头像/注册时间/`has_wechat`、
`conflict_ticket`、`rebind_available`、`rebind_blocked_days`),交前端冲突页。
冲突页三选一,后端提供 continue / rebind 两个动作(「取消」为前端本地行为)。
## 关键设计
**两种短时令牌(`core/security.py`,复用 `JWT_SECRET_KEY`,靠 `typ` 区分):**
- `bind_ticket`(typ=wechat_bind,sub=openid,附微信昵称/头像,TTL 10min):
openid 未命中时下发,覆盖「授权→输手机号→收码→验码」整个绑定流程。
- `conflict_ticket`(typ=wechat_conflict):比 bind_ticket **多编码已验证的手机号**;
换绑 / 继续绑定只认票里的 phone,堵住「拿别人手机号去夺号」的接管漏洞。
**30 天换绑限制:** 新增台账表 `phone_rebind_log`(手机号级、渠道无关),
记录「腾号重建」这一破坏性事件,靠 `rebound_at` 算窗口;
阈值走配置 `PHONE_REBIND_LIMIT_DAYS`。命中限制的换绑请求返回 409。
**M3 §10 昵称/头像替换:** 绑微信默认用微信昵称/头像替换展示,
抽成共享 helper,登录绑号路径与钱包绑微信路径两处共用
(故 `wallet.py`、`tests/test_withdraw.py` 一并有改动)。
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #139
2026-07-17 21:49:10 +08:00
liujiahui
b395648b7c
fix(withdraw): 新人档提现没成功也被判定已用(解绑微信退回后 0.1 永久锁死) ( #142 )
...
## 现象
新人提现 0.1 元,提现审核中时解绑微信 —— 现金正常退回,但「新人领取的 0.1 元」被判定为已使用:该档位从提现页永久消失,重提报 400。
## 根因
新人档(0.1/0.3)「一次性」的判定口径只看**有没有发起过**,完全不看提现单的最终状态。`withdraw_tier_states()` 里的 `used_newbie` 查询没有任何 `status` 过滤:
```python
select(WithdrawOrder.amount_cents).distinct().where(
WithdrawOrder.user_id == user_id,
WithdrawOrder.source == "coin_cash",
WithdrawOrder.amount_cents.in_(newbie_amounts),
# ← 没有 status 条件:只要「存在这条单」就算用过
)
```
而解绑微信时,`refund_reviewing_withdraws_on_unbind` → `_refund_withdraw` 把待审核单**退回现金 + 置 `failed`**,但记录仍在库里。两者相撞:那条 `failed` 的单照样落进 `used_newbie` → **钱退了,资格没退**。
## 改法
判定口径从「发起过没有」改为「钱是不是真到手 / 正在路上」——只认 `reviewing`/`pending`/`success`:
| status | 含义 | 钱在哪 | 新人档 |
|---|---|---|---|
| `reviewing` / `pending` | 待审核 / 打款在途 | 已扣,挂在单上 | 占用(防重复发起) |
| `success` | 打款成功 | **进了用户微信** | 用掉,永久锁死 |
| `failed` | 转账失败 / **解绑退回** | 已退回余额 | **恢复可提** |
| `rejected` | 审核拒绝 | 已退回余额 | **恢复可提** |
核心即查询加一行 `WithdrawOrder.status.in_(_NEWBIE_TIER_HELD_STATUSES)`。
- 下发(`withdraw-info`)与下单校验(`create_withdraw`)共用 `withdraw_tier_states`,**改一处两处同时生效**,不会出现「显示能提、点了 400」。
- 复用现有 `status` 语义,**无新字段、无 alembic 迁移**。
- **存量受害用户上线后自动恢复**(他们那条单是 `failed`,新查询天然排除它),无需数据修复脚本;已成功提现过的(`success`)仍正确锁死,不会误恢复。
## ⚠️ 行为变更
7-9 定的「新人档发起就算(含被拒/失败)」按本次拍板**放宽为「没成功打款就恢复」**。
常规档(0.5 / 10 / 20)的「当天名额发起就算、被拒不退」规则**一行未动**。
## 🔴 测试按要求不进本 PR —— 但既有测试会失败,合入前需同步处理
本 PR 只含源码改动。**既有测试 `tests/test_withdraw_tiers.py::test_newbie_tiers_independent_and_once_forever` 锁死的正是本次推翻的旧口径**(它断言「被拒后 0.1 消失、重提 400」)。
已实测(main 上的原版测试 + 本 PR 的新代码):
```
FAILED tests/test_withdraw_tiers.py::test_newbie_tiers_independent_and_once_forever
1 failed, 5 passed
```
**本 PR 合入前需同步调整该测试,否则 main 会红。** 本地已备好可用的测试改动(反转该断言,另加「解绑退回后恢复」回归 + 「成功打款后永久锁死」),需要即可单独提供。
## 待办
- 上述既有测试需同步调整(否则合入后 main 红)
- 纯后端逻辑改动,**未真机验证**
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: no_gen_mu <liujianhishen@gmail.com >
Reviewed-on: #142
Co-authored-by: liujiahui <liujiahui@wonderable.ai >
Co-committed-by: liujiahui <liujiahui@wonderable.ai >
2026-07-17 16:34:55 +08:00
zhuzihao
e9fd51d119
fix(auth): 发码防刷改为按成功计数 + 新增每设备每日发码上限 ( #136 )
...
- 修复:发码限流原为原子「判+记」,被单号 60s 冷却挡下的重发也占设备额度
→ 正常用户连点重发可能被误锁 1 小时。改为「先判后记、只对成功发码计数」:
check 判在真发之前(超限直接 429、不真发),record 只在 send_code 成功后调;
被单号冷却 / 供应商失败抛 429 时直接返回、不计数。
- 新增:同一设备(device_id)+ IP 每天最多 20 次发码上限,与原每小时 5 次两道闸并存,
均按成功计数,叠一层日封顶挡低频长时间轰炸。
- 基建:ratelimit.py 新增 RateLimitRule + check_rate_limits / record_rate_limits
(peek/commit 拆分);原子的 enforce_rate_limit 仍保留给登录爆破(失败也计)不变。
- 测试:补 2 个用例(冷却挡下不占额度 / 每日上限)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #136
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-07-16 09:40:56 +08:00
guke
5c6840dd71
feat(compare): 比价记录 LLM token 成本落库与展示(按当时价冻结) ( #133 )
...
- comparison_record 加 llm_cost_yuan(元/float)+ llm_price_snapshot(JSON)两列
- _backfill_llm_calls 回填时按 app_config 当时单价逐模型算成本、冻结成本+快照到记录
- app_config 新增 llm_token_price 配置(per_model + default 兜底,运营在系统配置页可改)
- services/llm_cost.py:compute_llm_cost 纯函数(按 model 分桶、error/无 usage 跳过、
脏价格当 unpriced 不抛异常以免连累 token 回填)+ get_llm_prices reader
- admin schema 暴露成本:列表项带 llm_cost_yuan,详情另带价格快照
- tests/test_llm_cost.py(10 测试);scripts/seed_mock_llm_cost.py(mock seeder)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #133
2026-07-13 17:46:11 +08:00
marco
824045dd19
fix迁移问题
2026-07-11 13:47:12 +08:00
guke
930eff822c
feat(ad-revenue): 领券/比价看板逐次广告收益(ad_ecpm.trace_id + 逐页聚合) ( #131 )
...
## 背景
admin「领券数据」「比价记录」两个看板此前只能看到场景级(所有领券/比价)的广告收益,
无法定位「这一次领券/比价具体赚了多少」。根因:收益表 `ad_ecpm_record` 缺 `trace_id`,
无法与领券会话 / 比价记录按 trace 关联。
## 改动
- **模型/迁移**:`ad_ecpm_record` 新增 `trace_id`(String(64), index, nullable);
迁移 `ad_ecpm_trace_id` 加列 + 索引 `ix_ad_ecpm_record_trace_id`,并**收敛当前两个
alembic head**(`11c44afbea58` selfstat + `merge_pages_override_coupon_slot`)为单 head。
- **上报链路**:`EcpmReportIn` 增 `trace_id` 字段;`/api/v1/ad/ecpm-report` 透传;
`create_ecpm_record` 落库。
- **收益聚合**:新增 `revenue_yuan_by_trace(db, trace_ids)`——按 trace_id 聚合展示收益,
单条 = `min(eCPM元, ¥500钳顶)/1000`,与广告收益报表 `ad_revenue.py` **同口径**;
只吃当前页的 trace_id(逐页批量,索引命中,无 N+1)。
- **两个看板**:`CouponDataRow` / `AdminComparisonListItem` 增 `ad_revenue_yuan`;
`coupon_data_report`(主表 + 用户抽屉)与 `list_comparison_records` 分页后逐页补该字段。
- **测试**:`tests/test_ad_ecpm_trace_revenue.py`(聚合/钳顶/落库)、
`tests/test_board_ad_revenue.py`(两看板 + 抽屉)。
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #131
2026-07-10 22:14:00 +08:00
marco
285e46ebaf
迁移处理
2026-07-10 19:23:12 +08:00
liujiahui
3630fb7b3a
提现档位后端权威化:tiers下发+每日限次/选一额度+下单档位闸(7-9) ( #129 )
...
## 提现档位后端权威化(7-9,配套 android 同名分支 PR)
### 规则(2026-07-09 与产品逐条拍板)
- 档位硬编码 `rewards.WITHDRAW_TIERS_COIN_CASH`:0.1/0.3(新人,历史一次性)+ 0.5(日3次)/10/20(日1次)
- 计次口径「发起就算」:当天创建的单不论最终状态(含被拒/失败)都占名额
- 新人档:任意状态发起过即永久消失;两档独立同天可各提一次;不参与「每日选一个额度」互斥
- 常规三档每天只能选一个;invite_cash 无档位概念(tiers 空、下单不走档位闸,邀请页行为不变)
- 「今天」= 北京时 cn_today();计次与 admin 看板同口径(Beijing 0点转 UTC 比较 created_at)
### 改动
- `GET /wallet/withdraw-info` 新增 `source` 参数 + 响应 `tiers[]`(amount/label/badge/available/disabled_reason/remaining_today)
- `create_withdraw` 加档位闸:coin_cash 仅可提预设档位且该档可提,否则 400/409(防绕过客户端刷);放在幂等返回/在途互斥之后,不破坏同号重试
- 0.01 调试直发(allow_sub_min)不受档位约束,保持原样
### 测试
- 新增 `tests/test_withdraw_tiers.py` 6 项全过(档位下发/新人独立+一次性/选一额度/次数耗尽/非档位金额拒绝/invite 不受影响)
- 全量回归 305 过;5 项失败为 main 既有(coupon_proxy/invite_compare_reward,stash 验证与本 PR 无关)
- 3 处旧测试的 coin_cash 金额从 100/200 调整为合法档位 50
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: no_gen_mu <liujianhishen@gmail.com >
Reviewed-on: #129
Co-authored-by: liujiahui <liujiahui@wonderable.ai >
Co-committed-by: liujiahui <liujiahui@wonderable.ai >
2026-07-09 22:03:07 +08:00
guke
2ddea4159d
feat(coupon-data): 领券成功率看板(整单/点位/分平台 + 按券) ( #130 )
...
后台「领券数据」看板此前只有发起/完成数与耗时分位,缺少成功率视角。本 MR 补齐三档平台粒度成功率与一张按券(coupon_id)成功率表,并为看板增加领券状态多选过滤。
服务端埋点
领券每帧按 trace_id 把「成功平台」并集幂等写入 platform_success(merge_session_platform_success;读不到 session 行则静默跳过,不建兜底行;无新平台不写库)。
record_claims 按 session_app_env(trace_id) 反查并打 app_env 标。
新增平台推导:coupon_id_to_platform(前缀 mt_→美团 / tb_·ele_·elm_→淘宝 / jd_→京东,与客户端 couponIdToPlatform 同词表)、succeeded_platforms。成功语义统一为 success + already_claimed。
复用同一 SessionLocal、紧接 record_claims,不新增连接;fire-and-forget,异常已吞。
后台指标与接口
Summary 新增:②整单成功率(勾选平台全领到的 session 占发起数)、③点位成功率(Σ成功平台 / Σ勾选平台)、分平台点位成功率(恒含美团/淘宝/京东三档)。基数与「发起数」一致,含全部 session。
新端点 GET /coupon-data/coupons(coupon_slot_report):按券成功率表,数据源 coupon_claim_record,设备-天粒度,成功率 = 成功 /(成功+失败),skipped 排除,按尝试数倒序。
主表加 status 多选过滤(started/completed/failed/abandoned),汇总/成功率/趋势/明细整体按选中状态算,与 app_env 同级。
兼容性 / 风险
两个新列均可空、旧行按空集/不回填处理,无数据回填;埋点 fire-and-forget,失败不影响领券主流程。
部署务必 alembic upgrade head(因新增了 head 合并迁移)。
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #130
2026-07-09 17:31:48 +08:00
guke
37fd51a498
feat(analytics): 埋点/上报健康度自报计数(selfstat)+ admin 健康度聚合端点 ( #127 )
...
背景 / 目标
客户端埋点从「采集」到「上报落库」全链路存在丢失(采集端丢帧、网络丢包、上报失败),现有 analytics_event(行为事件)无法度量这条链路的健康度。本 MR 引入一条与行为埋点完全独立的自报计数(selfstat)链路:客户端周期上报「自 epoch 起算的累计计数」,服务端只 append 存原始快照,admin 查询时在 Python 侧差分聚合出两段成功率(埋点成功率 / 上报成功率),支持总览 / 按天趋势 / 按维度下钻。
改动概览
① App 侧 — 上报接入(不强制登录)
新增 POST /api/v1/analytics/selfstat:一份快照 = 设备头 + N 条 event 累计计数,单事务落库,返回 snapshot_id。
稳态兜底:落库异常不裸奔 500,logger.exception 后回 503(计数链路要稳,不阻塞端上主流程)。
events 允许空列表(某次只报设备级计数也合法),上限 200 条/次;与行为埋点 min_length=1 有意不同。
② Admin 侧 — 健康度聚合(只读,需 admin 鉴权)
新增 GET /admin/api/analytics-health/{overview,trend,breakdown}。
差分聚合:原始累计快照按 (device_id, epoch_id, event) 分区、created_at 升序做相邻差分;分区首行增量=累计值,负值(epoch 重置 / 乱序)夹 0;每条增量按其快照 created_at 归入北京天桶。
基线行:取 date_from 左侧每分区最后一条快照,保证区间第一条增量正确(参与差分后丢弃)。
两段率(分母为 0 → null):
埋点成功率 track_success_rate = (attempted − drop_capture) / attempted
上报成功率 report_success_rate = delivered / (delivered + drop_undelivered)
breakdown 维度限 event | app_ver | oem(路由正则校验);结果按上报成功率升序(最差在前,None 垫底)。
③ 数据模型 / 基建
新表 analytics_selfstat(快照头 + 设备维度 app_ver/oem/os + 设备级诊断量 batches_attempted/ok/fail、retries、queue_depth、端 sent_at + 服务端权威 created_at)与 analytics_selfstat_event(四类累计计数,FK → 快照头)。
Alembic 迁移 11c44afbea58(down_revision admin_user_plain_password):建两表 + 索引。
模型登记进 app/models/__init__.py(供 Alembic 发现);路由挂进 app/admin/main.py。
关键设计取舍
只存原始累计、查询时 Python 差分:admin 低频、量级小,跨 PG/SQLite 无方言坑(与 cps.py/coupon_data.py 同款约定)。
选「最新一条」用 max(id) 而非 max(created_at):id 严格单调,规避 SQLite 秒级时间戳撞车的歧义。
tz 口径统一:SQLite 返回 naive UTC、PG 返回 aware UTC,过滤前统一转 naive UTC 再比较。
基线子查询无下界:扫 date_from 左侧全量(spec §7 已接受的取舍;量级变大再上物化 rollup)。
测试(+14,全绿)
tests/test_analytics_selfstat.py(3):正常落库 / 空 events / 落库异常回 503。
tests/test_analytics_health.py(11):差分逻辑(首行=累计值、相邻差、epoch 重置换分区、乱序夹 0、同时间戳按 id 定序)、两段率公式、零分母→None、北京天边界、端点(overview 鉴权 / overview / breakdown)。
迁移 / 部署注意
部署需执行 alembic upgrade head(建两张新表)。纯新增表,无回填、无破坏性改动,向后兼容。
客户端需按约定 payload(snake_case、累计语义)对接 /api/v1/analytics/selfstat;admin 前端(独立仓 shaguabijia-admin-web)消费三个 analytics-health 端点,不在本 MR 范围。
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #127
2026-07-09 17:31:24 +08:00
marco
fa4127b9e5
更新文档0709 ( #128 )
...
Reviewed-on: #128
2026-07-09 14:24:46 +08:00
liujiahui
0cf5b3816f
feat(coin-history): 信息流广告奖励按点位场景拆流水文案(比价/领券) ( #124 )
...
## 改动
`grant_feed_reward` 按 `feed_scene` 落不同 `biz_type`/`remark`,让收益明细里比价等候期看的广告与领券时看的广告文案分开:
- `comparison` → `feed_ad_reward_comparison`(比价奖励)
- `coupon` → `feed_ad_reward_coupon`(领券奖励)
- 其余(welfare / 空 / 旧端不传)→ 维持通用 `feed_ad_reward`(信息流广告奖励)
客户端按 `biz_type` 直显固定文案(见 android 侧 `CoinHistoryViewModel.coinTitle`),`remark` 仅作后台留痕/兜底。
## 附:验收脚手架
`scripts/seed_coinhistory_labels_test.py`(dev-only):往测试号 `11111111111` 塞每种 bizType 各一条金币流水(`ref_id` 前缀 `TESTDOC`),一屏核对收益明细全部新文案;`--clean` 按前缀精确清理,不污染真实数据。
## 联动
客户端配套改动:`shaguabijia-app-android` 同名分支 `7-6ljh`。
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: no_gen_mu <liujianhishen@gmail.com >
Reviewed-on: #124
Co-authored-by: liujiahui <liujiahui@wonderable.ai >
Co-committed-by: liujiahui <liujiahui@wonderable.ai >
2026-07-09 10:03:09 +08:00
liujiahui
9e082e6376
后台权限管理支持「自定义」角色(后端) ( #126 )
...
admin_user 加 pages_override(可空 JSON)+ Alembic 迁移;role==custom 时可见页由逐页勾选决定,否则跟随角色。
create/update 收 pages_override(切回普通角色自动清空),_validate_role 放行 custom 哨兵角色。
登录/me 下发有效页时优先用 override → 左侧导航按勾选即时生效。补 4 个用例,test_admin_roles 全绿。
配套前端 PR:shaguabijia-admin-web#admin-custom-perms。
---------
Co-authored-by: no_gen_mu <liujianhishen@gmail.com >
Reviewed-on: #126
Co-authored-by: liujiahui <liujiahui@wonderable.ai >
Co-committed-by: liujiahui <liujiahui@wonderable.ai >
2026-07-09 01:09:03 +08:00
zhuzihao
93cb283c1b
feat(marquee): 真实条不按用户去重(打乱+去连簇)+ 后台按模式分页浏览可展示记录 ( #123 )
...
- get_feed 真实条不再按 user 去重:改「洗牌 + 去连簇」(相邻尽量不同用户、减少单人连刷),
同一用户可多条露出但被打散、尽量不连续。_shuffle_declustered 支持传入固定种子 rng。
- 新增 GET /admin/api/marquee-seeds/real-records:按**当前模式**分页浏览全部可展示记录(不去重),
供运营逐页审核——只真实=真实记录;只种子=各启用种子按生成逻辑各出一行;混播=真实+种子。
固定种子洗牌+去连簇 → 排列恒定、翻页稳定、能翻遍全部;含 OpsRealRecord* schema。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #123
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-07-08 00:57:00 +08:00
guke
a43e082391
fix(dashboard): 比价/领券奖励金币按 feed_scene 汇总(修复恒 0 + 激励视频双计) ( #125 )
...
世睿之前mr未合并的逻辑
比价/领券奖励金币此前查 coin_transaction.biz_type in (comparison/coupon...),
但这些 biz_type 全站从未写入——比价/领券信息流广告金币实际记为 feed_ad_reward、
场景区分在 ad_feed_reward_record.feed_scene——故两卡恒 0;领券桶还误含
reward_video/ad_reward,把激励视频金币双计进领券。
改为:comparison/coupon 奖励金币 = biz_type 桶(历史空、留作兜底)+ 按
ad_feed_reward_record.feed_scene 的 granted 实发金币(reward_date 北京自然日窗口);
reward_video/ad_reward 拆成独立 REWARD_VIDEO_BIZ_TYPES,不再混入领券,
REGULAR_TASK_EXCLUDED_BIZ_TYPES 保持不变。
测试:tests/test_admin_read.py 加 3 个用例(比价/领券按 feed_scene 汇总、
too_short 不计、领券排除激励视频);全量 pytest 除 5 个既有失败外全绿。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #125
2026-07-07 18:29:23 +08:00
guke
e11ad9449c
revert 6ec4cfb4be
...
revert fix(dashboard): 比价/领券奖励金币按 feed_scene 汇总(修复恒 0 + 激励视频双计)
比价/领券奖励金币此前查 coin_transaction.biz_type in (comparison/coupon...),
但这些 biz_type 全站从未写入——比价/领券信息流广告金币实际记为 feed_ad_reward、
场景区分在 ad_feed_reward_record.feed_scene——故两卡恒 0;领券桶还误含
reward_video/ad_reward,把激励视频金币双计进领券。
改为:comparison/coupon 奖励金币 = biz_type 桶(历史空、留作兜底)+ 按
ad_feed_reward_record.feed_scene 的 granted 实发金币(reward_date 北京自然日窗口);
reward_video/ad_reward 拆成独立 REWARD_VIDEO_BIZ_TYPES,不再混入领券,
REGULAR_TASK_EXCLUDED_BIZ_TYPES 保持不变。
测试:tests/test_admin_read.py 加 3 个用例(比价/领券按 feed_scene 汇总、
too_short 不计、领券排除激励视频);全量 pytest 除 5 个既有失败外全绿。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-07-07 18:05:33 +08:00
guke
6ec4cfb4be
fix(dashboard): 比价/领券奖励金币按 feed_scene 汇总(修复恒 0 + 激励视频双计)
...
比价/领券奖励金币此前查 coin_transaction.biz_type in (comparison/coupon...),
但这些 biz_type 全站从未写入——比价/领券信息流广告金币实际记为 feed_ad_reward、
场景区分在 ad_feed_reward_record.feed_scene——故两卡恒 0;领券桶还误含
reward_video/ad_reward,把激励视频金币双计进领券。
改为:comparison/coupon 奖励金币 = biz_type 桶(历史空、留作兜底)+ 按
ad_feed_reward_record.feed_scene 的 granted 实发金币(reward_date 北京自然日窗口);
reward_video/ad_reward 拆成独立 REWARD_VIDEO_BIZ_TYPES,不再混入领券,
REGULAR_TASK_EXCLUDED_BIZ_TYPES 保持不变。
测试:tests/test_admin_read.py 加 3 个用例(比价/领券按 feed_scene 汇总、
too_short 不计、领券排除激励视频);全量 pytest 除 5 个既有失败外全绿。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-07-07 18:02:21 +08:00
guke
a52fc1973f
revert 58b59c264d
...
revert fix(dashboard): 比价/领券奖励金币按 feed_scene 汇总(修复恒 0 + 激励视频双计)
比价/领券奖励金币此前查 coin_transaction.biz_type in (comparison/coupon...),
但这些 biz_type 全站从未写入——比价/领券信息流广告金币实际记为 feed_ad_reward、
场景区分在 ad_feed_reward_record.feed_scene——故两卡恒 0;领券桶还误含
reward_video/ad_reward,把激励视频金币双计进领券。
改为:comparison/coupon 奖励金币 = biz_type 桶(历史空、留作兜底)+ 按
ad_feed_reward_record.feed_scene 的 granted 实发金币(reward_date 北京自然日窗口);
reward_video/ad_reward 拆成独立 REWARD_VIDEO_BIZ_TYPES,不再混入领券,
REGULAR_TASK_EXCLUDED_BIZ_TYPES 保持不变。
测试:tests/test_admin_read.py 加 3 个用例(比价/领券按 feed_scene 汇总、
too_short 不计、领券排除激励视频);全量 pytest 除 5 个既有失败外全绿。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-07-07 17:52:57 +08:00
guke
58b59c264d
fix(dashboard): 比价/领券奖励金币按 feed_scene 汇总(修复恒 0 + 激励视频双计)
...
比价/领券奖励金币此前查 coin_transaction.biz_type in (comparison/coupon...),
但这些 biz_type 全站从未写入——比价/领券信息流广告金币实际记为 feed_ad_reward、
场景区分在 ad_feed_reward_record.feed_scene——故两卡恒 0;领券桶还误含
reward_video/ad_reward,把激励视频金币双计进领券。
改为:comparison/coupon 奖励金币 = biz_type 桶(历史空、留作兜底)+ 按
ad_feed_reward_record.feed_scene 的 granted 实发金币(reward_date 北京自然日窗口);
reward_video/ad_reward 拆成独立 REWARD_VIDEO_BIZ_TYPES,不再混入领券,
REGULAR_TASK_EXCLUDED_BIZ_TYPES 保持不变。
测试:tests/test_admin_read.py 加 3 个用例(比价/领券按 feed_scene 汇总、
too_short 不计、领券排除激励视频);全量 pytest 除 5 个既有失败外全绿。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-07-07 17:46:09 +08:00
Ghost
0e149c83e7
修复美团 CPS 订单 pay_time 入库为空导致大盘美团收益漏算 ( #119 )
...
Co-authored-by: guke <guke@autohome.com.cn >
Co-authored-by: 陈世睿 <2839904623@qq.com >
Reviewed-on: #119
Co-authored-by: Ghost <>
Co-committed-by: Ghost <>
2026-07-07 17:11:00 +08:00
zhuzihao
9e011f699a
feat(marquee): 预览可按数据源模式实时 + 默认昵称归入「无昵称」脱敏档 ( #122 )
...
后台「首页轮播种子」预览与用户名脱敏两处改动,对齐《省钱log&平台数据 显示策略》:
- 预览按模式实时:get_feed 增加可选 mode 入参(不落库),admin 预览接口
/marquee-seeds/preview 透传 ?mode=,后台切换「混播/只真实/只种子」时预览即时对应;
不传 mode 时读持久化配置,客户端 /savings-feed 行为不变。
- 默认昵称归「无昵称」档:创建时自动分配的默认昵称(「用户」+9 位随机)不算用户主动设的
昵称,脱敏走 id 规则「用户*****+id后2位」而非昵称规则。新增 user.is_default_nickname
精确匹配生成格式,不误伤真人以「用户」开头的昵称(如「用户体验师」)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #122
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-07-07 00:16:20 +08:00
zhuzihao
66c1e3ad8f
fix(withdraw): 提现账本校验按 source 分账,邀请奖励金提现纳入对账 ( #121 )
...
线上后台「提现审核」页现金账本报红(缺扣款/缺退款流水),根因是
withdraw_ledger_check 用全部提现单去比对普通现金流水(cash_transaction),
而 source=invite_cash 的提现,其扣款/退款流水写在独立的 invite_cash_transaction
表,于是每笔邀请提现单都在普通现金流水里找不到、被误报为「缺流水」。
改动:
- 抽出 _check_withdraw_ledger_side 复用单账核对逻辑;withdraw_ledger_check
改为按 order.source 分两本账各自对账:
coin_cash ↔ cash_transaction(withdraw/withdraw_refund),
invite_cash ↔ invite_cash_transaction(invite_withdraw/invite_withdraw_refund)。
- 邀请奖励金账户的余额差额也纳入校验(此前完全未对账)。
- 纯只读校验,不改任何资金/流水写入;不掩盖真实缺流水
(coin_cash 单若真缺流水仍照报)。
- WithdrawLedgerCheckOut 新增 7 个 invite_* 字段(默认 0,向后兼容)。
- 补 tests/test_withdraw_ledger_check.py(此前无 ledger-check 测试):
覆盖邀请提现不再误报、邀请账缺流水能被抓、普通现金账未回归。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #121
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-07-07 00:16:12 +08:00
zhuzihao
982e92bc24
feat(ad-revenue): 收益报表 DAU/ARPU 按所选区间统计(复用大盘活跃口径) ( #120 )
...
- 抽出 stats.period_active_dau(date_from, date_to):任意北京自然日区间的去重活跃用户
(登录 + 开始比价 + 开始领券),口径与数据大盘 period.users.active 一致。today_dau 与
dashboard_overview 的 period 活跃、逐日趋势统一复用它,消除三处重复的并集计算,行为不变。
- ad_revenue_report 去掉「仅今日单天才给 dau」的 is_today 限制,dau 改为按 date_from~date_to
区间统计(含今日、近 7 天、近 30 天);ARPU = 区间预估收益 ÷ 区间活跃用户 对三个时段均可算,
dau 与大盘同区间同值。
- dau 为全局口径,不随 user_id/ad_type/feed_scene/app_env 筛选变化;更新 schema 字段说明。
- 验证:admin 读接口测试(含 dashboard_overview)全过;实测多天区间 dau 非空且 == 大盘
period.users.active。前端展示改动见 admin-web 对应 PR。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #120
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-07-06 00:23:36 +08:00
zhuzihao
6241543119
运营后台后端:RBAC 权限管理 + 系统配置下发修复 + 比价记录店/商品搜索 ( #117 )
...
一、RBAC 权限管理(角色 → 可见页面)
- 新增 admin_role 表 + 权限目录 permissions.py:角色持有一组页面 key,登录后左侧只展示这些页
- 内建角色 管理员(super_admin 全权锁定)/运营/财务/技术,页集对齐原型;key 承重(require_role 用),另加中文 label 展示
- 角色 CRUD 端点(仅 super_admin)+ 目录端点;登录/me 下发当前角色有效可见页
- admins 加删除、角色存在性校验;可复看已确定登录密码:UI 建的账号留存明文 plain_password,脚本建的超管不留存
二、系统配置下发修复
- 首页数据「保存即生效」:显式保存(apply_now)对 real/manual 直接落配置目标值、绕过只增不减护栏(修「改了 app 端不变」),护栏仍管自动 tick
- 首页轮播新增数据源三选一:mixed(真实优先+种子)/real(只真实)/seed(只种子),get_feed 分支 + /marquee-seeds/mode 端点
- 福利页 Tab 隐藏 任务/里程碑,及看广告的单次金币/每轮次数/信息流开关:CONFIG_DEFS 加 hidden + list_config 过滤,业务读取默认值不受影响
三、比价记录店/商品搜索
- comparison_record 加 product_names 派生列(从下单 items 拼商品名),迁移建列并回填历史行
- admin 比价记录列表店/商品分列可搜:走 product_names 普通列 LIKE,规避 SQLite JSON 中文 ensure_ascii 转义搜不到的坑
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #117
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-07-05 09:46:19 +08:00
guke
7b6756f936
feat(meituan-cps): 经纬度→城市离线反查 + rec/销量最高按城市过滤 ( #116 )
...
## 主要功能
新增离线「经纬度 → 美团 cityId」反查,让 `rec`(智能推荐)与 `top-sales`(销量最高)从离线库只返**同城券**(此前会混返异地券)。
- `app/utils/geo.py` + `app/utils/meituan_city.py`:坐标 → 美团 `city_id`(reverse_geocoder 离线反查,零网络)。
- `feed?tab=rec` / `/top-sales`:按 `city_id` 过滤;解析不出 / 老客户端不带坐标 → 降级返空。
- `top-sales` 与 `rec` 一致置空库内距离(相对城市默认点、对用户无意义)。
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #116
Co-authored-by: guke <guke@wonderable.ai >
Co-committed-by: guke <guke@wonderable.ai >
2026-07-05 09:31:53 +08:00
marco
3eb439b5ae
feat(onboarding): 新增重置新手引导端点 /api/v1/user/onboarding/reset ( #114 )
...
Reviewed-on: #114
2026-07-04 02:39:12 +08:00
liujiahui
6b10e866e9
7-3 尾声帧: /api/v1/trace/epilogue 透传壳 ( #112 )
...
比价结果页"尾声帧"配套改动(透传壳一条)。
App 收到 done、渲染完比价结果页后, 把自己页面的截图(base64)经本壳透传给 pricebot
`/api/trace/epilogue`, 存进 trace 目录并触发重传 —— trace 里补上"用户实际看到的
汇总页"(trace 步骤帧只有目标 App 画面, App 自渲染的汇总页天然在镜头外)。
壳同 `/trace/finalize`: 不鉴权、按 trace_id 一致性 hash 亲和。
配套分支(同名 7-3-ljh-testlab): pricebot-backend(端点+viewer 实拍块, PR #160 ) /
shaguabijia-app-android(结果页拍屏上传)。
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: no_gen_mu <liujianhishen@gmail.com >
Reviewed-on: #112
Co-authored-by: liujiahui <liujiahui@wonderable.ai >
Co-committed-by: liujiahui <liujiahui@wonderable.ai >
2026-07-04 01:42:01 +08:00
liujiahui
13b143cd97
feat(invite): 发奖改「比价并下单」+ 好友 is_compared 状态 + DEV 造数/重置接口 ( #113 )
...
重开分支重提 PR #110 的邀请发奖改动,**不含任何测试文件**(`tests/` 3 个 + `scripts/invite_reward_e2e.*` 2 个 E2E 脚本本地自用,未入库)。
只含 8 个产品/部署文件:
- 发奖口径从「上报比价」改为「实际下单」:`order.py` / `compare_record.py` / `repositories/invite.py`
- 好友列表返回 `is_compared` 已比价状态:`schemas/invite.py` / `repositories/invite.py`
- 新增 DEV 内部接口(生产 `is_prod` 一律 404):`POST/DELETE /invite/seed-fake` 造/清虚拟好友;`POST /invite/dev-reset` 固定测试号打回出厂态
- fix(deploy):每日兑换定时器锁北京时区 `Asia/Shanghai`(`daily-exchange.service` / `.timer`)
对应 PR #110,源分支 `7-1ljh`。
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: no_gen_mu <liujianhishen@gmail.com >
Reviewed-on: #113
Co-authored-by: liujiahui <liujiahui@wonderable.ai >
Co-committed-by: liujiahui <liujiahui@wonderable.ai >
2026-07-03 19:01:37 +08:00
zhuzihao
93f052535e
精简短信登录风控:保留单号冷却 + 单设备频控两道 ( #109 )
...
按 mentor 要求精简 SMS 登录链路的防刷策略,只保留两道主策略:
- 单号 60s 冷却(发码,SMS_SEND_INTERVAL_SEC)
- 单设备(device_id + IP)每小时 5 次频控(发码 + 登录,enforce_rate_limit)
删除两道:
- 单号每日发送上限(SMS_DAILY_LIMIT_PER_PHONE)
- 登录接口纯 IP 20 次/分钟限流(rate_limit 依赖)
验证码安全底线保留(一次性使用 + 输错 SMS_MAX_VERIFY_ATTEMPTS 次即作废),
属验证码正确性、不算限流策略,不在精简范围。
顺带清理:sms.py 移除 _daily_count/_today 及 datetime 导入;config.py 删常量;
auth.py 清理变为无用的 Depends/rate_limit 导入;同步 docs/integrations/sms.md 与
docs/后端技术实现.md 的防刷说明(并修正 sms.md 里已过时的 /sms/send rate_limit 描述)。
影响:单号发码上限由 10 条/天放宽为仅受 60s 冷却约束;登录撞库防护改由设备频控
+ 验证码错误次数上限兜底。test_auth.py 相应用例已删除/更新,auth 测试全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #109
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-07-03 15:28:32 +08:00
guke
a563c1ca4b
@ ( #111 )
...
docs/api目录文档分类和补全
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #111
2026-07-03 15:00:37 +08:00
chenshirui
ee132aa93b
心跳掉线判定超时从10分钟调成1小时 ( #107 )
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
---------
Co-authored-by: 陈世睿 <2839904623@qq.com >
Reviewed-on: #107
Co-authored-by: chenshirui <chenshirui@wonderable.ai >
Co-committed-by: chenshirui <chenshirui@wonderable.ai >
2026-07-02 22:09:54 +08:00
zhuzihao
4512b6ecac
feat: 反馈加来源/场景/运营回复 + 提现类型筛选 ( #105 )
...
用户反馈后台:区分反馈类型(比价反馈/普通反馈)+ 审核可给用户留言;提现后台:按提现类型筛选。
- feedback 表加 source(profile/comparison)/scene/admin_reply + 迁移;提交接口 /api/v1/feedback
接收 source/scene,来源判定显式 source 优先、否则据 scene 有无派生(有=comparison);
/records 带回 scene + admin_reply。
- admin 反馈:列表加「反馈类型」筛选;采纳/拒绝支持存 admin_reply(给用户的回复,用户端可见);
FeedbackOut 带出 source/scene/admin_reply。
- admin 提现:WithdrawOut 暴露 source,列表加「提现类型」筛选(coin_cash=福利页提现 / invite_cash=邀请提现)。
- 补 4 项测试:来源派生、回复存取、反馈按类型筛选、提现按类型筛选。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #105
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-07-02 19:50:14 +08:00
zhuzihao
4630fd017b
feat(ad-revenue): 收益报表按一次比价/领券聚合(trace 合并)+ 微信昵称/签到时间修复 ( #103 )
...
- 报表主表改为「单次广告行为」:激励视频=一次观看一行;一次比价/领券=同一 trace_id(整场,
无 trace 时兜底 ad_session_id)的多条发奖聚成一行,点开看逐条金币复算。中途跳转广告致浮层重弹、
ad_session_id 变化也能按 trace 归一行。
- 信息流统一 Draw(业务已全切):新增 audit 的 feed_all scene,让「Draw 信息流」筛选覆盖历史误标的
feed/NULL;聚合行 ad_type 统一 draw;_feed_rows 带出 trace_id 供聚合。
- 父行补 sub_rewards(组内逐条复算明细)、sub_count;row_revenue_yuan=该次发奖广告 eCPM 折算收益之和,
仅供主表逐行展示、不进合计/趋势(避免与展示侧 total 重复计)。
- 用户 360 概览(AdminUserListItem)返回 wechat_nickname,供收益详情抽屉显示微信昵称。
- 金币记录:签到来自 coin_transaction(存北京 wall-clock),组装时转 UTC 与广告记录统一,修抽屉签到
时间多 8 小时;签到窗口边界 +8h 对齐;顺带补 _window_conds 引用却从未定义的 _as_utc_naive
(自定义区间会 NameError)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #103
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-07-02 08:57:15 +08:00
wuqi
70c2349950
feat(migration): 添加合并 jd_cps_order_fields 和 coupon_session_origin_package 的迁移文件 ( #102 )
...
Reviewed-on: #102
Co-authored-by: wuqi <wuqi@wonderable.ai >
Co-committed-by: wuqi <wuqi@wonderable.ai >
2026-07-01 20:21:34 +08:00
zhuzihao
357b2312af
chore(deploy): 穿山甲收益拉取 systemd 定时器(每天10:30)+ 运维手册 ( #100 )
...
为 #92 的 scripts/sync_pangle_revenue.py 补 systemd 部署单元,让穿山甲 GroMore T+1 后台收益每天自动拉取入库,免人工敲命令。纯部署/文档文件,不改运行时代码。
- service:oneshot,跑 `--days 3` 回补近 3 天(幂等 upsert,扛偶发漏跑与穿山甲历史订正);10min 硬超时;加固块对齐主服务。
- timer:每天 10:30 触发(穿山甲约 10:00 出数留余量),Persistent 宕机后补跑。
- md:运维手册——上线前置(.env 填 PANGLE_REPORT_* 凭证、子账号需授权否则接口 118)、部署/健康检查/参数/注意事项(join key 用 ad_unit_id 非 code_id 等)。
- 风格对齐现有 deploy/daily-exchange.* 与 deploy/meituan-etl.*。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #100
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-30 22:59:41 +08:00
zhuzihao
78d970f420
feat: 领券数据看板 — coupon_session 流水表 + admin 聚合接口 ( #99 )
...
- 新表 coupon_session(一次领券一行,trace_id 唯一):POST /api/v1/coupon/session
两段 upsert(发起 started / 收尾 completed·failed·abandoned),记全程耗时、各平台耗时、
机型/ROM、app_env、trace_url、origin_package(发起来源)。
- admin GET /admin/api/coupon-data:发起/完成数 + 平均耗时 + P5/P50/P95/P99(Python 算分位,
SQLite 无 percentile)+ 按天/小时趋势 + 明细分页 + join 用户手机号/昵称,app_env 默认 prod;
另加 GET /admin/api/coupon-data/user-records 供「点手机号看该用户全部领券」抽屉。
- 迁移拆 3 个:建表 coupon_session_table + trace_url 加列 + origin_package 加列
(建表迁移已被某环境应用后改它不重跑,故新列单独加列迁移)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #99
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-30 22:59:36 +08:00
chenshuobo
a3a14b484a
feat: 接入京东联盟订单到数据大盘 ( #90 )
...
改了什么:新增京东联盟订单查询客户端、cps_order 京东字段迁移、CPS 对账接口 platform=all/jd、数据大盘京东订单与佣金聚合。
为什么改:数据大盘需要展示京东 CPS 真实订单佣金,不再保持占位。
验证方式:python -m pytest tests\\test_admin_read.py::test_dashboard_overview tests\\test_cps_admin.py::test_jd_reconcile_updates_dashboard -q;本地真实拉取 2026-06-27 京东订单 3 行,页面显示京东有效 1 单/佣金 ¥0.13。
---------
Co-authored-by: lowmaster-chen <1119780489@qq.com >
Reviewed-on: #90
Co-authored-by: chenshuobo <chenshuobo@wonderable.ai >
Co-committed-by: chenshuobo <chenshuobo@wonderable.ai >
2026-06-30 21:56:58 +08:00
liujiahui
208112ff24
fix(dl): 落地页底图+logo 纳入 git → 修生产 /media 404 毛坯 ( #97 )
...
## 背景
用户反馈浏览器落地页仍是「毛坯」(裸黄底、缺优惠券卡/价格卡/logo)。
## 真因(实测线上)
`https://app-api.shaguabijia.com/media/dl.html ` 的 **HTML 早已是完善版且已部署**,但它引用的两张静态图被 `.gitignore` 的 `data/media/*` 规则挡在库外、从未随部署进生产 `/media`:
| 资源 | 线上 | 本地/git |
|---|---|---|
| `/media/coupon-page-bg.png`(底图:优惠券卡+跨平台比价价格卡) | **404**(22B `{"detail":"Not Found"}`) | 之前被 gitignore |
| `/media/sb-brand.png`(logo) | **404** | 之前被 gitignore |
| `/media/shaguabijia.apk`(对照) | 200 ✓ | — |
→ 两图加载失败 → 页面回退裸黄底 = 毛坯。
## 改动
照 `dl.html` / `taobao_landing.jpg` 既有白名单写法,把这两张图加进 `.gitignore` 例外并入库。
- `.gitignore`:`!data/media/coupon-page-bg.png`、`!data/media/sb-brand.png`
- 入库两图,md5 与原型一致(coupon-page-bg `f2adf28a` / sb-brand `e5b6e004`)
**无代码/HTML 改动**(dl.html 本就对齐原型,见上一 commit `6d4cbc8`)。
## 部署后生效
合并并部署本分支后,生产 `/media/` 即含这两图,落地页恢复完整。对应 6-29 UI 对齐 #10。
---------
Co-authored-by: no_gen_mu <liujianhishen@gmail.com >
Reviewed-on: #97
Co-authored-by: liujiahui <liujiahui@wonderable.ai >
Co-committed-by: liujiahui <liujiahui@wonderable.ai >
2026-06-30 20:48:31 +08:00
zhuzihao
32f300b5a2
调现金接口支持目标账户(金币现金 / 邀请奖励金)+ overview 带邀请余额 ( #95 )
...
- GrantCashRequest 加 account(coin_cash / invite_cash,默认 coin_cash 兼容旧调用)
- 调现金接口按 account 分支:邀请账户走 grant_invite_cash,余额读取/扣负保护/审计
detail 都按对应账户走;两本账物理隔离不可串
- 用户 360 overview 返回 invite_cash_balance_cents(admin 调现金弹窗展示邀请余额)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #95
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-30 12:02:56 +08:00
zhuzihao
81639ed388
fix(ad-revenue): 报表收益按满额钳顶 + 新增 app_env 过滤 ( #93 )
...
广告收益报表两处隐患修复(admin/repositories/ad_revenue.py + routers/ad_revenue.py):
- 收益钳顶:单次展示收益原用裸 parse_ecpm_yuan、未钳上限;发奖侧 calculate_ad_reward_coin
已钳 AD_ECPM_MAX_FEN(¥500 CPM)。异常/伪造天价 eCPM 会让报表预估收益虚高任意大。改为
min(parse_ecpm_yuan, AD_ECPM_MAX_FEN/100)/1000,与发奖同口径。
- app_env 过滤:报表聚合原无 app_env 过滤,测试应用假 eCPM(如 ¥678 CPM)会进正式收益合计/平均。
新增 app_env 参数(repository + router Query),显式传 prod/test 才过滤;默认仍全部(不擅自改成
默认排除 test:本地 dev 库多为 test 会空、且属产品口径)。穿山甲后台收益列暂未联动此过滤。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #93
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-29 23:11:41 +08:00
zhuzihao
5efe624340
admin 反馈/低价审核接口增强 + 反馈采集端环境(版本/机型) ( #94 )
...
- 反馈:新增 GET /admin/api/feedbacks/summary(待审核/已采纳/未采纳/合计计数);
列表联表带出完整手机号 + 昵称(供 admin 点手机号查该用户全部反馈)
- 低价审核:列表加 sort_by/sort_order(提交时间排序);联表带出手机号/昵称,
并按 comparison_record_id 关联比价记录带出 trace_id/trace_url + 机型/ROM/Android/app 版本
- feedback 表新增 app_version/device_model/rom_name/android_version 四列 + alembic 迁移;
/api/v1/feedback 提交接口接收并落库,供 admin 反馈页展示「提交版本号」「机型OS版本」
(字段可空,旧端不传不影响提交)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #94
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-29 23:11:30 +08:00
zhuzihao
b622f76a02
feat(ad-revenue): 接入穿山甲 GroMore 数据 API 拉取后台收益(预估+收益API) ( #92 )
...
- 新增穿山甲 GroMore「聚合数据报告 API」对接,按天 T+1 拉取后台 revenue(预估)与
api_revenue(收益API,更接近结算),在广告收益报表大盘 + 按天趋势级展示,与客户端自报
eCPM 折算的预估并列对照;逐条广告事件行不动(仍是客户端预估)。
- 链路:integrations/pangle_report.py(MD5 签名,与官方文档两个测试向量逐字节一致)→
scripts/sync_pangle_revenue.py(拉昨天/--days 回补)→ 新表 ad_pangle_daily_revenue
(repositories/ad_pangle_revenue.py:upsert + 按日聚合)→ admin/repositories/ad_revenue.py
汇总,新增 total_pangle_revenue_yuan / total_pangle_api_revenue_yuan / daily[].pangle_*。
- 穿山甲无用户/类型/场景维度:仅全量视图(未按 user/ad_type/feed_scene 过滤)给值,否则置
None;join key 用 ad_unit_id(=客户端配的 104xxx),非 code_id。
- 新增配置 PANGLE_REPORT_USER_ID/ROLE_ID/SECURITY_KEY(≠发奖 m-key)+ site_id→应用映射;
含单测(签名向量+分页解析);已真连穿山甲验证。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #92
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-29 09:54:13 +08:00
wuqi
c0b67fd879
feat(launch_confirm): 新增启动确认样本列表内部接口(供 pricebot distill 回读) ( #91 )
...
补齐「启动确认窗自进化闭环」里 app-server 这一侧的读路径:pricebot 的 launch_confirm
LLM 兜底成功样本会 server→server 落到 app-server 的 launch_confirm_sample 表;本提交
新增「样本列表查询」内部接口, 供 pricebot 的 scripts/distill_launch_confirm.py 回读这些
样本、按「宿主包 × 文案变体」聚合沉淀出候选规则。
- app/api/internal/launch_confirm.py: 新增样本列表内部接口(server→server, 不走用户 JWT)
- app/repositories/launch_confirm_sample.py: 样本列表查询
- app/schemas/launch_confirm_sample.py: 列表响应 schema
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
Reviewed-on: #91
Co-authored-by: wuqi <wuqi@wonderable.ai >
Co-committed-by: wuqi <wuqi@wonderable.ai >
2026-06-28 21:40:42 +08:00
xiebing
2ed62c789f
feat(h5): 我的页改 H5(mine 页 + shared bridge/api) ( #89 )
...
- h5/mine/index.html: 个人中心「我的页」H5 实现
- h5/shared/bridge.js, api.js: H5↔客户端桥与 API 封装(mine 依赖)
Reviewed-on: #89
Co-authored-by: xiebing <xiebing@wonderable.ai >
Co-committed-by: xiebing <xiebing@wonderable.ai >
2026-06-28 14:16:32 +08:00
chenshuobo
1548406f29
美团 feed 改用离线库 + 三 tab 降级兜底(替换 main 的实时版 feed) ( #88 )
...
Co-authored-by: lowmaster-chen <1119780489@qq.com >
Co-authored-by: chenshuobo <1119780489@qq.com >
Reviewed-on: #88
Co-authored-by: chenshuobo <chenshuobo@wonderable.ai >
Co-committed-by: chenshuobo <chenshuobo@wonderable.ai >
2026-06-28 10:38:31 +08:00
guke
b7b958ed58
perf(pricebot): 透传复用共享 httpx 单例,免每请求重建 SSL 上下文 + 绕过进程代理 ( #87 )
...
coupon/compare 透传原先每请求 async with httpx.AsyncClient(...) 新建:
每次重建 SSL 上下文(加载 certifi CA)实测 ~1s+,而 pricebot 是纯 HTTP
透传根本用不到 TLS;且 trust_env 默认 True 会把 http://localhost:8000
经进程代理(Clash)再绕几秒。
抽 app/core/pricebot_client.py 共享单例:trust_env=False 直连,lifespan
启动预热(SSL 一次性成本付在启动)、关停 aclose;timeout 下放到 .post()
保留 coupon 30s / compare 60s 差异。keep-alive 复用 TCP,每帧降到个位数 ms。
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #87
Co-authored-by: guke <guke@wonderable.ai >
Co-committed-by: guke <guke@wonderable.ai >
2026-06-28 09:36:31 +08:00
wuqi
f3cd97a190
feat: 信息流广告改"所见即所得"发奖——直接发客户端小球显示金币 ( #86 )
...
Reviewed-on: #86
Co-authored-by: wuqi <wuqi@wonderable.ai >
Co-committed-by: wuqi <wuqi@wonderable.ai >
2026-06-27 23:49:51 +08:00
zhuzihao
b4c27f4d88
fix(ad): eCPM 上报撞会话唯一约束不再抛 500,按全局口径兜底去重 ( #85 )
...
create_ecpm_record 撞 uq_ad_ecpm_record_session(只含 ad_session_id、全局唯一)
时,改用与约束同口径的全局查找 _find_by_session_global(仅按 ad_session_id)兜底,
并去掉末尾的 raise。原逻辑兜底用带 user_id 的 find_by_session:不同 user 上报了
同一 ad_session_id 时二次查找仍为 None → raise → 接口 500,违背该接口
best-effort / fire-and-forget 约定(丢一两条不影响业务,穿山甲后台才是结算权威)。
find_by_session 签名与行为不变,另两个调用方(ad_reward、ad.py)不受影响。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #85
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-27 22:42:58 +08:00
zhuzihao
e38120ad49
feat(ad-revenue): 收益报表分页/场景筛选/倒序,并修复信息流金币审计复算口径+ 大盘改版 ( #84 )
...
收益报表(/admin/api/ad-revenue-report):
- 明细改按时间倒序;新增 offset 真分页(limit 作每页大小、total 为全量),可翻页看当前筛选下全部数据,突破原 1000 条上限。
- 「场景」(feed_scene)下推后端做全局筛选,同时作用于明细/合计/daily·hourly 趋势(原为前端仅过滤明细)。
- 新增全量 hourly 序列,按小时趋势改用它,不再受分页截断影响。
修复信息流金币审计复算口径漂移(ad_audit):
- 发奖侧 grant_feed_reward 早已是「一条广告=1份、LT 按账号累计条数(COUNT)」,但审计仍按 unit_count 逐份累加 + SUM(unit_count) 做 LT 基线,导致单条停留>20s(份数>1)时应发虚高、必然「✗ 不符」。
- 审计改为每条 granted 按 1 份复算、LT 基线用 COUNT,与发奖对齐;金币审计页与收益报表(复用同一复算)一并恢复正确。纯复算口径修正,不改实际发奖、不动钱。
文档同步更新 admin-ad-revenue-report.md。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #84
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-27 22:42:54 +08:00
marco
3d67749101
fix(compare-record): specs 兼容 pricebot 嵌套规格对象
...
pricebot 嵌套规格统一(2026-06-27)后 calibration.specs 可能是规格对象
[{name, qty, sub_specs}] 而非字符串。ComparisonItemIn.specs 加 before-validator
把对象拍平成可读字符串"主项(子1,子2)",兼容新旧输入、保持 list[str] 契约(下游零改动)。
防两坑:① 直接声明 list[str] → 对象 specs 整条 422 被拒、不入库;
② 仅放宽成裸 list → 下游 join 出"[object Object]"乱码。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-27 09:26:00 +08:00
marco
f1c2ea662b
fix(migration): merge alembic 3-way 分叉为单 head(修本地 upgrade head 报 Multiple head revisions)
...
#82(invite_cash)/埋点(c2874d2bf705)/device_first_protected_at 三个 PR 并行合 main 留下 3 个 head,
`alembic upgrade head`(单数)报 Multiple head revisions。加一个空 merge 迁移把三支并成单 head,
deploy 的 `upgrade heads`(复数)和本地 `upgrade head` 都干净。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-27 03:06:53 +08:00
marco
be15a119a7
fix(migration): 缩短 revision id 至 26 字符(修 alembic_version varchar(32) 截断致 0.2.1 部署失败)
...
invite_cash_account_and_compare_reward(38字符) > alembic_version.version_num varchar(32),
INSERT 时 psycopg StringDataRightTruncation → 部署 alembic 迁移失败。改为 invite_cash_compare_reward(26)。
该迁移是链末端(无后续 down_revision 依赖)、首次部署(任何环境都没应用过、PG DDL 事务失败已回滚无残留), 改 id 安全。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-27 02:58:29 +08:00
zhuzihao
59fe715245
feat(analytics): 埋点事件接收接口 + admin 查询接口 + 埋点表 ( #83 )
...
Reviewed-on: #83
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-26 23:56:29 +08:00
zhuzihao
667cda566f
feat(analytics): 埋点事件接收接口 + admin 查询接口 + 埋点表 ( #81 )
...
新手引导埋点的服务端:
- 表 analytics_event(五维硬性列 + props JSON 扩展字段;event/device_id/user_id/session_id/created_at 带索引)
- POST /api/v1/analytics/events:客户端批量上报(不鉴权、body 读可选 user_id、补 client_ip + server_at)
- admin GET /admin/api/event-logs:列表 + 按 事件/设备/用户/会话/时间 筛选(offset 分页,照 list_feedbacks)
- alembic migration 建表(autogenerate 顺带检出的 ad/cps 历史索引漂移已手动剔除)
app 主后端 :8770 与 admin :8771 共用同一 SQLite,admin 同库直接查、无需跨库。
配套客户端五维上报 + admin 日志页(另两仓库 PR)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #81
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-26 23:33:31 +08:00
xiebing
19f5987436
feat(invite): 邀请奖励金账户(与金币隔离) + 比价发奖 + 奖励金提现 ( #82 )
...
Reviewed-on: #82
Co-authored-by: xiebing <xiebing@wonderable.ai >
Co-committed-by: xiebing <xiebing@wonderable.ai >
2026-06-26 21:10:21 +08:00
chenshirui
1d6432d8bb
feat(admin): 设备存活监控接口 + 首次无障碍开启时间(first_protected_at) ( #80 )
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: 陈世睿 <2839904623@qq.com >
Reviewed-on: #80
Co-authored-by: chenshirui <chenshirui@wonderable.ai >
Co-committed-by: chenshirui <chenshirui@wonderable.ai >
2026-06-26 15:28:16 +08:00
guke
2eb44fe947
feat(cps): 每日明细按天按用户领券下钻接口 + /daily date 改全日期 ( #79 )
...
- 新增 GET /admin/api/cps/groups/{id}/day-users?date=YYYY-MM-DD:
按 openid 聚合当天领券(copy)/点击(visit) + 每人 visit 过的券(券×次数)
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #79
2026-06-26 15:17:37 +08:00
zhuzihao
ea22a11ef9
fix(ad): 激励视频发奖按环境隔离,堵测试包看广告给生产同 id 用户串号发币 ( #77 )
...
## 问题
激励视频发奖走穿山甲 S2S 回调 /api/v1/ad/pangle-callback,发币时只认回调里的裸数字
user_id(grant_ad_reward 仅校验「本库存在该 id」),无任何环境归属校验。
同一 user_id 在测试库(本地 localhost)与正式库(线上)是两个不同的人;穿山甲 S2S 回调
地址只配一个(指向生产),测试包(dev)看广告的回调也会打到生产 → 生产把奖发给「本库里
同 user_id 的另一个真人」,造成跨库串号:测试看一次广告,正式库同号用户也被发币(线上
广告收益页因此多出一条本不该有的发奖记录)。
## 修复(服务端,按环境隔离)
pangle_callback 解析激励视频 mediaExtra 透传的 srv_env(dev/prod),与本服 settings.APP_ENV
不符则受理但不发币、不写记录(is_verify=true 防穿山甲重试)。不带 srv_env 的旧客户端按本
环境放行(兼容,不误伤存量正式用户)。
- 新增回归测试 test_callback_foreign_env_skipped(srv_env=prod 打到 APP_ENV=dev 的服务 → 不发)
- sim_pangle_callback 增加 --srv-env 便于本地手验
客户端配套(mediaExtra 写入 srv_env)见 app-android 对应 PR。
## 生效前提(三者缺一仍漏)
1. 生产 app-server 部署本代码并重启
2. 生产 .env 为 APP_ENV=prod
3. app 重新构建(旧包不带 srv_env 仍走兼容放行)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #77
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-26 13:17:27 +08:00
zhuzihao
2edebfad83
feat(auth): 短信登录防刷 — 同一设备id同IP 每小时限 5 次 ( #78 )
...
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #78
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-26 13:17:07 +08:00
guke
3dca126411
fix(dev): 启动脚本钉定 .venv 解释器 + watchfiles 降噪 ( #75 )
...
微信优惠券增加美团/京东中间页,领券前获取微信头像授权(主功能)
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #75
Co-authored-by: guke <guke@wonderable.ai >
Co-committed-by: guke <guke@wonderable.ai >
2026-06-25 17:47:30 +08:00
zhuzihao
acdb10b516
feat(ad): 提现看视频广告后端支持(withdrawal_ad 场景 + 开关下发) ( #74 )
...
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #74
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-24 22:38:06 +08:00
marco
b1849834f7
fix(alembic): merge meituan_coupon_image_meta 与 comparison/ad 双 head
2026-06-24 20:50:21 +08:00
guke
8008366abc
fix(wx-cps): 修复 微信cps分发时淘宝落地页授权刷新导致click_value 重复入库问题 0624 ( #73 )
...
通过authed=1判断当前请求是否是微信授权回填,如果是授权回填,不再insert cps_click
---------
Co-authored-by: guke <guke@autohome.com.cn >
Reviewed-on: #73
Co-authored-by: guke <guke@wonderable.ai >
Co-committed-by: guke <guke@wonderable.ai >
2026-06-24 18:15:44 +08:00
marco
43f28d358b
docs: 补全 API 与数据库文档
...
- 新增 device-liveness(无障碍存活监控)、internal(内部回写端点)、cps-redirect(CPS 短链落地)三族单文件文档
- API 总览补录此前缺失端点:coupon prompt 频控、report/invite、wxpay 回调、platform 客户端配置、feedback/user/ad 零散读端点
- database 新增 cps_wx_user / device_liveness / launch_confirm_sample 三表文档,更新 OVERVIEW/README/comparison_record/数据库迁移/后端技术实现
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-24 04:07:54 +08:00
zhuzihao
128db7fb86
feat(admin): 广告收益报表改为逐条广告事件 + 用户列显示手机号 ( #72 )
...
报表主表从「按 用户×类型×应用×代码位 聚合」改成「逐条广告事件」(每次广告一行):
- 激励视频:展示(ad_ecpm)与发奖(ad_reward)按 ad_session_id 合并成一行,直接给出
eCPM/收益 + 状态/应发/实发/一致;展开看该条金币复算因子
- 信息流:轮播每条展示各一行;整场发奖(client_event_id 与展示 impressionId 对不上)单独成行
- 纯展示行不计对账(matched 恒 true);有展示无发奖 / 有发奖无展示各自成行
- 每行补 user_phone(批量查 User.phone,完整不脱敏,与用户/钱包/比价记录页一致)
- 合计与对账在全量上统计、不受 limit 影响;event_key 作前端稳定 rowKey
ad_audit.audit_rows 顺带补返回 ad_session_id(供展示↔发奖按会话合并)。
真实库验证:逐条输出正确、合计交叉核对一致(展示条数=ecpm行数、实发=库实发)、schema 校验通过。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #72
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-24 03:54:01 +08:00
zhuzihao
c222137b2a
feat(auth): 默认昵称加“用户”前缀(用户+9 位随机串) ( #70 )
...
新用户注册即分配的默认昵称由 9 位字母数字随机串改为“用户”+9 位随机串(如 用户aB3xK9mP2),更友好可读;长度 11 仍在 nickname(64)与改名上限(20)内。同步更新 test_login_assigns_username_and_nickname 断言。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #70
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-24 03:53:58 +08:00
zhuzihao
d8c7a6d1ef
feat(auth): 测试账号免验证码登录 + 每次走引导 + 每日上限 ( #69 )
...
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #69
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-24 03:53:52 +08:00
chenshuobo
4a9d3f1d1a
调整首页滚动条用户名称打码规则 ( #68 )
...
改了什么:将 savings-feed 用户名脱敏改为 PRD 规则;有昵称按长度打码,无昵称真实用户显示 用户*****后两位;种子和兜底继续合成多样化脱敏名。
为什么改:避免旧规则出现 用户********xxx,与产品要求的用户标识打码格式保持一致。
验证方式:本地请求 /api/v1/platform/savings-feed?limit=8,确认返回 用户*****xx、首字+**、首字+***+末 等新格式。
---------
Co-authored-by: lowmaster-chen <1119780489@qq.com >
Reviewed-on: #68
Co-authored-by: chenshuobo <chenshuobo@wonderable.ai >
Co-committed-by: chenshuobo <chenshuobo@wonderable.ai >
2026-06-23 21:09:23 +08:00
chenshuobo
b07ccb4bf5
feat(meituan-coupon): 采集记录头图大小/类型 + 读取侧缩放口子 + 回填脚本 ( #71 )
...
Reviewed-on: #71
Co-authored-by: chenshuobo <chenshuobo@wonderable.ai >
Co-committed-by: chenshuobo <chenshuobo@wonderable.ai >
2026-06-23 21:09:10 +08:00
marco
2164155a23
docs(cps): 加 CPS 发券分发 + 微信授权 交接文档
...
设群/建活动/生成落地页短链/美团对账/统计 + 微信网页授权拿 openid 做用户级统计的
完整说明,含数据流/数据表/平台差异/端点清单/配置项/代码地图/部署/排障/已知技术债。
挂入 docs/README 的 guides 索引。供他人接手。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-23 18:02:08 +08:00
marco
e81fd0a176
fix(alembic): 合并多 head( #65 device 迁移 + comparison_token_cols)
...
#65 的 device 迁移链与 comparison_token_cols 都从 ceb286289426 分叉成双 head,alembic upgrade head 报 Multiple head revisions;加 merge 迁移 4dc2af7ebe74 合并(空 DDL 仅接链)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-23 17:58:30 +08:00
chenshirui
a6f55a00b8
feat(device): 无障碍保护存活心跳检测 + 掉线后置检测(pull) ( #65 )
...
Co-authored-by: 陈世睿 <2839904623@qq.com >
Reviewed-on: #65
Co-authored-by: chenshirui <chenshirui@wonderable.ai >
Co-committed-by: chenshirui <chenshirui@wonderable.ai >
2026-06-23 17:33:38 +08:00
marco
a584e1f59f
feat(comparison): 比价记录累计 LLM token(入/出)并在 admin 展示
...
server 从 llm_calls[].usage 累加 prompt/completion_tokens 落库,admin 比价记录列表/详情据此展示 token 数与估算成本;配套迁移 comparison_token_cols。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-23 17:24:54 +08:00
marco
e13f0f7658
fix(cps): 群内微信用户按"本群点击"算,不用 first_group_id
...
first_group_id 只记首次授权群;同一用户(cookie 已存)再去别的群不再走授权回调,
会被漏算(实测:用户首次在群47授权,再去群52点击,群52详情显示0)。
改为:在本群有带 openid 点击的 distinct openid 即本群用户,关联 cps_wx_user 画像。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-23 16:36:57 +08:00
chenshuobo
f868414966
feat(feedback): 补齐反馈历史审核发奖闭环 ( #66 )
...
- 新增反馈审核字段和迁移,支持 pending/adopted/rejected、未采纳原因和采纳金币
- 增加用户端反馈历史 records 接口和 admin 采纳/拒绝接口
- 采纳时同事务写状态、金币流水和审计日志,拦截重复审核
- 验证:pytest tests/test_feedback.py tests/test_admin_write.py tests/test_admin_read.py
---------
Co-authored-by: lowmaster-chen <1119780489@qq.com >
Reviewed-on: #66
Co-authored-by: chenshuobo <chenshuobo@wonderable.ai >
Co-committed-by: chenshuobo <chenshuobo@wonderable.ai >
2026-06-22 23:18:42 +08:00
zhuzihao
900cc83d38
fix(ad): 广告配置默认信息流位 104090333 → 104142227 ( #67 )
...
_AD_CONFIG_DEFAULTS 里 compare/coupon_feed_code_id 的旧默认 104090333
不在穿山甲应用 5830519 名下(请求报 44406「配置为 null」、出不了广告);
2026-06-21 真机核对后台,5830519 名下真实信息流位是 104142227「信息流 1」。
客户端接入 /api/v1/platform/ad-config 下发后会以此默认为准(空库回退),
故更正默认值。注:若 DB 里已存过 104090333,仍需在 admin 广告管理页改存为 104142227。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #67
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-22 23:00:27 +08:00
zhuzihao
3cab75b6ac
feat(admin-withdraw): 提现详情金币记录分页 + 风险标签拆分 ( #64 )
...
- user_coin_records 增返 total(三源 granted 计数和),支持前端页码分页
- 风险标签「历史异常提现」拆成「提现拒绝N笔」「提现失败N笔」(拒绝=人工驳回退款,失败=打款失败退款)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #64
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-21 23:41:40 +08:00
marco
08bbb5775e
fix(compare-record): platform_results 类型 list→dict,修部分失败比价上报 422 丢记录
...
ComparisonRecordIn.platform_results 误声明为 list,但 pricebot/客户端透传的是 dict
({platform_id: {status,reason,...}})。该字段仅「失败/部分失败」比价 done 帧才带,故
成功比价一直正常入库、部分失败比价上报被 pydantic 422 拒导致不入库、记录页查不到。
改 dict 后用线上真实 body 验证通过;字段仅随 raw_payload 落库、无结构化列、零副作用。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-21 05:11:29 +08:00
zhuzihao
8a2f72d366
feat(user): 昵称上限放宽到 20 字(与客户端/原型一致) ( #63 )
...
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #63
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-21 00:16:40 +08:00
marco
f7a3ef2e0b
feat(internal): 启动确认窗 agent 兜底样本落盘(pricebot 回写)
...
- 新增内部端点 POST /internal/launch-confirm-sample(X-Internal-Secret 鉴权):
pricebot 对没见过文案的启动确认窗用 LLM 兜底放行后,把样本(host 包 / 弹窗树 /
LLM plan / 设备 locale 机型)回写落 launch_confirm_sample 表,供研发人工沉淀回
pricebot 规则 yaml
- 配套: model LaunchConfirmSample + schema + repo(都上报不去重) + alembic 迁移
launch_confirm_sample_table(down_revision=cps_wx_user) + main 注册 router +
models/__init__ 登记
- docs: 后端技术实现.md 加 §6.5 pricebot 回写内部端点说明
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-20 13:39:45 +08:00
marco
d8358a6816
fix(user): 注销账号前置校验资金余额 + 释放微信/邀请码唯一约束
...
- delete_account 端点: 有可提现现金余额 / 在审提现单(reviewing)时拒绝注销(409), 防余额锁死在 deleted 行无法退出; pending 提现已在途不拦(对账 worker 不依赖 user.status)
- soft_delete_account: 注销连带清 wechat_openid/nickname/avatar(释放微信绑定槽, 否则该微信永久无法再被任何账号绑定→提现通道锁死) + invite_code(释放邀请码唯一槽)
- wallet 新增 get_cash_balance_cents(只读, 不建账户) 供注销前置校验; 复用 has_reviewing_withdraw
- 新增 tests/test_delete_account.py
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-20 02:03:33 +08:00
marco
95a07f75b7
feat(ad): 穿山甲广告配置后台化(app_id/各位ID/验签密钥/各场景开关)
...
- app_config 存 ad_config(默认=客户端当前内置 ID,空库维持现状)
- GET /platform/ad-config 下发客户端(不含验签密钥 reward_mkey)
- admin GET/PATCH /admin/api/ad-config 读写(operator/finance + 审计,mkey 不记明文)
- pangle 回调验签 m-key 优先读后台配置、.env 兜底兼容
- 比价/领券信息流位拆分(compare/coupon_feed_code_id)
- 客户端需发版接入 /platform/ad-config 才生效;本次后端就绪、不影响现状
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-20 01:35:17 +08:00
marco
733a51260f
feat(cps): 微信网页授权加总开关 WX_MP_OAUTH_ENABLED(默认关)
...
服务号认证审核中,先关掉落地页授权(走原逻辑不跳授权/不拿 openid),整套微信代码保留。
审核通过后 .env 置 WX_MP_OAUTH_ENABLED=true + 重启即启用,无需改代码。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-19 12:03:58 +08:00
marco
7fd70a264d
feat(cps): 微信网页授权接入——落地页拿 openid/昵称头像,用户级群统计
...
- 服务号网页授权(WX_MP_APPID/SECRET,区别于 App 的 WECHAT_APP_ID):
落地页 base 静默拿 openid;点「领券」补 userinfo 拿昵称头像(交互触发避快照页)
- 新表 cps_wx_user + cps_click 加 openid + 迁移;integrations/wx_oauth
- /c/{code} 授权链路 + 回调 /wx/oauth/cb + cookie 免重复授权;click 带 openid
- admin 群详情加「群内微信用户」(领券画像)端点
- 下单归因到人需 user-level sid,本期只做身份+领券侧
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-19 11:44:11 +08:00
marco
4b98d405d7
feat: OTA 版本下发端点(检查更新)
...
- app_config 加 get/set_app_version(独立 key,复用表不进 CONFIG_DEFS)
- GET /api/v1/platform/app-version 公开读 + POST /internal/app-version 内部写(X-Internal-Secret)
- tests/test_app_version.py 5 端到端测试
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-19 08:31:31 +08:00
marco
63400c29cc
docs(api): 补录比价透传端点到 API 总览
...
intent/precoupon/step、intent/step、trace/finalize、meituan/top-sales 补进总览表,
标注 Phase 流程与不鉴权; 更新最后更新日期到 2026-06-18。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-19 07:04:48 +08:00
marco
42be0f828f
feat(cps): 加微信网页授权域名校验文件端点(MP_verify)
...
coupon.shaguabijia.com 全反代 app-server,微信请求该域名根目录 MP_verify_xxx.txt
验证域名归属(放根目录)。为微信网页授权(落地页拿 openid)接入做准备。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-19 05:53:45 +08:00
marco
5bafe5b71a
feat(cps): 群每天明细端点(点击+订单按天合并,含退款/净佣金)
...
- repo group_order_daily:订单按 pay_time 北京分天聚合(口径同 _order_agg)+ 退款佣金合计
- GET /groups/{id}/daily?days=N:点击侧(补零连续天)与订单侧(仅美团群有 sid)按天合并
- 用生产 g46 真实数据验证按天口径无误
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-18 17:32:18 +08:00
marco
498da05995
docs(database): 补充数据库表文档(cps/invite/price_observation/store_mapping 等)
...
(工作区既有改动, 非本次会话产出)
2026-06-17 23:32:36 +08:00
marco
3cab3b9055
fix(compare-record): admin 放行 cancelled 筛选 + llm_calls 拉取改后台任务
...
- admin 列表 status pattern 加 cancelled(客户端会上报用户终止比价)
- 上报端点拉 pricebot llm_calls 改 BackgroundTasks 异步回填, 不阻塞上报响应(独立 session)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 23:32:36 +08:00
marco
f790519765
fix(cps): 时序分桶对 naive datetime 按 UTC 兜底(本地 SQLite 错桶)
...
clicked_at.astimezone 对 naive(SQLite 读回无 tz)会按系统时区解释→分桶偏移;
naive 先按 UTC 兜底再转北京,与 queries._as_utc 约定一致。生产 PG(tz-aware)本就正确。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 23:06:05 +08:00
marco
a2fd666203
feat(admin): 比价记录 debug 页后端——补全链路 + DB 迁移
...
model/schema 的 debug 字段已在 4ee6de2 起头,本次补完整条链路:
- repo upsert 存客户端环境/性能字段;端点同机拉 pricebot llm_calls 落库 + 算 llm次数/重试
- schema 加 platform_results 透传(admin「卡在哪一步」从 raw_payload 读)
- admin 比价记录查询接口(按 user_id/phone 列表分页 + 详情含 llm_calls)
- 迁移 comparison_debug_fields: 给 comparison_record 加环境/性能/llm_calls 列(全 nullable)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 22:33:33 +08:00
marco
aa898fc09a
feat(cps): 群点击时序聚合端点(天/小时级 PV/UV/复制,补零连续桶)
...
- repo group_click_timeseries:北京时区分桶,visit/copy 的 PV/UV,无数据补 0
- GET /cps/groups/{id}/timeseries?granularity=day&days=N | granularity=hour
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 22:31:34 +08:00
marco
4ee6de2548
feat(cps): 活动支持编辑(PATCH /activities/{id})
...
- CpsActivityUpdate schema + repo update_activity(部分更新,非 None 覆盖)
- router 按「合并后最终值」校验平台必填项(同新建口径),写审计 cps.activity.update
- 可改名/平台/对应字段/落地页图/备注/状态
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 22:13:35 +08:00
marco
c78cfc838f
feat(cps): 群/活动支持删除(operator,二次确认,不级联删已发链接)
...
- repo delete_group/delete_activity + DELETE /cps/groups/{id} 与 /cps/activities/{id}
- 硬删记录但保留 cps_link:已发短链继续可用(落地页不依赖 group/activity 存活)
- 写审计 cps.group.delete / cps.activity.delete
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 21:43:34 +08:00
marco
d55f47fc59
feat(cps): 淘宝活动落地页图做活——每活动关联图,运营上传/选已有
...
- model: cps_activity.image_url + 迁移 cps_activity_image 回填存量淘宝活动为现有图
- admin: 新建淘宝活动必传图(上传 /upload-image 或选已有 /activity-images),存绝对 URL
- 落地页 cps_redirect: 按 link→activity 取图展示,缺图兜底默认图,img src 经 html.escape
- media: save_cps_image + to_abs_media_url(基于 CPS_REDIRECT_BASE,跨域 admin-web 可显示)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 21:32:10 +08:00
marco
7368a1ca8a
feat(cps): 淘宝落地页改用设计图主视觉 + 复制按钮上提至 75% 屏高
...
- banner/card 手画区域整体换成设计图 /media/taobao_landing.jpg(~240KB jpg)
- 复制淘口令按钮从底部固定上提到 top:75vh, 上方留白
- 图入库(data/media 白名单, 同 dl.html 先例), 同域 /media serve
- 淘口令注入 / 复制 / toast 逻辑不变
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 20:53:34 +08:00
marco
3a40f617bd
feat(cps): 后端接入淘宝/京东多平台(payload + 群多平台 + 复制统计 + 迁移)
...
- model: cps_group.platforms 多选 / cps_activity.payload(淘口令/京东链接)
/ cps_link.sid 可空 + 复制统计
- admin API: 群与活动平台多选、批量生成落地页短链 referral-links、对账字段可空
- 落地页 cps_redirect: 淘宝展示淘口令(记 copy 事件) / 京东 302 / 美团原逻辑
- 迁移 cps_v2_platforms: 加 platforms/payload/event_type 列, sid 放宽可空(含 downgrade)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 19:21:20 +08:00
marco
003fd9d986
feat(compare): 失败比价记录支持 trace 链接
...
compare.py 加 /trace/finalize 透传(按 trace_id 一致性 hash 落同一 pricebot 进程);compare_record.py list_records 加 include_trace,对本人记录在调试态(客户端 agent 调试模式)放行 trace_url。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 15:56:15 +08:00
ouzhou
04edb50acc
feat(admin/ad): 提现自动对账后台开关 + 客户端广告 flags 端点 + 穿山甲多 m-key 验签 ( #59 )
...
- 自动对账接入 app_config 运行时配置(新增 bool 配置类型):env 部署总闸 + DB 运营日常开关
双层;worker 每轮读 DB 即时生效,健康检查改报「env AND DB」实际生效态
- 新增 GET /api/v1/platform/flags(不鉴权)下发 comparing_ad_enabled 远程 kill-switch,
客户端拉取后缓存;空库回退默认 True
- 穿山甲发奖回调支持多激励位 m-key(三命名项 PANGLE_REWARD_SECRET_TEST/_DEDICATED/_PROD
+ 旧逗号分隔合并去重),verify_callback_sign_any 逐个验签任一通过即受理;向后兼容旧单 key
- 补 test_platform / bool config 用例;app_config 文档同步
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #59
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-17 10:37:54 +08:00
marco
3483c1bba9
Merge origin/main (meituan-etl #57/#58) into CPS 分发
...
整合实习生 meituan-etl 全国扩城 + NUL 清洗,与本次 CPS 分发对账并线。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 10:03:29 +08:00
marco
277f9b16a2
feat(cps): 群发券分发与对账 + 短链点击追踪
...
群(sid)/活动池/转链(带sid)/美团 query_order 按 sid 对账/按群统计;
短链 /c/{code} 记点击(PV/UV)→302 跳美团,点击→下单漏斗。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 10:00:29 +08:00
marco
9d93b70b9b
feat(user): username 对外展示 + 默认昵称 + 存量回填
...
代提工作区既有的他人在制品(非本次 CPS);CPS 迁移链 cps_tables 依赖其
b3f1a2c4d5e6 迁移,需一并提交。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-17 10:00:29 +08:00
chenshuobo
f853938095
fix(meituan-etl): 清洗文本 NUL 字节 + 逐城入库容错(修全量首灌崩在脏数据) ( #58 )
...
全国 359 城全量首灌崩在厦门:美团某券文本字段含 NUL(0x00),PostgreSQL
text/jsonb 拒绝该字节,整批 upsert 抛 DataError → --once 进程崩、后续 300+ 城
全不跑(本地 20 城没撞上、跑全国才暴露)。两处修复:
- _strip_nul 递归清洗入库 dict 所有字符串(含 raw JSON)的 NUL;
- 逐城 _upsert 包 try/except + rollback,单城入库失败记 fails 跳过,不再让
一条脏数据 / 一次抖动拖垮整轮 359 城。
---------
Co-authored-by: chenshuobo <1119780489@qq.com >
Reviewed-on: #58
Co-authored-by: chenshuobo <chenshuobo@wonderable.ai >
Co-committed-by: chenshuobo <chenshuobo@wonderable.ai >
2026-06-16 21:19:47 +08:00
chenshuobo
783dfd059d
feat(meituan-etl): 离线库扩到全国 359 城(多城并发抓取入库) ( #57 )
...
智能推荐 / 销量最高两 tab 的离线库此前只有北京(ETL 写死 cityId),改为遍历
美团官方城市字典 359 个地级市全量抓取。实测一个地级市 cityId 已覆盖其下辖县级市
(徐州→邳州/新沂/睢宁等),按地级市抓即可,无需区县层级。
- 城市字典:tools/gen_meituan_cities.py 从美团 Excel 生成随仓库 JSON
(app/integrations/data/meituan_cities.json,359 城),app/integrations/cities.py 读取;
- ETL:city_id 参数化 + 城市级并发(默认 12)+ worker 启动错峰削平瞬时峰值
+ 主线程逐城串行入库(Session 不跨线程);
- 配速实测:单城全量 ~110s/~2300 条;15 并发抓完一轮 ~50-60min,402 占 3% 退避全消化;
每 3h 一轮全量(--interval 10800),窗口充裕;
- prune 双护栏:本轮 0 入库 或 失败城占比 >5% 时跳过,防上游故障/大面积限流误删全表;
- 仅写入侧;读取侧(rec/top-sales 按城过滤)待后续(依赖用户定位→cityId 映射,字典无经纬度)。
---------
Co-authored-by: chenshuobo <1119780489@qq.com >
Reviewed-on: #57
Co-authored-by: chenshuobo <chenshuobo@wonderable.ai >
Co-committed-by: chenshuobo <chenshuobo@wonderable.ai >
2026-06-16 15:40:46 +08:00
marco
f97048ff56
fix(alembic): 合并多 head (store_mapping_jd_dl_invalid + ad_revenue) 修复 0.1.2 部署
...
PR #56 的 jd_deeplink_invalid 迁移与 ad_revenue 报表迁移从同一 branchpoint
分叉成双 head, 部署时 alembic upgrade head 报 "Multiple head revisions"。
加一个 no-op merge 节点统一 DAG, 两支迁移列不冲突。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-16 01:39:05 +08:00
marco
f15ca74a22
feat(store-mapping): JD 缓存 deeplink 失效标记(对标淘宝) ( #56 )
...
Reviewed-on: #56
2026-06-16 01:25:57 +08:00
ouzhou
f7d86011c1
feat(ad-revenue): admin 广告收益报表(按 用户/日期/类型/应用/代码位 聚合) ( #54 )
...
- 新增 GET /admin/api/ad-revenue-report:展示条数/收益 + 复用金币审计逐条复算做发奖对账
- ad_ecpm/ad_reward/ad_feed_reward 各加 app_env + our_code_id 两列(alembic 迁移)
- ecpm-report / feed-reward 接收并落库 app_env/our_code_id;激励发奖按 ad_session_id 回填
- ad_audit 抽出 audit_rows,报表与逐条审计复用同一复算口径
- 组级 matched 改「组内逐条全一致」,避免应发和==实发和的互相抵消掩盖错误
- list_feedbacks 改 offset 分页并返回 total(配合 admin 页码分页)
- 反馈正文上限 _CONTENT_MAX 2000→200
- 文档:新增 admin-ad-revenue-report,更新 ecpm/feed-reward/feedback 及对应 db docs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #54
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-15 23:13:14 +08:00
marco
9ec9d2389d
feat(store_mapping): 淘宝缓存 deeplink 失效检测→标记→回退(app-server 侧) ( #53 )
...
加 taobao_deeplink_invalid_at 列(迁移+model);mark_taobao_deeplink_invalid 按 shopId 标记所有行;lookup_nearest 过滤失效淘宝候选;新增 POST /internal/store-mapping/invalidate。+ 7 单测。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
Reviewed-on: #53
2026-06-15 18:07:32 +08:00
liujiahui
8fa55eec3e
fix(coupon): 领券弹窗频控按 App 独立 + debug 全重置补全 ( #52 )
...
bug: 弹窗 engagement 表只按 (device, 日) 全局记一条,美团弹过/领过就把整台设备当天
标记 engage,淘宝/京东被压住不弹。需求是美团/淘宝/京东各自独立、每日各弹一次。
改动:
- model: CouponPromptEngagement 加 package 列,唯一约束 (device,日) → (device,package,日)
- alembic: 新增迁移 coupon_engage_per_package(加列 + 改唯一约束, batch_alter_table)
- repository: has_engaged_today / mark_engagement 加 package 维度;新增 reset_today_completion
- api: should-show / dismiss 接收 package;coupon_step(step=0) 按 App 记 engagement;
补 /prompt/shown 接口(客户端一直在调但后端缺失, 原 404);
补 /completed-today/reset(开发设置全重置用, 解首页卡置灰)
验证: curl 端到端 —— 美团弹过后 should-show 美团=false 淘宝/京东=true;
shown/reset/completion-reset 端点全 ok。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: no_gen_mu <liujianhishen@gmail.com >
Reviewed-on: #52
Co-authored-by: liujiahui <liujiahui@wonderable.ai >
Co-committed-by: liujiahui <liujiahui@wonderable.ai >
2026-06-14 23:41:54 +08:00
ouzhou
27f76918b2
feat(admin): 运营后台跟进——review 加固 + 反馈改版 + 列表页码分页 ( #51 )
...
本 PR 汇合三块运营后台改动(原 #50 仅含其中「加固」一块,已并入本 PR 并关闭)。
## 1. review 加固 (436b2a3 )
- 超管防自锁:降级/禁用最后一个 active super_admin 前校验,杜绝零超管死局
- 时间筛选统一 tz-aware(列为 timestamptz),比较绝对时刻、不依赖 DB 会话时区
- 上报审核 / 调余额(set·扣减)加行锁,防并发/连点重复发钱
- ad_audit 复算排序补 id 次级键;health-check 限 finance;调账/拒绝 reason 去空白校验
## 2. 反馈改版 (5a18dbb )
- contact 可选、截图≤6;admin 反馈列表筛选/排序;admin·wallet 接口调整 + docs
## 3. 列表页码分页 (1a7a624 )
- CursorPage 加 total;新增 offset_paginate(count 与分页同源)
- 上报/审计日志从 id 游标改 offset 分页(支持跳页)
- 用户 / 提现 / 上报 / 审计日志 四页接入页码分页
测试:admin 套件 47 passed。前端配套改动见 shaguabijia-admin-web。
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #51
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-14 22:54:07 +08:00
liujiahui
47812f7fcc
比价结算记录 schema + comparison 模型 ( #44 )
...
## 改动
- 支撑客户端比价结算页:补 `compare_record` schema 字段 + `comparison` 模型调整
- docs/database 索引合并(领券三表 + onboarding_completion,表数对齐 28)
## 验证
- 模型/schema parse OK;已 merge origin/main 解 OVERVIEW/README 文档冲突(取并集)
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: no_gen_mu <liujianhishen@gmail.com >
Reviewed-on: #44
Co-authored-by: liujiahui <liujiahui@wonderable.ai >
Co-committed-by: liujiahui <liujiahui@wonderable.ai >
2026-06-14 04:16:52 +08:00
marco
8659a7ed2b
feat(store_mapping): 跨平台店铺映射表 + pricebot 内部上报端点 ( #48 )
...
新增「平台店铺表」资产层: 淘宝比价拿到 shopId 后, pricebot server→server 把跨平台
店铺身份(各平台 id/名 + 地理 + 来源)落库, 作为未来"我见过这家店→跳过重搜/匹配"的源头。
与 price_observation(价格事实)平行、独立。
- models/store_mapping.py: store_mapping 表 22 列 —— 跨平台身份(id/name_taobao/meituan/jd)
+ 地理(city/geohash/lng/lat/taobao_address) + 溯源(source_platform/trace_id/device/user)
+ 淘宝原料(share_url/resolved_url/deeplink) + attrs(JSONB) + created_at。
- schemas/store_mapping.py + repositories/store_mapping.py: append-only, trace_id 幂等
(pricebot 重试/replay 不重复写; 并发 IntegrityError 兜底返已存在行), 跨方言安全。
- api/internal/store.py: POST /internal/store-mapping(复用 price.py 共享密钥 X-Internal-Secret 校验)。
- 注册 model(__init__) + router(main.py); 迁移 store_mapping_table 接现 head coin_txn_task_ref_uq。
验证: alembic 单 head + 零模型/迁移漂移; TestClient 全链(无密钥401/有密钥inserted=1/幂等inserted=0/错密钥401)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
Reviewed-on: #48
2026-06-14 01:24:29 +08:00
ouzhou
cce3a01de1
fix(marquee): 首页轮播脱敏名改为按 user_id 恒定 + 去 Faker 依赖 ( #49 )
...
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #49
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-13 23:44:57 +08:00
ouzhou
8d7b91219a
feat(wallet): 解绑微信前对待审核提现二次确认 ( #46 )
...
## 改动
解绑微信前若有**待审核(reviewing)**提现单,首次解绑被拦下返回二次确认,用户确认后带 `force=true` 才真解绑。
- `schemas`: `UnbindWechatRequest{force}` + `UnbindWechatResultOut{needs_confirm, message}`
- `repositories`: `has_reviewing_withdraw`(只看 reviewing)
- `api`: `unbind-wechat` 接受可选 body(兼容旧无 body 客户端);`bound` 返回真实绑定态
- `docs/api`: 同步协议
## 为什么只拦 reviewing
reviewing 单解绑后审核打款会回读空 openid → 自动退回现金(钱不丢、只为让用户知情);pending 转账已在途、解绑影响不到,不拦。
配套客户端见 shaguabijia-app-android 同名分支。
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #46
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-13 03:53:43 +08:00
zhuzihao
25484aadb8
feat(user): 昵称上限放宽到 20 字(与客户端/原型一致) ( #47 )
...
ProfileUpdateRequest.nickname 的 max_length 16 → 20,与客户端 take(20) 和
原型 settings.html(maxlength=20)对齐。修复 17–20 字昵称保存时后端返回 422、
客户端弹「更新失败,请重试」的问题。DB 列为 String(64),放得下,无需迁移。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #47
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-12 23:27:35 +08:00
marco
e8bd12cc1f
feat(onboarding): force_onboarding 改设备维度 + admin 设备管理 + status 查询
...
废弃 force_onboarding(运营按用户强制引导,客户端不认——被 onboarding_completed 压过),
改用 onboarding_completion(设备+账号 维度),admin 直接增删该表记录控制重走。
- 删 force_onboarding: User 列 + auth/admin schema + admin 接口 + mutations + repo
clear + /onboarding/complete 调用 + 3 测试;新增 alembic 迁移删列
- admin 设备管理: 列设备(按 device 聚合) / 重置单设备 / 全部重置(清 onboarding_completion)
- 新增 GET /api/v1/user/onboarding/status: 客户端已登录启动查"本设备是否要重走引导"
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-12 00:51:47 +08:00
ouzhou
57ddcd356b
feat(wallet): 0 点定时把用户金币「到分全额」自动兑成现金 ( #45 )
...
客户端已删手动兑入口、改「0 点自动兑现金」,服务端补这条批处理:
- wallet.exchange_coins_to_cash 加 remark / enforce_min 参数:enforce_min=False(自动兑)只要够
1 分(COIN_PER_CENT)即可,不受手动兑下限约束;remark 可定制。
- 新增 wallet.daily_auto_exchange(db):扫 coin_balance>=100 的用户,到分全额(floor(余额/100)*100)
兑现金,零头留下次;幂等键=当天是否已有 exchange_in 流水(_has_exchange_in_on);逐用户独立事务,
单用户异常 rollback 不中断。
- scripts/daily_auto_exchange.py(--once / --loop):带 .env AUTO_EXCHANGE_ENABLED 开关(false→no-op)
+ 文件锁防重入;deploy/ 配 systemd service+timer(0 点触发)+ 说明。
- config.AUTO_EXCHANGE_ENABLED(默认 true)+ .env.example。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #45
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-11 23:36:26 +08:00
marco
5f9af204ea
fix(alembic): 合并 user_force_onboarding 与 comparison_idx/coupon_daily 双 head
...
#43 的 user_force_onboarding 迁移挂在 onboarding_completion 上,与
d4b87c5847de(comparison_status_created_idx + coupon_daily 合并、同样以
onboarding_completion 为父) 形成并列双 head,alembic upgrade head 报
Multiple head revisions,ecs1 部署会在迁移步失败未重启。
加纯合并迁移 9b894f5fff05 (Revises: d4b87c5847de, user_force_onboarding)
收敛为单 head,upgrade/downgrade no-op 不改 schema。本会话第 2 次、历史第 3
次此类问题(前为 a8c47fc4dc39 / be5e94e / f01db5d77dac)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-10 22:49:33 +08:00
zhuzihao
3d07461ef5
feat(force-onboarding): 运营一键开启新手引导 — User.force_onboarding + admin 接口 + /me 带出 + 走完自动清 ( #43 )
...
- User 新增 force_onboarding 列(仿 debug_trace_enabled)+ 迁移 user_force_onboarding。
- POST /admin/api/users/{id}/force-onboarding(operator 角色 + 审计 user.force_onboarding.set)。
- UserOut(/me 与登录响应)带出该字段;/api/v1/user/onboarding/complete 走完即自动清回 false,只触发一次。
- 测试:admin 设置+审计+角色守卫、走完引导自动清标记。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: zzhyyyyy <2685922758@qq.com >
Reviewed-on: #43
Co-authored-by: zhuzihao <zhuzihao@wonderable.ai >
Co-committed-by: zhuzihao <zhuzihao@wonderable.ai >
2026-06-10 22:27:06 +08:00
ouzhou
db399a40fa
feat(admin): 首页门面数字保底/护栏 + 轮播种子真实化与批量 + 比价记录索引 ( #42 )
...
- ops_stat: 真实值 offset→保底(max);初始基数过只增不减护栏
- ops_marquee: 脱敏名手机尾号+中文昵称混合;金额长尾;时间随机抖动;真实+种子不足兜底补满;真实记录查询 ~30s 缓存
- comparison_record: 复合索引 (status, created_at) + 迁移(PG CONCURRENTLY);修复 alembic 多 head(merge 改挂 onboarding_completion)
- 轮播种子批量删除/启用接口 + ids 上限;docs 同步
- 附带并行会话 WIP:admin users / wallet
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #42
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-10 22:26:51 +08:00
marco
a753843804
美团 feed 改用离线库 + 三 tab 降级兜底(替换 main 的实时版 feed) ( #40 )
...
把 main 的实时 feed 换成离线库版本 + status 降级兜底:
- rec 智能推荐:改走离线库 meituan_coupon 筛佣金≥3%(SQL 去重+排序+分页),不再实时撞限流
- distance 距离最近:改实时搜索翻页(sortField=6),按用户真实位置由近及远排
- top_sales 销量最高:改 SQL DISTINCT ON 分页,修原全表拉取+全量解析的翻页卡顿
- 四路加 status(ok/empty/degraded):空库/上游失败均返 200 不再 5xx;/coupons 502 软化为降级
- FeedResponse/CouponListResponse 加 status 字段(默认 ok,向后兼容)
注意:替换 main #18 的实时 feed 实现,需 review 行为变更;依赖的离线库由 ETL 灌(见 meituan-etl PR)
---------
Co-authored-by: chenshuobo <1119780489@qq.com >
Reviewed-on: #40
2026-06-10 20:05:50 +08:00
zhangxianze
bf82c68408
feat(compare): 美团比价前用券透传端点 /intent/precoupon/step ( #41 )
...
## 背景
客户端在美团源比价的**意图识别前**先循环调本端点用券。app-server 只做透传壳(决策在 pricebot-backend)。
## 改动
- 新增 `POST /api/v1/intent/precoupon/step` → 透传到 pricebot-backend `/api/intent/precoupon/step`
- 同 `/intent/step` 透传壳: 不鉴权、不做 schema 校验, device_id 透传区分设备
## 测试
- 链路自测(:8770 → :8000) + 真机实测用券成功
## 关联(跨三仓库, 同名分支 `feat/meituan-precoupon`)
- WonderableAI/pricebot-backend
- WonderableAI/shaguabijia-app-server
- WonderableAI/shaguabijia-app-android
**改协议字段需三仓库一起改。**
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: xianze <ze@192.168 .0.128>
Reviewed-on: #41
Co-authored-by: zhangxianze <zhangxianze@wonderable.ai >
Co-committed-by: zhangxianze <zhangxianze@wonderable.ai >
2026-06-10 19:35:58 +08:00
zzhyyyyy
0ae19dc49c
合并远程更改并解决冲突
2026-06-10 18:47:18 +08:00
zzhyyyyy
389ece9052
Merge branch 'feat/onboarding-completion'
2026-06-10 18:44:29 +08:00
marco
455884401f
新增美团 CPS 券定时抓取入库(北京试点),作为"销量/佣金排序"的本地数据源 ( #38 )
...
Co-authored-by: chenshuobo <1119780489@qq.com >
Reviewed-on: #38
2026-06-10 18:34:40 +08:00
zzhyyyyy
dacb37e3e1
feat(onboarding): 新手引导完成按 设备+账号 去重(表/模型/仓储/端点/迁移/测试)
...
- onboarding_completion 表(user_id+device_id 唯一约束)+ model + repository
- POST /api/v1/user/onboarding/complete 标记完成;登录响应 TokenWithUser.onboarding_completed
(按登录请求带的 device_id 判定是否已走过引导,跨卸载重装稳定)
- alembic 迁移 onboarding_completion(rebase 到最新 main 后已链在 coupon_daily_completion 之后,单 head)
- docs/database 文档 + tests/test_onboarding.py + run.bat(本地启动脚本)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 18:32:51 +08:00
liujiahui
0890e693d7
feat(coupon): 今日跑完整轮领券后端记录 + 查询(首页置灰源) ( #34 )
...
新增 coupon_daily_completion 表:按 (device_id, 自然日) 记"今天是否已跑完
整轮领券(到 done 帧)"。客户端据此把首页「去领取」卡置灰、不可点。
口径(用户决策 A 方案):到 done 即算,不管单券成败 —— 失败/跳过常是无障碍/
环境问题,重复点也补不回来。判断维度 device_id,与 engagement/claim 一致。
- model CouponDailyCompletion(uq device+complete_date,仿 CouponPromptEngagement)
- repo has_completed_today / mark_completed_today(幂等 upsert + IntegrityError 兜底)
- schema CouponCompletedTodayOut(completed: bool)
- coupon_step 透传链路在 action.command=="done" 那帧 best-effort 写完成记录
(pricebot 只在整轮全跑完才保留 command=="done",故无需再判 continue)
- GET /api/v1/coupon/completed-today?device_id= 供首页查询
- alembic 迁移 coupon_daily_completion(down_revision=0cf18d590b1d 当前 head)
合并后需 alembic upgrade head,否则 /completed-today 500。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: no_gen_mu <liujianhishen@gmail.com >
Reviewed-on: #34
Co-authored-by: liujiahui <liujiahui@wonderable.ai >
Co-committed-by: liujiahui <liujiahui@wonderable.ai >
2026-06-10 16:07:33 +08:00
ouzhou
0bddce516f
feat(ad): eCPM 单位统一为分 + 新增看广告金币审计接口 ( #32 )
...
- eCPM 口径修正:getEcpm 原值是分/千次,新增 parse_ecpm_fen;parse_ecpm_yuan
改为 ÷100 转元,因子1 阈值按元判档(100/200/400),收益换算用元
- test-grant 用客户端按 ad_session_id 上报的真实 eCPM 发奖(取不到/≤0 兜底 200)
- 新增 GET /admin/api/ad-coin-audit 只读复算对账(router/repo/schema),复用发奖公式
- 同步更新 docs/api、docs/database 的分/元口径
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #32
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-10 15:53:52 +08:00
xiebing
90fe6d6aa7
feat(invite): 邀请落地页改跳应用商店(替代手点右上角下载) ( #36 )
...
- 浏览器内:点按钮→写剪贴板归因→跳 market:// 唤起手机自带商店
- market:// 唤不起(2.5s 无响应)→ 兜底跳应用宝网页下载页
- 微信内保留引导去浏览器;iOS 维持"即将上线"
- 仅改 dl.html;App/后端不动,归因+指纹兜底机制不变
- market:// 各厂商兼容性真机待验
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
Reviewed-on: #36
Co-authored-by: xiebing <xiebing@wonderable.ai >
Co-committed-by: xiebing <xiebing@wonderable.ai >
2026-06-10 15:53:14 +08:00
marco
be5e94ed6d
fix(alembic): 线性化 f8d3b1e60a27 接 a8c47fc4dc39,消除与 #33 mergepoint 平行的双 head
...
f8d3b1e60a27 最初基于旧 main(#31 双 head)写、down_revision 指向那对父;但 #33 已用 a8c47fc4dc39 合并那对 head,合入 main 后 a8c47 与 f8d3 平行 → 又成双 head,alembic upgrade head 报 Multiple heads、deploy 迁移会失败。
改 f8d3 的 down_revision 为 a8c47fc4dc39(线性接其后、只加两列),收口单 head。已 SQLite upgrade head 验证整链可 apply。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 15:43:54 +08:00
marco
953a05a5e6
feat(debug-trace): 调试链接权限 + trace_url 落库与按权限下发 ( #35 )
...
- user 加 debug_trace_enabled、comparison_record 加 trace_url
- 迁移 f8d3b1e60a27:加两列,down_revision 用 tuple 顺带合并既存双 head(invite_fingerprint_table + 044dce6e9b1f)
- UserOut 加 debug_trace_enabled → /me 与登录响应带出
- ComparisonRecordIn/Out 加 trace_url;upsert 落库
- /compare/records 列表与详情按 user.debug_trace_enabled 下发;详情连 raw_payload 里那份一并抹掉,防权限绕过
- admin 加 POST /users/{id}/debug-trace(operator + 审计),列表带该字段
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
Reviewed-on: #35
2026-06-10 15:35:08 +08:00
zhangxianze
e69788eb41
feat(admin): 上报更低价人工审核(通过发1000金币/拒绝填理由) ( #33 )
...
运营后台审核用户上报的「某平台比我们算的最低价更便宜」+截图。
- routers/price_report.py:列表 + /summary、approve|reject(require operator)
- repositories:list_price_reports + price_report_summary(queries)、review_price_report(mutations)
- core/rewards.py:PRICE_REPORT_REWARD_COINS=1000(通过即发,grant_coins 由 router 同事务调)
- admin/main.py:注册 price_report_router
跨端配套:前端 admin-web feat/price-report-review;端上金币刷新 android feat/price-report-review。
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: xianze <ze@192.168 .0.128>
Reviewed-on: #33
Co-authored-by: zhangxianze <zhangxianze@wonderable.ai >
Co-committed-by: zhangxianze <zhangxianze@wonderable.ai >
2026-06-10 14:44:28 +08:00
marco
cfc54ac2be
fix(alembic): 合并 invite_fingerprint 与 044 双 head 修复 Multiple heads 部署失败
...
#31 的 invite_fingerprint_table 迁移 down_revision 挂在 0cf18d590b1d,与 #30
的 044dce6e9b1f(ad_reward+withdraw 合并) 形成并列双 head,alembic upgrade head
报 "Multiple head revisions are present",ecs1 部署在迁移步失败未重启。
加纯合并迁移 a8c47fc4dc39 (Revises: invite_fingerprint_table, 044dce6e9b1f)
收敛为单 head,upgrade/downgrade 为 no-op 不改 schema。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-10 02:28:56 +08:00
xiebing
b2f5a53dd8
feat(invite): 被邀请人列表接口 + 指纹归因(任务3) ( #31 )
...
- GET /invitees: 分页 + 名字降级兜底(昵称→微信昵称→脱敏手机号)
- 指纹归因: 落地页采集 + (IP,设备型号)反查撞库 + 时间窗口闸
- test_invite: +5 个列表测试(脱敏/倒序/昵称优先/分页/空), 修指纹测试跨用例串味
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
Reviewed-on: #31
Co-authored-by: xiebing <xiebing@wonderable.ai >
Co-committed-by: xiebing <xiebing@wonderable.ai >
2026-06-09 21:48:48 +08:00
wuqi
a828b51d9f
feat(alembic): 添加合并广告奖励会话与提现安全的迁移脚本 ( #30 )
...
Reviewed-on: #30
Co-authored-by: wuqi <wuqi@wonderable.ai >
Co-committed-by: wuqi <wuqi@wonderable.ai >
2026-06-09 18:10:58 +08:00
marco
25b2b6850b
feat(coupon): 领券弹窗频控加重置端点 + reset_today_engagement ( #29 )
...
- 加 POST /api/v1/coupon/prompt/reset:删该设备今日 engagement → has_engaged_today 变 false、
今天又能弹。开发设置「重置今日领券弹窗状态」按钮调它(客户端改纯后台频控后前台清缓存已失效)
- coupon_state 加 reset_today_engagement(按 device_id + 今天删);should-show 文档改为"纯后台判据"
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
Reviewed-on: #29
2026-06-09 11:18:07 +08:00
ouzhou
d7c29c0883
feat(coin): 金币数值体系二期——看广告按 eCPM 发奖 + ad_session_id 幂等 + 签到膨胀固定金币 ( #28 )
...
- rewards: 激励视频实发改按 calculate_ad_reward_coin(eCPM, 当日第N次) 公式;AD_REWARD_COIN/MAX_AD_REWARD_COIN 降为历史兼容口径;新增 SIGNIN_BOOST_COIN=2000
- 签到膨胀: Day1-13 看完激励视频额外发固定金币、Day14 不允许 (signin.py / signin-boost)
- ad_session_id 幂等: ad_ecpm/ad_reward/ad_feed_reward 记录加 ad_session_id 列 + 唯一索引(新迁移 coin_reward_phase2 / ad_feed_reward_session)
- ad.py + schemas + repositories: ecpm-report / feed-reward / reward-status / test-grant 改造;config_schema 增 signin_boost_coin;admin stats overview 补充
- 同步 docs/api + docs/database + docs/integrations/pangle 及 tests(test_ad_reward / test_welfare)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #28
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-09 01:56:47 +08:00
ouzhou
6432497af1
feat: 后端接入微信提现人工审核与资金安全校验 ( #27 )
...
提现申请改为先扣款并进入待审核,审核通过后才发起微信打款,审核拒绝或微信失败时自动退款。
新增运营后台提现列表的关键词搜索、日期筛选、快捷筛选和排序参数,并支持批量通过、批量拒绝、批量刷新查单。
新增微信支付配置健康检查、现金账本校验、自动对账 worker 和单实例保护。
新增数据库唯一索引,约束同一用户未完成提现和同一提现单重复退款,提升并发安全性。
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #27
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-09 01:56:37 +08:00
zhangxianze
94b7c027be
fix(time): created_at 统一存北京时间,修面向用户展示慢 8h ( #25 )
...
savings/comparison/wallet(金币·现金流水)/report 写入显式 created_at=datetime.now(CN_TZ).replace(tzinfo=None),替代 SQLite 下返回 UTC 的 server_default=func.now()。后台统计 admin/ops 依赖 created_at 是 UTC,不动。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: xianze <ze@192.168 .0.128>
Reviewed-on: #25
Co-authored-by: zhangxianze <zhangxianze@wonderable.ai >
Co-committed-by: zhangxianze <zhangxianze@wonderable.ai >
2026-06-08 16:23:26 +08:00
marco
97f0fb210f
fix(alembic): merge coupon_state + invite 双 head 为单一 head
...
coupon_state_tables 与 invite_code_and_relation 都挂在 11a1d08c6f55 下、是兄弟迁移,
合 main 后形成两个 head,alembic upgrade head 会报 Multiple head revisions 致部署失败。
本 merge 迁移(无 schema 变更)把两条链并起来,恢复单一 head。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-08 05:54:55 +08:00
marco
4d392f44f5
feat(invite): 好友邀请后端(注册即生效,邀请人+被邀请人各发1万金币) ( #24 )
...
- user.invite_code 列 + invite_relation 表(invitee_user_id 唯一 = 幂等防重复发奖)
- GET /api/v1/invite/me(我的码+分享链接+战绩)、POST /api/v1/invite/bind
- 复用 wallet.grant_coins 同事务发币;自邀屏蔽 / 无效码 / 新用户闸(注册72h内才发)
- alembic 迁移(11a1d08c6f55 -> invite_code_and_relation)+ 8 个测试(钱路/幂等/并发兜底全覆盖)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
Reviewed-on: #24
2026-06-08 04:57:55 +08:00
marco
357ba27499
feat(coupon): 领券今日状态后端(弹窗频控 + 领券记录) ( #23 )
...
- 两张表 + Alembic 迁移:coupon_prompt_engagement(device×日,弹窗频控源)/ coupon_claim_record(device×券×日,领券记录·资产)
- coupon/step 透传顺手写库:step0 写 engagement(claim_started);响应解析 last_coupon_result/coupon_results 幂等写 claim record(按 coupon_id 去重,避免 done 帧 last 与 coupon_results 重复在 autoflush=False 下撞唯一约束回滚整批)
- 新增 GET /coupon/prompt/should-show(弹窗判据)+ POST /coupon/prompt/dismiss(拒绝通知,透传链路看不到拒绝)
- 判断按 device_id(券发到设备登录的外卖账号,比 user 更贴近登录环境);user_id 仅资产留痕、可空
- MVP 先不去重(队列照旧全发);pricebot 未改
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
Reviewed-on: #23
2026-06-08 03:20:51 +08:00
marco
ece41086cd
新增美团 CPS 券定时抓取入库(北京试点),作为"销量/佣金排序"的本地数据源 ( #18 )
...
背景:美团搜索/供给对销量排序支持差(实测乱序)、且有 402 限流和单次召回上限,不适合
每次实时打接口排序。改为定时把券抓进本地 meituan_coupon 表,查询时从库里捞、自己按
销量/佣金排。
本次内容:
- app/models/meituan_coupon.py:新表模型。字段尽量全(售价/原价/销量档/佣金率/佣金额/
门店/距离/品牌/图 等),raw 列存整条原始返回避免漏字段;(source, product_view_sign) 唯一。
- alembic/versions/meituan_coupon_table.py:建表迁移(挂在 withdraw_review_ad_watch 之后)。
- scripts/pull_meituan_coupons.py:ETL,抓 3 路并按 (source, product_view_sign) upsert:
1) 外卖·搜"外卖"(platform=1)翻到尽头
2) 外卖·搜"美食"(platform=1)翻到尽头
3) 到店·多业务线供给(到餐+到综+酒店+门票)翻到尽头
支持 --once(单轮,给 cron;线上每 1h)/ --loop --interval(本地循环,默认 10min)
+ 运行锁防重叠 + --prune-hours 清理陈旧券(默认 24h)。
- app/models/__init__.py:注册新模型。
实测:北京一轮约 2.5 分钟、入库约 2700 条;销量/佣金可直接从库里排序。
给接手同事:
- 仅北京(city_id 固定);v2 再加 per商圈 union 突破全城单次召回上限。
- 销量仅粗档位且约 51% 的券才有,查询需 WHERE sale_volume_num IS NOT NULL
(PG 的 DESC 默认把 NULL 排最前);佣金、价格 100% 有值。
- productViewSign/skuViewId 跨渠道会变,不能当全局 id;跨源去重用 dedup_key=md5(品牌|名|价)。
- 待做:查询接口(从库捞 + dedup_key 去重 + 按销量/佣金排,返回前端)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: chenshuobo <1119780489@qq.com >
Reviewed-on: #18
2026-06-08 00:16:25 +08:00
ouzhou
766666601e
feat(platform): 首页门面三统计 + 运营后台展示模式配置 ( #22 )
...
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #22
Reviewed-by: marco <marco@wonderable.ai >
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-07 23:17:27 +08:00
marco
f5d1a1a20d
feat(price-observation): 价格观测资产表 + pricebot 内部上报端点 ( #21 )
...
比价沉淀的价格事实(平台/门店/菜篮/到手价/时间/来源)无条件落库,server 侧存储、
与登录无关,独立于用户视角的 comparison_record。是未来"别人查过同店→秒回价格"
价格大数据的源头(先存数据,用法后续)。
- models/price_observation.py: 新增 price_observation 表。结构化列(平台/门店/价格/
红包/地理/来源)+ dishes/attrs 两个 JSONB 兜底列;(trace_id,platform,scope) 幂等
唯一约束;observed_at/store_name/geohash/source_device_id/source_user_id 等索引
- alembic/versions/price_observation_table.py: 建表迁移,down_revision=wx_transfer_auth
(已核为当前唯一 head,无多 head 风险)
- schemas/ + repositories/price_observation.py: 批量收发模型 + 跨方言(PG/SQLite)幂等
批量插入(先查 trace 已有 (platform,scope) 只插新的,并发撞唯一约束则回滚跳过)
- api/internal/price.py: 新增 POST /internal/price-observation,X-Internal-Secret 头校验
(常量时间比较,未配密钥→503),server→server 专用,不走用户 JWT
- core/config.py + main.py: 新增 INTERNAL_API_SECRET 配置 + 注册内部路由
- .env.example: INTERNAL_API_SECRET(须与 pricebot 侧同值,留空=内部写端点关闭)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: pure <pure@192.168 .0.104>
Reviewed-on: #21
2026-06-07 03:31:35 +08:00
marco
7126fb3ba3
feat(concurrency): trace 一致性 hash 亲和 + 透传 stream 优化 ( #20 )
...
配合 pricebot 单机多进程:透传层按 trace_id 把同一次比价/领券的所有帧
亲和到同一 pricebot 实例,使进程内内存维护 state、无需 Redis。本仓改动:
- 一致性 hash: 新增 app/core/pricebot_router.py(ketama 环,1000 虚拟节点)
+ pick_pricebot(trace_id),md5 纯函数、跨 worker 一致;实测负载偏差 4%、
6→7 扩容只重映射 ~13%(取模会 ~85%)。
- 实例列表: app/core/config.py 加 PRICEBOT_INSTANCES(逗号分隔)+
pricebot_instances property;留空 fallback 单实例,向后兼容。
- 透传亲和 + stream: api/v1/compare.py + coupon.py 改为读原始字节、只
loads 一次拿 trace_id 选实例、转发原始 bytes 不重序列化(省 ~半透传 CPU,
单 worker 即可扛高并发,避开多 worker 破坏短信内存表的坑);覆盖
intent/recognize、intent/step、price/step、coupon/step 四端点。
- .env.example 加 PRICEBOT_INSTANCES + PRICEBOT_COMPARE_TIMEOUT_SEC。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: pure <pure@192.168 .0.104>
Reviewed-on: #20
2026-06-07 02:33:50 +08:00
marco
f098ba3816
日志: app-server 增加 JSON 文件输出供阿里云 SLS 采集 ( #19 )
...
app/core/logging.py:
- 新增 RotatingFileHandler 落 logs/app-server.log (此前仅 stdout), 用 JsonFormatter 输出
单行 JSON (time/level/service/logger/func/line/message, 异常栈内嵌不换行)。
- 控制台保持文本 (stdout 行为不变, systemd/run.log 不受影响)。
- setup_logging 加幂等保护 + 清理预置 root handler 防重复输出。
- 支持环境变量 LOG_FILE/LOG_DIR/LOG_JSON_CONSOLE/LOG_SERVICE_NAME
(admin 子进程应设独立 LOG_FILE 避免与主进程争抢同一轮转文件)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: pure <pure@192.168 .0.104>
Reviewed-on: #19
2026-06-07 02:26:45 +08:00
marco
b4780e256e
docs(database): 补全 19 表文档 + 新增 OVERVIEW 总览
...
- 重写 15 张已有表文档:每张补「用在 App 哪/增删改查时机/字段取值/join key 指向」
- 新建 4 张缺失表文档:wechat_transfer_authorization / ad_watch_log / price_report / app_config
- 新增 OVERVIEW.md:功能↔表映射 + 写入路径 + 表间关系(含语义 join key)+ ER + 资金模型
- README 索引补齐到 19 表 + 置顶 OVERVIEW + 通用约定补全
- 纠正过时:withdraw_order(reviewing/rejected/user_name)、savings_record(/order/report 真实写入)、coin_transaction(biz_type)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-07 00:25:57 +08:00
marco
b59dc3ac19
feat(wallet): 微信商家转账「用户授权免确认收款」(免确认到账) ( #16 )
...
提现从「每次跳微信确认」升级为免确认模式:首次授权一次,之后审核通过直接到账,用户零跳转。
方式一(首单转账顺带授权 pre-transfer-with-authorization)+ 方式二(显式开启 user-confirm-authorization)。
- 新增 wechat_transfer_authorization 表(user 1:1,out_authorization_no/authorization_id/state) + 迁移
- wxpay.py 新增 apply/query/close_transfer_authorization + pre_transfer_with_authorization + transfer_with_authorization
- repositories/wallet.py 授权 CRUD + sync/_refresh_active_auth + execute_withdraw_transfer 三分叉
(active->免确认转账 / 无授权且配了回调->方式一 / 未配->退化原确认模式);失效回查授权权威判定,绝不盲退
- /wallet/transfer-auth(开启)/status/close 路由 + withdraw-info 增 transfer_auth_enabled
- 授权结果回调 stub /api/v1/wxpay/transfer-auth-notify(一期仅应答不验签,真值靠 query 轮询)
- config 增 WXPAY_AUTH_NOTIFY_URL
一期无回调验签;二期补 V3 平台证书验签 + AEAD 解密。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
Reviewed-on: #16
2026-06-06 18:52:42 +08:00
marco
e252277431
feat(tools): 美团 CPS API 调试 playground
...
浏览器填参数 → 本地签名代理打美团 → 卡片列表 + 可折叠 JSON 树。
模板按到店/到家分组 + 渠道标识(榜/搜/供),支持翻页、测试坐标、距离展示。
复用 integrations/meituan.py 的 _sign/_content_md5,AppSecret 只留后端,不碰生产代码。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-06 18:13:07 +08:00
ouzhou
0e42e96ddb
docs(admin): 补充管理后台 API 与数据库文档 ( #12 )
...
新增 admin 模块接口文档(auth login/me、admins CRUD、audit-logs、feedback 处理、stats 概览、user coins/detail/status、wallet 流水、withdraw 对账/刷新/列表) + admin_user/admin_audit_log 两张表文档,更新 api/database README 索引。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #12
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-06 15:58:23 +08:00
marco
07c0b7502c
feat(welfare): 提现人工审核流程 + 看广告每日时长限流(防刷主闸) ( #15 )
...
提现人工审核(提现不再即时打款):
- models/wallet.py: WithdrawOrder 状态机改为
reviewing →(审核通过)→ pending → success/failed;reviewing →(驳回)→ rejected(已退款),
默认态 pending → reviewing(扣现金建单但不打款);新增 user_name 列(实名, 微信达额转账要求)
- admin/routers/withdraw.py + admin/schemas/wallet.py: 运营后台审核接口(通过触发微信转账 / 驳回退款)
- api/v1/wallet.py + repositories/wallet.py: 发起提现进 reviewing 态, 驳回退回现金并写 withdraw_refund
- schemas/welfare.py: 提现出参增 reviewing/rejected 状态 + fail_reason
看广告每日时长限流(防刷主闸 + 次数兜底):
- 新增 models/ad_watch_log.py: 按 (user_id, watch_date) 聚合当日观看秒数
- 新增 repositories/ad_watch.py: watched_seconds_today / add_watch_seconds(夹 [0, MAX_SINGLE_WATCH_SECONDS])
- api/v1/ad.py: 新增 POST /ad/watch-report(JWT 取 user, 落时长返当日累计);
/ad/reward-status 增 watched_seconds_today / limit / remaining
- schemas/ad.py: WatchReportIn/Out
- core/rewards.py: 新增 DAILY_AD_WATCH_SECONDS_LIMIT=50 分钟(主闸)+ MAX_SINGLE_WATCH_SECONDS=120;
DAILY_AD_REWARD_LIMIT 20→200 改作次数兜底(防前端少报时长绕过时长闸, 又不误伤看短广告的正常用户)
- repositories/ad_reward.py + models/__init__.py: 接入新表与时长闸
alembic: withdraw_review_and_ad_watch 迁移(withdraw_order.user_name 列 + ad_watch_log 表)
tests: 补 test_ad_reward / test_withdraw
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: pure <pure@192.168 .0.104>
Reviewed-on: #15
2026-06-06 04:51:50 +08:00
marco
67a9a15775
feat(admin): app_config 运营配置后台(rewards 等常量可后台配置) ( #14 )
...
- 新增 app_config 表 + model/repository/config_schema + admin config 路由与 schema + 测试
- rewards.py 及 ad_reward/signin/task/wallet/comparison_milestone repositories 接入可配置项;ad.py / admin/main.py 配套
- alembic merge(ebb6af5c0b56)合并本地 app_config(cfg1a2b3c4d5)与 incoming price_report/best_deeplink(b7e2c1a9f4d3)两 head
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
Reviewed-on: #14
2026-06-06 04:13:08 +08:00
zhangxianze
4506677483
feat(report): 上报更低价后端——price_report 表 + 提交/列表接口 ( #13 )
...
Co-authored-by: xianze <ze@192.168 .0.138>
Reviewed-on: #13
Co-authored-by: zhangxianze <zhangxianze@wonderable.ai >
Co-committed-by: zhangxianze <zhangxianze@wonderable.ai >
2026-06-06 00:42:39 +08:00
marco
b50495bebe
feat: 短信接入极光真实发送 + 新增运营 admin 后台子应用
...
短信(SMS_MOCK 切 mock/real):
- integrations/sms.py 重写: real 模式走极光短信 REST /v1/messages 自定义验证码(本服务 secrets
生成 6 位码 + 进程内存 + 本地校验一次性/防爆破), 鉴权复用极光一键登录 JG_APP_KEY/MASTER_SECRET
(同一极光应用, 上线只需 SMS_MOCK=false); mock 仍"任意6位通过"不动其余测试
- 防刷四层: 单号冷却 + 单号每日上限 + 单IP rate_limit(/sms/send 10/min、/sms/login 20/min)
+ 单码失败次数作废; SmsError 带 status_code 映射 429/503/400
- config 增 SMS_SEND_ENDPOINT/SIGN_ID/TEMPLATE_ID/CODE_LENGTH/DAILY_LIMIT/MAX_VERIFY_ATTEMPTS;
test_auth 加 real 模式单测; sms.md/后端技术实现/待办账本同步
admin 后台(app/admin/ 独立子应用, uvicorn app.admin.main:admin_app :8771):
- 复用主仓 models/repositories/integrations + 同库, 鉴权完全隔离(ADMIN_JWT_SECRET≠JWT_SECRET_KEY
+ payload typ=admin + bcrypt 密码 + 可选 IP 白名单); 主 app 不 import 本包, admin 崩不影响主进程
- 路由: 登录 / 账号管理(RBAC: super_admin·finance·operator) / 用户列表+360详情+封禁+手动调币 /
钱包流水 / 提现重试对账 / 反馈工单 / 数据大盘; 全写操作落 admin_audit_log(涉钱与业务写同事务)
- 涉钱逻辑(调微信/退款/对账)复用 app.repositories.wallet 不重写
- 新增 models/admin.py(AdminUser/AdminAuditLog) + admin_tables 迁移 + create_admin.py +
deploy/shaguabijia-admin.service; 依赖加 bcrypt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-04 03:02:41 +08:00
ouzhou
90c0d65a16
chore(alembic): merge comparison_milestone 和 savings_report_fields 两个 head ( #11 )
...
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #11
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-03 09:48:28 +08:00
OuYingJun1024
518f8c8b92
Merge branch 'main' of https://gitea.shaguabijia.com/WonderableAI/shaguabijia-app-server
2026-06-02 15:07:23 +08:00
marco
e4588303fb
feat(compare): 加 /api/v1/intent/step 透传到 pricebot(淘宝多帧意图识别)
...
配套 pricebot-backend 的 /api/intent/step(淘宝源 Phase 1 多帧意图识别: 展开 + 滚动
采集 → LLM 提取)。沿用 _passthrough 壳: 不鉴权、原样转发, 客户端循环调用直到 done
(done 帧顶层带 result + calibration)。目前仅淘宝源走这条, 其它源走单次 /intent/recognize。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-02 14:40:19 +08:00
OuYingJun1024
9521cd96ce
chore(alembic): merge comparison_milestone 和 savings_report_fields 两个 head
2026-06-02 09:53:25 +08:00
ouzhou
da7ce69494
feat(ad): eCPM 上报接口 + 冷却策略解耦纯函数 + savings JSONB 跨库修复 ( #8 )
...
- 新增 POST /api/v1/ad/ecpm-report:激励视频展示后客户端上报本次 eCPM,落 ad_ecpm_record 做内部收益统计(model/repository/schema/alembic 迁移 + 接口文档)
- 看广告冷却策略抽到 app/core/ad_cooldown.py 纯函数;ad_reward.today_status 只取数据,换策略只改这一处
- savings.dishes 改 JSON().with_variant(JSONB,"postgresql"):SQLite 无 visit_JSONB 会让 create_all 编译崩,修复后测试套件恢复
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com >
Reviewed-on: #8
Co-authored-by: ouzhou <ouzhou@wonderable.ai >
Co-committed-by: ouzhou <ouzhou@wonderable.ai >
2026-06-01 22:23:15 +08:00
zhangxianze
288766443a
feat(记账): 比价省钱记账后端 M1(savings_record 唯一真相表 + /order/report + demo 兜底) ( #10 )
...
从最新 origin/main 重拉的干净分支,只含本人记账改动,不含 marco 的归因订单(order_record)——后者是另一条未合并分支 feat/user-order-records 的功能,此前被误用 git add -A 和记账混进了 feat/savings-accounting-m1。本分支不带它:savings_record 升级为唯一真相表(新增原价/比价价/支付渠道/源平台/幂等键等列,迁移 savings_report_fields 直接挂 f01db5d77dac、不经 order_record_table);/order/report 改写 savings_record(source='compare'),省额=源平台原价−实付;展示 /api/v1/savings/* 真实优先 demo 兜底。无 order_record 的 model/repo/迁移/路由。alembic 单一 head + pytest 54 passed。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
---------
Co-authored-by: xianze <ze@192.168 .0.138>
Reviewed-on: #10
Co-authored-by: zhangxianze <zhangxianze@wonderable.ai >
Co-committed-by: zhangxianze <zhangxianze@wonderable.ai >
2026-06-01 21:29:52 +08:00
marco
a114a0f3f2
docs(integrations/wxpay): 删 code_to_openid 行,跟齐 9d1278c 死代码清理
...
9d1278c 已删 wxpay.py 的 code_to_openid 函数(能力被 code_to_userinfo 覆盖),
docs 函数表里这一行没跟着删,补齐。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-06-01 20:44:46 +08:00
marco
9d1278cb33
chore: 清理两处未使用代码
...
- app/db/base.py: 删 MappedAsDataclass 的 import (没人继承用)
- app/integrations/wxpay.py: 删 code_to_openid 函数 (code_to_userinfo 已覆盖能力, 18 行死代码)
2026-05-31 17:10:16 +08:00
marco
6c7eaa9734
docs: 跟齐 5 月 PR ( #4 user/feedback / #5 ad-cooldown / #6 PG / abd8eab meituan 降级)
...
整轮 doc 跟齐 2026-05 落地但 doc 没补全/没更新的内容. 按 P0~P3 优先级处理.
P0 - 新 API doc + index 更新:
* docs/api/user-profile.md (新) PATCH /api/v1/user/profile 改昵称
* docs/api/user-avatar.md (新) POST /api/v1/user/avatar 上传头像(魔数嗅探/旧文件清理)
* docs/api/user-delete.md (新) DELETE /api/v1/user 注销账号(软删+匿名化)
* docs/api/feedback.md (新) POST /api/v1/feedback 提交反馈(含截图)
* docs/api/README.md 加 User/Feedback 组 + /media 静态服务段 +
修 UserOut "无接口可改恒为 null" 过时描述
P0 - 美团降级 / 现有 doc 校准:
* docs/api/meituan-coupons.md 加 "未配 MT_CPS 凭证降级返空" 说明
* docs/api/meituan-referral-link.md 同上 + 客户端兜底建议
* docs/api/meituan-feed.md 区分两种空结果路径(未配 vs 调用失败) +
/health.mt_cps_configured 排障入口
* docs/integrations/meituan.md "未配凭证降级" + "feed 静默吞错" 写进集成层
* ad-reward-status.md PR #5 commit 已自带 doc 更新, 复核无需再改
P1 - 主文档大改:
* docs/后端技术实现.md
- §1 能力表加 用户资料/外卖比价/钱包/福利/广告/反馈 (从 3 条扩到 9 条)
- §1 数据模型纠正 "仅 1 张 user 表" -> 现有 10 张 + alembic_version
- §2 DB 行 PG 主 / SQLite 兜底 + 加 psycopg3 行
- §3 目录树补 user.py / feedback.py / media.py / repositories/feedback.py /
scripts 子项 / docs/postgres-migration.md
- §7 数据模型重写: 10 张业务表速查表 + alembic 合并迁移 f01db5d77dac 始末
- §8 部署: 加 init_postgres.py 一句话流程 + checklist 加 "DATABASE_URL 切 PG"
+ "psycopg3 已装" + "MEDIA_ROOT 可写"
- §10 已知问题: 加 美团降级 / agent 不鉴权 / MEDIA_ROOT serve / init_postgres bug /
alembic 多 head 风险; 删过时 SQLite 行
- 接口计数 11 -> 38
* docs/待办与技术债.md
- 已解决追加: PG 切换 / 外卖比价透传 / 用户资料反馈接口 / 美团降级 /
看广告 4 态冷却
- 新增 "P2 DB 与 Alembic 治理" 段: alembic 多 head 团队规约 (含本次撞坑实录) +
init_postgres.py 已知 5 处小 bug + MEDIA_ROOT serve
P2 - PG 迁移文档互指 + 迁移链补完:
* docs/数据库迁移.md
- 顶部加 postgres-migration.md / 后端技术实现.md 互指
- §6 迁移链 9 条 -> 12 条 (含 feedback_table / convert_dishes_jsonb /
merge_pg_jsonb_and_feedback_heads)
- §7 踩坑里 "多 head" 一行扩成完整故事 + 修复命令 + 团队预防规约
* docs/postgres-migration.md
- 顶部加 数据库迁移.md / 后端技术实现.md §10 互指
P3 - 上线清单:
* docs/看广告赚金币上线清单.md
- 设计要点加 PR #5 4 态 CTA + 后端权威派生说明 +
VIDEO_ROUND_REQUIRED_COUNT/VIDEO_ROUND_COOLDOWN_SECONDS 常量
- §E 端到端验收加 本轮冷却 / 弹窗 limit note / 跨设备一致 三项
所有最后更新日期同步到 2026-05-31.
2026-05-31 11:05:10 +08:00
marco
c3c64fa06d
chore: alembic merge revision + PG 迁移指南文档 ( #7 )
...
PostgreSQL 迁移 (#6 ) 之后两个分支引入了独立 heads (ef96beb47b1e +
d1e2f3a4b5c6), 必须合并成单一 head 才能 alembic upgrade head 顺利跑通。
- alembic/versions/f01db5d77dac_merge_pg_jsonb_and_feedback_heads.py:
空 op 的 merge revision, 把两个 heads 收敛为 f01db5d77dac
- docs/postgres-migration.md: SQLite → PostgreSQL 迁移指南
(背景/动机/操作步骤/无真实用户数据假设, 上线前补 pgloader 演练等)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
Reviewed-on: #7
2026-05-30 18:13:11 +08:00
marco
e69e244de7
Merge pull request 'feat(ad-reward): reward-status 加本轮 3 次冷却字段 (round_count + cooldown_until)' ( #5 ) from feat/welfare-video-round-cooldown into main
...
Reviewed-on: #5
2026-05-29 15:45:51 +08:00
marco
2ebde935f9
Merge pull request 'feat: SQLite → PostgreSQL 迁移代码改动' ( #6 ) from feat/postgres-migration-and-bootstrap-script into main
...
Reviewed-on: #6
2026-05-29 15:44:40 +08:00
chenshuobo
b3d3fda744
feat: 新增 PG 初始化脚本 scripts/init_postgres.py
...
新机器一键拉起数据库, 替代手动跑 psql 建用户建库的 6 行命令.
脚本特性:
- 跨平台 Python (Windows/macOS/Linux 通吃, 不依赖 psql CLI 在 PATH 里)
- 直接用 psycopg 连 PG, 不 shell out
- 幂等: 用户已存在则重置密码, 库已存在则跳过, 重复跑不出错
- 业务密码自动 secrets.token_urlsafe(32) 强随机, 不让同事自己想
- 自动从 .env.example 复制出 .env 并写入 DATABASE_URL
- .env 里已有 DATABASE_URL 会询问是否覆盖 (不无脑覆盖)
- 跑完自动 alembic upgrade head, 一条命令到可用状态
- 全交互项支持环境变量提前指定 (PG_SUPER_PASS / APP_DB_PASS 等), CI 友好
同事新机器初始化流程从 6 步简化为 3 步:
pip install -e .
python scripts/init_postgres.py # 唯一手动输入项: postgres 超级用户密码
./run.sh
CREATE/ALTER USER 注意点: PG 不支持参数化密码绑定, 用 sql.Literal 内联,
psycopg 自带的转义会处理引号和注入.
docs/数据库迁移.md 拆出 PG / SQLite 两套初始化流程, PG 走脚本,
SQLite 保留旧步骤 (本地临时回退用).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-29 15:21:04 +08:00
chenshuobo
b76e5bd515
feat: SQLite → PostgreSQL 迁移代码改动
...
按 docs/数据库迁移.md 第 3 节扫雷清单落地代码层改动:
- pyproject.toml: 新增 psycopg[binary]>=3.1 依赖 (psycopg3, SQLAlchemy 2.0 时代默认, 不要再装 psycopg2)
- app/db/session.py: 非 SQLite 自动加连接池参数
- pool_size=10, max_overflow=20, pool_recycle=3600
- SQLite 单文件不池化, _is_sqlite 判断分支保留以便本地 dev 临时回退
- app/models/savings.py: dishes 列从 JSON 改为 JSONB
- PG 上能用 GIN 索引和 jsonb 操作符
- SQLite 上 JSON 实际是 TEXT, 切到 PG 后用 JSON 类型存的是 json 不是 jsonb
- alembic/versions/ef96beb47b1e_*.py: 新增迁移把 savings_record.dishes 从 json 转 jsonb
- 用 op.get_bind().dialect.name 判断 PG 才执行 ALTER COLUMN
- SQLite 上是 no-op (SQLite 没有 jsonb 类型)
- 旧的 savings_shop_dishes.py 迁移保持 sa.JSON() 不动 (按文档"不改老迁移"原则)
本地切 PG 验证: 10 张表全部建出, dishes 字段为 jsonb 类型,
登录/me/美团 feed 三个真接口全 200, user 数据正确写入 PG.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-29 15:20:22 +08:00
OuYingJun1024
56f4548654
feat(ad-reward): reward-status 加本轮 3 次冷却字段 (round_count + cooldown_until)
...
让客户端 4 态任务行 CTA (Normal/Loading/Capped/CoolingDown) 与 ExitReward
弹窗 limit note (本轮看完/今日看完 二选一) 都由后端权威派生, 跨设备 / 重装 /
杀进程都一致, 客户端不再依赖 in-memory 本地存储。
- core/rewards.py: 加 VIDEO_ROUND_REQUIRED_COUNT=3 / VIDEO_ROUND_COOLDOWN_SECONDS=600
- repositories/ad_reward.py: today_status 从 3-tuple 扩成 5-tuple,
从已有 created_at 字段按 ORDER BY DESC + OFFSET round_count 派生最近一个
已完成轮末尾时间; 零新字段零新表
- schemas/ad.py: AdRewardStatusOut + TestGrantOut 加 round_count + cooldown_until
- api/v1/ad.py: reward_status / test_grant 接 5-tuple
- tests: 加 4 个覆盖 (初始 / 未达一轮 / 刚达一轮进冷却 / 冷却已过)
- docs/api/ad-reward-status.md: 补字段说明 + 客户端 4 态 CTA 推导规则 +
"冷却仅 UX, 发奖不受影响" 注解
冷却是 UX 约束: pangle-callback 发奖只看 daily_limit, 冷却期间穿山甲
回调照常发奖, 不另设拒发分支。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-29 13:27:56 +08:00
marco
4d444b7c43
Merge pull request 'feat(user): 用户资料与反馈接口(昵称/头像/注销 + 帮助反馈)' ( #4 ) from feat/user-profile-feedback-api into main
...
Reviewed-on: #4
2026-05-29 01:04:10 +08:00
陈世睿
49379fd045
feat(user): 用户资料与反馈接口(昵称/头像/注销 + 帮助反馈)
...
- /api/v1/user: PATCH 改昵称、POST 上传头像、DELETE 注销账号(软删除+匿名化)
- /api/v1/feedback: 提交反馈(内容/联系方式/截图入库)
- 新增 media 模块(图片落盘+魔数校验)+ /media 静态服务
- feedback 表 + Alembic 迁移(部署需 alembic upgrade head)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-28 22:27:36 +08:00
marco
abd8eabf9b
feat(meituan): 未配置 MT_CPS_APP_KEY 时接口降级返空,不再 502
...
之前未配美团 CPS 凭证时,/coupons 和 /referral-link 调用会抛 MeituanCpsError
被翻成 502,前端首页一进就拉 /feed → 看到错误(虽然 /feed 因 _fetch_topic
吞异常已经返空)。新开发机没填美团 key 时体验差。
本次让美团 3 个接口都在入口处早返空响应:
- Settings 新增 mt_cps_configured property(同 wxpay_configured 套路)
- /coupons / /feed / /referral-link 未配置时分别返空 list / 空 link
- 客户端首页 feed 看到空列表,其他业务(登录/领券/广告/提现)完全不受影响
副作用:/feed 返空现在同时表示"配置缺失"和"美团真无券"两种状态,排查时
看 settings.mt_cps_configured 区分。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-28 15:54:51 +08:00
marco
8d9bd3c72f
Merge pull request 'test(coupon-proxy): 修正已过时的 401 鉴权断言' ( #3 ) from feature/coupon-claim-integration into main
...
Reviewed-on: #3
2026-05-28 10:53:58 +08:00
marco
ba946a9d9a
fix(docs): 移除 3ff95db merge commit 残留的冲突标记
...
3ff95db 那次 merge 时, IDE/editor 已经修改文件但 my Edit 被 file-freshness
guard 拒绝,后续 git add/commit/push 把仍带 <<<<<<< / =======/>>>>>>> 的
coupon-step.md 推上去了 —— 已部署文档破损。
按原合并方案 fix-forward:鉴权行用精确版「Bearer access_token(客户端契约;Server
MVP 阶段不强校验)」,⚠️ 段保留「前端已接通」详述 + 用「客户端契约 vs Server
实现」措辞写鉴权状态 + device_id 用途 + P1 链接。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-28 10:49:01 +08:00
marco
3ff95dba76
Merge origin/feature/coupon-claim-integration: 合并 coupon-step.md 鉴权措辞两边
...
冲突:本地 36f6465(MVP 不鉴权 + 前端已接通详述 + P1 指向)与远程 3d1bb78
(鉴权措辞精确化:Android 已带 Bearer 客户端契约 / Server MVP 不强校验)。
解法:综合两边——
- 鉴权行用远程精确版「Bearer access_token(客户端契约;Server MVP 阶段不强校验)」
- 说明段保留本地「前端已接通」路径详述(远程版没有)+ 用远程的「客户端契约 vs
Server 实现」措辞重写 ⚠️ 段 + 本地的 device_id 用途说明 + P1 链接。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-28 10:48:05 +08:00
no_gen_mu
3d1bb78afe
docs(api/coupon-step): 鉴权说法精确化 — Android 已带 Bearer / Server MVP 不强校验
...
P1 接通真机验证时 logcat 实证 Android 客户端通过 AuthInterceptor 自动带
Authorization: Bearer <access_token> 头(见 shaguabijia-app-android 仓
data/net/ApiClient.kt:36)。原 docs 只写「鉴权:Bearer access_token」描述
契约目标,但没说明 server 端 MVP 不强校验的实现现状,容易让对接方误以为
缺 token 就 401。
L3 + L18 修订后清晰描述「客户端契约 vs Server 端 MVP 实现」两端联合状态,
不动 server 代码(coupon.py 仍 MVP 不接 Depends, P1 后启用)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-28 10:44:19 +08:00
marco
36f646597d
docs: 同步领券去鉴权 + 前端已接通(收尾 stash 的预 WIP + merge 冲突解决)
...
应用本次会话开始时 stash 的两份文档 WIP(主题一致:领券去鉴权后的文档同步):
- docs/api/coupon-step.md: 鉴权 Bearer → 无(MVP);入参说明加 device_id;说明段去
"鉴权壳"措辞,加"前端已接通"链路 + MVP 不鉴权数据债说明(指向 P1)。
- docs/后端技术实现.md: 行 18 表格 / section 6 段 / 已知问题表三处 stash pop
已干净 apply;一句话总结(行 20)"鉴权 + 透传壳"→"透传壳"对齐;api/v1/ 文件树
冲突 — 采纳同事 8 行 router 清单(wallet/signin/tasks/savings/ad 新加),给
coupon.py 行末尾加"MVP 不鉴权",并补 compare.py 一行(本分支刚加的外卖比价
透传,同事 commit 那时还没有,补上保持文档跟代码一致)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-28 10:43:53 +08:00
marco
16b78478c0
Merge remote-tracking branch 'origin/main' into feature/coupon-claim-integration
...
# Conflicts:
# docs/api/README.md
2026-05-28 10:33:35 +08:00
marco
aa34ad7d0b
feat(compare): 外卖比价 2 端点透传到 pricebot-backend (food MVP)
...
仿 coupon_step 在 app/api/v1/compare.py 加纯 body 透传 2 端点(MVP 不鉴权,只读
device_id/trace_id/step 打日志,不做 schema 校验)。client /api/v1/{intent/recognize,
price/step} → backend 去掉 /v1 转发到 PRICEBOT_BASE_URL,接通外卖比价
client→server→pricebot-backend 链路。
- compare.py: _passthrough helper + intent/recognize + price/step
- config.py: PRICEBOT_COMPARE_TIMEOUT_SEC=60(意图识别大上下文 LLM、price/step
每帧 LLM,比领券 30s 长;对齐客户端 agent ApiClient 的 60s 读超时)
- main.py: 注册 compare_router
- tests/test_compare_proxy.py: 2 端点参数化测试(透传/5xx→502/不可达→502/坏 JSON→400)
- docs/api/{compare-intent-recognize,compare-price-step}.md + 更新 README 索引
- 待办与技术债.md P2 外卖 2 端点标 ✅ (2026-05-27);电商 2 个待接(compare.py
加两行即可)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-28 10:29:20 +08:00
marco
24faaf9d47
test(coupon-proxy): 修正已过时的 401 鉴权断言
...
coupon/step MVP 阶段已去鉴权(见「待办与技术债.md」已解决),但
test_coupon_step_requires_auth 仍断言"无 token → 401",baseline 实际走到上游
拿 502,这条用例长期红灯。改为 mock httpx 验证不带 token 也能正常透传,与新加的
compare 端点 no-auth 测试一致。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-28 10:29:20 +08:00
marco
6e99287c77
Merge pull request 'feat: 福利钱包+签到+省钱战绩+激励广告发奖+微信提现 后端' ( #2 ) from feature/welfare-withdraw-ad into main
...
Reviewed-on: #2
2026-05-27 23:04:22 +08:00