Parcourir la source

feat: V563 实验补丁修 risk uid "null" 错杀 + funnel log 加 uid 字段

bug 链路:
RickUserCacheJob L88 用 String.valueOf(record.getBigint(0)) 把 ODPS BIGINT
NULL 转字符串 "null" 写入 Redis Set risk:user:uid (现有 22 行 NULL 数据).
client 传字符串 "null" uid 的 guest user 全部错杀. 1 小时 80 万请求里
~6.6 万 mid 命中, 真实有效 distinct uid 命中只 ~600. SISMEMBER risk:user:uid
"null" 返回 1 已验证.

修复 (RecommendService.processRiskUser, 3 行 if 补丁):
  if (hitRiskUidCache && "null".equals(param.getUid()) && isHitV563Exp(...)) {
      hitRiskUidCache = false;
  }
- 精准修 bug: 只针对 V563 + uid="null" 的请求, 真黑名单 uid (12008828 等)
  不受影响, 风控功能保留.
- 新增 helper isHitV563Exp + 常量 V563_EXP_CODE, 走 judgeHitAlgoExp 同时
  覆盖 abExpCodes 通道和 rootSessionId 尾号通道.

观测 (funnel log 加 uid 字段, 3 处各 1 行):
- FunnelContext / RecommendService.buildFunnelContext / FunnelAggregator
- 上线后 SLS 可按 uid 维度直接查 "null" 命中占比, 不用再 JOIN
  video_action_log 反查 (之前 JOIN 覆盖率 < 1%, 无法回答 distinct uid 数).

AB 对照: 非 V563 用户继续走 bug 路径 (控制组), V563 用户走修复路径
(实验组). BI 对比两组 CTR/完播率即修 bug 的真实业务价值.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
yangxiaohui il y a 1 semaine
Parent
commit
c72e42a977

+ 21 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/RecommendService.java

@@ -435,6 +435,12 @@ public class RecommendService {
             boolean isNoneUserRisk = isNoneUserRisk(request, param);
             boolean hitRiskScene = riskScenes.contains(request.getHotSceneType());
             boolean hitRiskUidCache = riskUserCache.getUnchecked(RedisKeyConstants.Recommend.riskUserUid).contains(param.getUid());
+            // V563 实验补丁: 命中 V563 时, 把 String "null" uid 的命中改回 false,
+            // 修 RickUserCacheJob 将 BIGINT NULL 转 String.valueOf(null)="null" 写入
+            // Redis Set, 导致 client 传 "null" 字符串 uid 的 guest user 全部错杀的 bug.
+            if (hitRiskUidCache && "null".equals(param.getUid()) && isHitV563Exp(request, param)) {
+                hitRiskUidCache = false;
+            }
             boolean hitRiskMidCache = riskUserCache.getUnchecked(RedisKeyConstants.Recommend.riskUserMid).contains(param.getMid());
             boolean riskUser = hitRiskScene || hitRiskUidCache || hitRiskMidCache || isNoneUserRisk;
             param.setRiskUser(riskUser);
@@ -489,6 +495,20 @@ public class RecommendService {
         return param;
     }
 
+    /**
+     * 是否命中 V563 实验 (走 judgeHitAlgoExp, 同时覆盖 abExpCodes 通道和 rootSessionId
+     * 尾号通道). 当前只用于 risk uid 命中里 String "null" 错杀的精准修复.
+     */
+    private static final String V563_EXP_CODE = "563";
+
+    private boolean isHitV563Exp(RecommendRequest request, RecommendParam param) {
+        return experimentService.judgeHitAlgoExp(
+                param.getAppType(),
+                request.getRootSessionId(),
+                param.getAbExpCodes(),
+                V563_EXP_CODE);
+    }
+
     private boolean isNoneUserRisk(RecommendRequest request, RecommendParam param) {
         boolean isNoneUserRisk = false;
         try {
@@ -651,6 +671,7 @@ public class RecommendService {
             ctx.setSubSessionId(Strings.nullToEmpty(request.getSubSessionId()));
             ctx.setRootSessionId(Strings.nullToEmpty(request.getRootSessionId()));
             ctx.setMid(Strings.nullToEmpty(request.getMid()));
+            ctx.setUid(Strings.nullToEmpty(request.getUid()));
             ctx.setAppType(request.getAppType());
             ctx.setNewExpGroup(Strings.nullToEmpty(request.getNewExpGroup()));
             ctx.setHotSceneType(request.getHotSceneType());

+ 1 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/funnel/FunnelAggregator.java

@@ -30,6 +30,7 @@ public class FunnelAggregator {
         row.put("subSessionId", StringUtils.defaultString(ctx.getSubSessionId()));
         row.put("rootSessionId", StringUtils.defaultString(ctx.getRootSessionId()));
         row.put("mid", StringUtils.defaultString(ctx.getMid()));
+        row.put("uid", StringUtils.defaultString(ctx.getUid()));
         row.put("appType", String.valueOf(ctx.getAppType()));
         row.put("newExpGroup", StringUtils.defaultString(ctx.getNewExpGroup()));
         row.put("abExpCode", JSON.toJSONString(ctx.getAbExpCodes()));

+ 1 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/funnel/FunnelContext.java

@@ -28,6 +28,7 @@ public class FunnelContext {
     private String subSessionId;
     private String rootSessionId;
     private String mid;
+    private String uid;
     private int    appType;
     private String newExpGroup;
     private Set<String> abExpCodes;