498da05995
(工作区既有改动, 非本次会话产出)
4.3 KiB
4.3 KiB
cps_click — CPS 短链点击事件(/c/{code} 每次访问/复制记一条)
模型
app/models/cps_link.py(CpsClick,与CpsLink同文件) · 仓库app/repositories/cps_link.py(record_click/click_stats_by_group) · 接口 用户GET /c/{code}(记visit)、POST /c/{code}/copy(记copy,app/api/v1/cps_redirect.py);统计 adminGET /admin/api/cps/stats· ← 索引 · 总览
群发短链 cps_link 每被点一次就在此记一条。在 CPS 群发漏斗里它是**"用户点了"这一步**:用户点 /c/{code} 进落地/被跳转记 visit,淘宝落地页点"复制口令"记 copy。据此统计点击 PV/UV(按群/活动/时段),配合 cps_order 的下单/佣金构成"点击→下单→佣金"漏斗。group_id/sid 在本表冗余一份(来自 link),统计直接按群聚合、不必 join cps_link。
用在哪 / 增删改查
- C(插入):
record_click(app/repositories/cps_link.py),由cps_redirect两处触发——GET /c/{code}(cps_landing)记event_type=visit;POST /c/{code}/copy(cps_copy,淘宝落地页点复制按钮 fetch 回调)记event_type=copy。写入时把 link 的group_id/sid冗余进来,ua截断到 500。记点击失败被_record吞掉绝不阻断用户跳转/落地(公网无鉴权,任何人点都要能跳)。无幂等——每次点都写新行(PV 即行数)。 - U / D:无。点击是只追加的事件流。
- R(读):
click_stats_by_group(GET /admin/api/cps/stats经group_stats调用)按group_id聚合 →{pv, uv, copy_pv, copy_uv};UV 按(ip, ua)近似去重,visit计 pv/uv、copy计 copy_pv/copy_uv。聚合在 Python 侧做(量级小、跨 PG/SQLite 无方言坑)。
字段
| 列 | 类型 | 约束 / 默认 | 说明(取值 / join) |
|---|---|---|---|
id |
Integer | PK, autoincrement | |
link_id |
Integer | index, NOT NULL | 来源短链 = cps_link.id(语义关联,非 FK) |
group_id |
Integer | index, NOT NULL | 冗余自 link.group_id;按群聚合点击快,免 join cps_link |
sid |
String(64) | index, nullable | 冗余自 link.sid;美团有、淘宝/京东无 |
event_type |
String(16) | NOT NULL, default/server_default visit |
visit(进落地页/被 302 跳) / copy(淘宝落地页点了"复制口令") |
ip |
String(64) | nullable | 客户端 IP(取 X-Forwarded-For 首段,否则 request.client.host),UV 去重键之一 |
ua |
String(512) | nullable | User-Agent,入库截断到 500;UV 去重键之一 |
clicked_at |
DateTime(tz) | server_default now(), index, NOT NULL | 点击时间,统计按时间窗过滤 |
关系 / Join Key
link_id→cps_link.id(多对一,语义关联,无 ForeignKey)。group_id→cps_group.id、sid≈cps_group.sid——均冗余字段,直接按群聚合用,非 FK。- 与
cps_order无直接关联:点击与订单在group_stats里各按group_id/sid聚合后并到同一群行,共同组成漏斗,但两表之间无 join key(无法把某次点击对到某笔订单——CPS 联盟不回传点击 id)。
索引与约束
- PK
id;indexlink_id、group_id、sid、clicked_at。 - 无唯一约束、无外键(纯事件流,PV 即行数,允许重复)。
注意
event_type是接淘宝时新增的:初版(277f9b1)只有 visit(点了就 302 跳美团,记一条即可);3a40f61接淘宝后,淘宝落地页是 H5、用户要点按钮"复制口令"才算转化,故迁移cps_v2_platforms加event_type(server_defaultvisit回填存量),copy单独统计成copy_pv/copy_uv。同迁移把sid改 nullable(淘宝/京东点击无 sid)。group_id/sid冗余的理由:统计是高频读路径,直接WHERE group_id=?聚合,省掉cps_click → cps_link的 join;代价是写入时多带两列(record_click从 link 拷)。这与 model docstring 明说的"统计直接按群聚合、不必 join cps_link"一致。- UV 是近似值:按
(ip, ua)去重,无 Cookie/设备指纹;同一人换网络/浏览器会重复计 UV。够运营看趋势,不作精确口径。 - 记点击绝不阻断:
_record用try/except: pass包住,落地/跳转优先级高于统计——丢几条点击可接受,挡住用户领券不可接受。