- 新增 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>
13 KiB
待办与技术债账本
用途:记"现在先简化、以后要补"的事,和暂不处理的技术债,免得忘。每条标优先级 + 触发背景。做掉的移到末尾「已解决」。 范围:跨栈账本——本仓
shaguabijia-app-server(转发层) +shaguabijia-app-android(无障碍引擎移植) +pricebot-backend(agent 上游,不改)。 大背景:把"后端当大脑、无障碍服务遥控前端操作其它 App"的比价/领券引擎,从 demo apppricebot移植进正式版傻瓜比价。引擎移植采用「代码全搬、独立子包…shaguabijia.agent.*、验证分步(先领券后比价)」。 最后更新:2026-05-31
P1 · 鉴权与用户绑定(最该补)
- 现状:agent 系列接口 MVP 阶段全部不鉴权。领券
coupon/step已落地不鉴权(✅ 已去掉CurrentUser依赖);比价 4 个(intent/recognize、price/step、ecom/intent/recognize、ecom/step)端点尚未建(见 P2),建时同样先不加鉴权。 - 代价(为什么记这笔账):不验 JWT → 转发时 server 拿不到
user_id→ agent 行为只能绑到device_id(设备级),采集不到"哪个用户领了/买了什么"的用户级画像。而精准人群画像、私域分群运营是商业模式的核心资产,靠的就是这份用户级行为数据。device_id仍照传(后端按设备串领券队列够用)。 - 待补:① agent 接口加 JWT 鉴权;② 建立
device_id ↔ user_id绑定(登录后上报一次即可);③领券/比价记录按 user 维度落库→ 比价记录已落地(见文末「已解决」):走独立的鉴权端点POST /api/v1/compare/record,客户端在 done 帧后用带 JWT 的通道上报、不依赖透传链路。所以透传端点本身仍不鉴权、本条 P1 主诉求(给透传链路补 JWT + device↔user 绑定)未解;领券记录尚未落库。 - 连带:补鉴权后,客户端引擎
ApiClient要接回 JWT(复用 app 现有AuthInterceptor思路:注入带鉴权头的 OkHttpClient)。比价记录上报已先行复用这套(authedClient挂AuthInterceptor+RefreshAuthenticator),可作透传链路补鉴权的参考样板。
P2 · 引擎移植 · 后端转发层(本仓)
- 比价透传端点:仿
coupon_step纯 body 透传、不加 Pydantic schema(协议归 pricebot 校验,否则它一改字段本仓就得跟着改,白白耦合)。client/api/v1/xxx→ 转发 backend/api/xxx(去掉 v1,同coupon_step)。- ✅ 外卖 2 个已落地(food MVP,2026-05-27):
intent/recognize+price/step,在app/api/v1/compare.py(不鉴权、超时 60s),测试tests/test_compare_proxy.py,文档docs/api/compare-*.md。 - 电商 2 个待接:
ecom/intent/recognize+ecom/step——放开电商 scene 时在compare.py加两行即可。
- ✅ 外卖 2 个已落地(food MVP,2026-05-27):
- ✅ 转发超时已分离(2026-05-27):比价用新增的
PRICEBOT_COMPARE_TIMEOUT_SEC=60(意图识别大上下文 LLM、price/step 每帧 LLM),与领券的PRICEBOT_REQUEST_TIMEOUT_SEC=30区分;真实耗时待真机实测再定终值。 - 生产 server 与 backend 同内网:
PRICEBOT_BASE_URL部署时设内网地址。比价是 30~80 步循环,每步多一跳,走公网延迟会累积。 - nginx body 上限:比价 debug 包带
screenshot(webp base64,几百 KB)。若 debug 联调走线上域名经 nginx,注意client_max_body_size(release 不带截图、debug 直连本地都无此问题,仅"debug 走域名"才撞)。
P2 · 引擎移植 · 客户端(android 仓)
领券链路相关已完成(详见文末「已解决」):无障碍身份改名、BuildConfig 包名、领券入口、SettingsScreen 权限卡、ApiClient 改址。剩余:
- ✅ 比价悬浮球(外卖)已接(2026-05-27):
SUPPORTED_BIZ_PACKAGES收窄到 3 个外卖源(美团/淘宝闪购/京东外卖);scene 写死 food(getSceneMode默认改「外卖比价」)。点球 →startTask→ Phase1/2 → 比价结果悬浮窗,这条链客户端本就齐(随引擎移植搬入),本次只收窄入口 + 写死场景 + 接通 server。电商放开时再加回 PDD/抖音 + scene 选择。 - 仿腾讯权限引导:搬来的
AccessibilityGuideActivity/PermissionGuide*/GuideOverlayService尚未接入口;当前是"跳系统设置页"最简版(SettingsScreen 权限卡 + 领券前检查已用)。体验后续可升级成仿腾讯浮层。 - 类名可选改名:
PriceBotService等用户不可见,保留无妨;想整洁可改ShaguaAccessibilityService(牵动引用,不急)。
技术债(暂不处理,知情即可)
- agent 接口公网裸奔:不鉴权 = 任何人可调,烧 pricebot 的 LLM 算力。与美团 3 接口现状同级,记账后随 P1 一起补。
- 既有债(已记录在别处):美团
referral-link的sid客户端可覆盖刷分佣;logout占位不吊销 token;SMS_MOCK=true全站免密登录;JWT_SECRET_KEY默认值可伪造 token。详见 后端技术实现.md 与 api/。
P2 · DB 与 Alembic 治理(2026-05-29 新增)
Alembic 多 head 风险 — 团队流程问题
触发背景:2026-05-29 用 PG 跑 alembic upgrade head 报 "Multiple head revisions are present"。
- 实习生的
feat/postgres-migration-and-bootstrap-script分支基于4d444b7(已合 PR #4 feedback) 创建,但新迁移ef96beb47b1e的down_revision挂在c8d9e0f1a2b3(ad_reward),跟 PR #4 引入的d1e2f3a4b5c6(feedback) 并行成两个 head - 根因:跨分支并行开发,PR 合并时没人检查 alembic 迁移链是否线性
临时修复:alembic merge -m "merge pg_jsonb and feedback heads" ef96beb47b1e d1e2f3a4b5c6 生成合并迁移 f01db5d77dac_merge_pg_jsonb_and_feedback_heads.py(已合进 main)。
团队规约(待写进 CONTRIBUTING):
- 新建包含 alembic 迁移的 PR 前,在分支上跑
alembic heads,确认只有一个 head;有多个就先 rebase 自己的迁移文件的down_revision - review 别人含 alembic 迁移的 PR 时,同样检查 heads 数
- 合并 main 之后再跑一次
alembic heads兜底——本次撞到就是 review 没拦住
init_postgres.py 已知小 bug(2026-05-29 review)
scripts/init_postgres.py 是实习生为 PG 切换写的 bootstrap 脚本(建用户/库/写 .env/跑迁移),有 5 处可改进项:
if val:把空字符串当未设 →PG_SUPER_PASS=""不能传(本机 trust 认证想跳过密码就只能给占位字符串)- 用户自定义
APP_DB_PASS含@:/会破坏 URL → 应urllib.parse.quote(db_pass, safe='')(自动生成的secrets.token_urlsafe是 URL 安全字符,但用户传值不一定) - subprocess 跑 alembic 没显式注
DATABASE_URL→ 父 shell 残留旧值会覆盖刚写入 .env 的新值,可能把表建到错的库 - 强制 TCP localhost → macOS Homebrew(默认 trust)和 Linux apt(默认 peer)的默认认证模式都不直接走 TCP,新机首次跑大概率失败
- 承诺的 JSONB GIN 索引没真加 → commit message 和文档都说"PG 上可建 GIN 索引",但
savings_record.dishes实际没加;要么补CREATE INDEX ... USING GIN (dishes),要么把"GIN"措辞从理由里删掉
短期不阻塞使用(熟手按预期参数能跑通),但累积起来会让新人踩坑。低优先级清理。
MEDIA_ROOT 进程内 serve
/media 路径当前由 FastAPI StaticFiles 挂载 serve(头像/反馈截图)。生产建议改 nginx 直 serve MEDIA_ROOT 目录,绕过应用进程减少压力。一行 location /media/ { alias /opt/shaguabijia-app-server/data/media/; } 即可。
阶段 1 领券落地后的遗留(待清理,不阻塞跑通)
- FloatingButton 死代码:Running 面板已换成 ComposeView 承载
CouponProgressPanel(=复用 AgentFloat)。旧原生面板字段(line1~4View/elapsedView/progressBarView/historyContainerView等)+updateStatus(StepStatus)里保留的那段更新逻辑 +appendActionLine/markCurrentActionDone/markPlatformDone/addCancelButton全部 no-op,待删。 - ComparingScreen / CompResultScreen 弃用:领券改走悬浮窗,不再走全屏假动画。
Routes.COMPARING/COMP_RESULT成死路由,连同两个 Screen 文件待删(CouponPromptDialog已抽到ui/coupon/,删 ComparingScreen 不影响它)。 - ApiClient 常量 unused:
getBackendUrl()改用BuildConfig.BASE_URL后,DEFAULT_BACKEND_URL/KEY_BACKEND_URL常量 unused,待清。 - 领券大数字语义:悬浮窗左侧"已领券"大数字目前接
progress.current(正在领第几张),非"已成功数"。中间帧后端不回传已领明细,要精确需后端在 status.progress 加completed[](协议文档第四节已留扩展点)。 - 正式版 versionCode 必须 > 占坑版线上最高:占坑版(
shaguabijia-android)与正式版同包名com.jishisongfu.shaguabijia原地覆盖。正式版当前versionCode=1,真机占坑版是 5 → 同签名降级被 Android 拒装,正式版无法覆盖更新线上占坑版用户。上线前 versionCode 要提到 > 线上占坑版最高值。
已解决
- ✅ 比价记录落库(server + client,2026-05-31):每次比价 done 后客户端上报、按 user 落库,作「我的比价记录」数据源 + 用户级画像沉淀。
- server(本仓):新表
comparison_record(独立于savings_record;结构化列 +items/comparison_results/skipped_dish_names/raw_payload四个 JSON(B) 列)+ 3 个鉴权端点POST /api/v1/compare/record((user_id,trace_id)幂等 upsert,best/saved/is_source_best/status 服务端从 comparison_results 派生)/GET /api/v1/compare/records(游标分页)/GET /records/{id}(含 raw_payload);models/comparison.py+repositories/comparison.py+schemas/compare_record.py+api/v1/compare_record.py+ 迁移comparison_record_table(headb2c3d4e5f6a7)+tests/test_compare_record.py(8 例全过)+docs/api/compare-record-*.md。 - client(android 仓):
PriceBotService.runTask()比价 done 后(lastDoneParams!=null,成功/引擎失败都报)用独立 IO 协程尽力上报;Protocol.CompareRecordRequest.fromComparison()从 calibration+done.params 零翻译组装;ApiClient.reportCompareRecord()走新建的authedClient(复用AuthInterceptor+RefreshAuthenticator,自动 Bearer+401 刷新)。领券不报(非价格对比,本期范围只外卖)。 - 未做:UI(「我的比价记录」页)等原型;真机端到端待验。
- server(本仓):新表
- ✅ 领券链路接通(阶段 1):首页「去领取」→
CouponPromptDialog→ 权限检查 →startCouponClaim→ 循环/api/v1/coupon/step;Running 悬浮窗换皮(ComposeView 承载CouponProgressPanel/AgentFloat,OverlayLifecycleOwner撑 Compose)。 - ✅ coupon/step 去鉴权:去掉
CurrentUser,MVP 不鉴权(device_id 透传)。 - ✅ ApiClient 改址:
BuildConfig.BASE_URL+/api/v1/前缀。 - ✅ SettingsScreen 权限卡:5 项(无障碍/悬浮窗/录屏<API30/电池/自启动)真实检测 + 点击跳转 + 自启动无法检测态 + onResume 自动刷新 + 刷新按钮;逻辑抽到
PermissionHelper(HomeScreen 领券前检查复用)。 - ✅ 无障碍身份统一傻瓜比价:服务名/图标继承 app;
accessibility_service_description/前台通知/GuideCopy/引导 Activity 里的 "PriceBot" 全改「傻瓜比价」;app 图标换成完整logo1(adaptive 居中+黄边,不裁切)。 - ✅ GuideOverlayService 组件名 bug:批量 sed 改包名误把无障碍服务 ComponentName 的 packageName 改成
…shaguabijia.agent(应是 applicationId…shaguabijia)→ 无障碍开启检测永远 false;改用PermissionHelper.isAccessibilityEnabled动态构造。 - ✅ BuildConfig 包名:
com.pricebot.app.BuildConfigimport 改本 app 包名。 - ✅ SQLite → PostgreSQL 切换(2026-05-29,PR #6 + alembic 合并迁移):新增
scripts/init_postgres.py一键脚本、psycopg[binary]>=3.1依赖、savings_record.dishes列改 JSONB、连接池参数(pool_size=10/overflow=20/recycle=3600)。SQLite 仍可作开发兜底(只改DATABASE_URL)。详见 postgres-migration.md。 - ✅ 外卖比价透传 2 端点(2026-05-27,
feat(compare)):/api/v1/intent/recognize+/api/v1/price/step在compare.py,纯 body 透传到 pricebot-backend,PRICEBOT_COMPARE_TIMEOUT_SEC=60独立超时。电商 2 个待接(compare.py加两行即可)。 - ✅ 用户资料 + 帮助反馈接口(2026-05-28,PR #4):
PATCH /api/v1/user/profile改昵称、POST /api/v1/user/avatar上传头像(魔数嗅探)、DELETE /api/v1/user注销账号(软删+匿名化)、POST /api/v1/feedback提交反馈(含截图);新建feedback表 +core/media.py+/media静态服务。 - ✅ 美团 3 端点未配凭证降级(2026-05-28,
feat(meituan)):未配MT_CPS_APP_KEY时coupons/feed/referral-link直接返空(不再 502),解决新开发机首屏炸的问题。坏副作用:/feed跟"已配但调用失败"路径在响应上无法区分(都是 200 + 空 items),靠/health的mt_cps_configured字段区分。 - ✅ 看广告 4 态 CTA + 本轮冷却(2026-05-29,PR #5):
reward-status加round_count+cooldown_until字段,客户端 CTA 4 态(Normal/Loading/Capped/CoolingDown)+ 弹窗 limit note("本轮看完"/"今日看完")全由后端权威派生,跨设备/重装/杀进程都一致。冷却仅 UX,发奖不受影响。