8.3 KiB
todos:
- id: "parse-fbc-fbclid" content: "在 insertADTag 回退路径实现 fbc→fbclid 解析与字段映射(ad_fbp/ad_ua/ad_ip)" status: pending
- id: "redis-pop-latest" content: "实现 resolveLatestDotPayloadBySource:zPopMax user:dot:set:{source} + unserialize 校验" status: pending
- id: "imei-only-guard" content: "回退逻辑仅 IMEI 新注册触发(覆写 ImeiRegisterService::insertADTag 或在 Abstract 中加平台/类型判断)" status: pending
- id: "redis-cleanup" content: "可选:SiteController 或 pop 侧增加 ZSet 过期/长度清理" status: pending
- id: "verify-fbdot" content: "联调:落地页上报 → IMEI 注册 → 检查 user tag 与 FbDot 注册打点" status: pending isProject: false
IMEI 注册 FB 像素归因补写方案评估
结论
方案可行,且与现有设计意图一致:slot_user/app/api/controller/SiteController.php 负责落地页采集,slot_user/app/api/controller/UserController.php 里已有被注释的 zPopMax 回退逻辑,slot_user/app/service/register/ImeiRegisterService.php 在新用户路径调用 insertADTag。
你已确认范围:仅 IMEI 新用户注册(现有 insertADTag 调用点),不覆盖老用户登录补写。
现有数据流
sequenceDiagram
participant Landing as LandingPage_FB_IAB
participant Site as SiteController_fb
participant Redis as Redis_ZSet
participant App as iOS_App
participant Reg as ImeiRegisterService
participant Tag as insertADTag
participant Dot as slot_console_FbDot
Landing->>Site: POST fbp,fbc,ua,source
Site->>Redis: zAdd user:dot:set:{source}
App->>Reg: IMEI register (无 fb 参数)
Reg->>Tag: insertADTag(uid)
Tag->>Redis: 若无 fbclid 则取最新
Tag->>Tag: UserInfoCache 写入 ad_fbc/ad_fbp/ad_ua/ad_ip
Dot->>Tag: 读 ad_fbc 作为 fbclid 打 CompleteRegistration
| 环节 | 现状 |
|---|---|
| 落地页采集 | SiteController::fb 校验 FB_IAB UA,写入 user:dot:set:{source},member 为 serialize(fbp,fbc,ua,ip,source),score 为 microtime |
| IMEI 注册 | ImeiRegisterService::run() 新用户分支 L115 调用 insertADTag |
| 写入用户标签 | insertADTag 当前固定用 $this->fbclid 写 ad_fbc,iOS 客户端通常为空 |
| FB 正式打点 | FbDot 读 ad_fbc,FbService::generateFBC() 用 fbclid 生成 fbc/fbp |
方案为何可行
- 基础设施已就绪:Redis key
USER_DOT_SET_KEY、落地页写入、注册侧回退思路(UserController 注释代码)均已存在。 - iOS 场景匹配:iOS 包拿不到 WebView cookie,但 FB 广告落地页在
FB_IAB内可拿到fbp/fbc,先上报再装 App 是合理补偿路径。 - 与 PWA 不冲突:PWA 仍优先用请求里的
fbclid;仅当$this->fbclid(及关联 fb 字段)为空时才回退 Redis。 - 打点链路可通:
FbDot只要求ad_fbc非空;存入正确 fbclid 后现有FbService可继续生成 CAPI 所需 fbc/fbp。
必须处理的 4 个技术点
1. 字段映射(最关键)
当前 insertADTag 写:
protected function insertADTag(int $uid)
{
// ...
$userInfo->setInfo(['ad_fbc' => $this->fbclid, 'ad_fbp' => $this->fbp, ...], '');
}
Redis 里存的是落地页 fbc/fbp,不是 fbclid。而 FbService 期望 ad_fbc 存 原始 fbclid(见测试数据与 generateFBC() 实现)。
实现要求:从 Redis 取出 fbc 后,需解析出 fbclid:
fbc格式:fb.{subdomainIndex}.{creationTime}.{fbclid}- 取最后一个
.之后的片段作为 fbclid 写入ad_fbc ad_fbp、ad_ua、ad_ip直接使用 Redis 中的值(比服务端随机生成 fbp 更准确)- 若
fbc为空(SiteController 允许),则无法补 attribution,应跳过写入并打 warn 日志
2. 取「最新」的方式
- 使用
ZPOPMAX user:dot:set:{source} 1(与注释代码一致),消费一条,避免同一条数据被下一个用户重复使用。 - 若
insertADTag后续setInfo失败,可考虑不 pop 或失败写回(当前insertADTag有 try/catch,失败只记日志;pop 后丢失风险可接受,或改为「先 ZREVRANGE 再 ZREM 成功后再写」)。
3. 同渠道并发碰撞(已知局限)
按 source 取全局最新一条,无法 100% 保证「这条 fbc 就是当前这台设备」。同渠道短时间多用户注册可能串 attribution。
可接受的 MVP:与当初 UserController 注释方案一致。若要提升准确率,后续可加:
- 时间窗口(如 30 分钟内)
- IP 辅助匹配(落地页 IP vs 注册 IP,不完全可靠)
- 落地页生成
dot_token透传到 App(需客户端配合,改动更大)
4. Redis 集合维护
当前 zAdd 无 TTL、无上限,长期会膨胀。建议实现时顺带:
- 写入时
ZREMRANGEBYSCORE清理超过 N 天(如 7 天)的旧 member - 或按 source 限制 ZSet 最大长度
推荐改动位置(实现阶段)
不在 UserController::register 做(你已选 register_only + insertADTag 内聚),建议:
主改文件:slot_user/app/service/register/AbstractRegisterService.php
- 新增私有方法,例如
resolveLatestDotPayloadBySource(string $source): ?arrayRedis::zPopMax(RedisKeyManagerService::getDotSetKey($source), 1)unserialize+ 校验必需字段(至少fbp非空;fbc非空才能解析 fbclid)
- 改造
insertADTag:- 若
$this->fbclid为空,调用上述方法补全fbclid/fbp/ua/ip - 若补全后仍无 fbclid,
return(不写空 tag) - 否则按现有逻辑
setInfo
- 若
可选:若希望仅 IMEI 走回退、不影响 Name 注册,可把回退逻辑放在 ImeiRegisterService 覆写 insertADTag,而非改 Abstract 基类。
前置条件(产品/客户端)
方案生效依赖用户路径:
- 用户从 FB 广告进入落地页(UA 含
FB_IAB) - 落地页成功调用
SiteController::fb(带正确source) - 用户在合理时间窗口内完成 iOS 首次 IMEI 注册,且
source与落地页一致
若用户跳过落地页直接装包注册,仍无法归因——这是架构固有限制,不是实现 bug。
风险清单
| 风险 | 级别 | 说明 |
|---|---|---|
| fbc 为空 | 中 | 落地页允许空 fbc,补写无效,FbDot 仍会报 fbclid 为空 |
| 同 source 串号 | 中 | 多用户并发时可能拿错上一条 fbc |
| pop 后写失败 | 低 | 数据丢失,仅影响单次 attribution |
| ad_fbp 未被 FbDot 直接使用 | 低 | 当前 FbDot 用 fbclid 重新 generate fbp;落地页 fbp 写入 user tag 但 CAPI 仍用生成值。MVP 可接受;若要最佳 attribution 需后续改 FbDot 优先用 ad_fbp/真实 fbc |
验证建议
- 落地页 mock:POST
SiteController::fb,确认 Redisuser:dot:set:{source}有 member - IMEI 新用户注册(不带 fb 参数),查
UserInfoCache/ user tag 中ad_fbc、ad_fbp、ad_ua - 触发
slot_console注册打点,确认FbDot::registerEvent不再报 fbclid 为空 - 并发两条落地页 + 两次注册,观察是否出现串号(评估是否需加时间窗)
总结
可行,且是 iOS 上架场景下合理的补偿方案。 实现重点不是「能不能取 Redis」,而是:
- 正确从
fbc解析 fbclid 再写入ad_fbc - 仅在客户端无 fb 参数时回退
- 用
zPopMax消费最新一条 - 接受同渠道并发下的 attribution 碰撞,或后续迭代匹配策略
按你确认的范围,只改 IMEI 新注册 insertADTag 即可,无需动登录链路。