|
@@ -0,0 +1,111 @@
|
|
|
|
|
+package com.tzld.piaoquan.ad.engine.service.predict.v2;
|
|
|
|
|
+
|
|
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
|
|
+import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.apache.commons.collections4.CollectionUtils;
|
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 基于 appType 和 rootSessionId 尾号的广告预测策略
|
|
|
|
|
+ * <p>
|
|
|
|
|
+ * 核心逻辑:
|
|
|
|
|
+ * 1. 根据 appType 和 rootSessionId 的尾号进行匹配
|
|
|
|
|
+ * 2. 只做数据填充(如 ecpmValue),不决定是否出广告
|
|
|
|
|
+ * 3. 返回匹配的配置信息,供后续流程使用
|
|
|
|
|
+ * <p>
|
|
|
|
|
+ * 配置格式示例:
|
|
|
|
|
+ * <pre>
|
|
|
|
|
+ * [
|
|
|
|
|
+ * {
|
|
|
|
|
+ * "appType": ["0", "3"],
|
|
|
|
|
+ * "tail": ["0", "1", "2"],
|
|
|
|
|
+ * "config": {
|
|
|
|
|
+ * "ecpmValue": 60
|
|
|
|
|
+ * }
|
|
|
|
|
+ * },
|
|
|
|
|
+ * {
|
|
|
|
|
+ * "appType": ["1", "4"],
|
|
|
|
|
+ * "tail": ["0", "1", "2"],
|
|
|
|
|
+ * "config": {
|
|
|
|
|
+ * "ecpmValue": 50
|
|
|
|
|
+ * }
|
|
|
|
|
+ * }
|
|
|
|
|
+ * ]
|
|
|
|
|
+ * </pre>
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Service
|
|
|
|
|
+public class PredictStrategyByAppTypeTail extends BasicPredict {
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Apollo 动态配置:根据 rootSessionId 尾号和 appType 进行匹配
|
|
|
|
|
+ */
|
|
|
|
|
+ @ApolloJsonValue("${experiment.app.type.tail.ecpm.config:[]}")
|
|
|
|
|
+ private List<RootSessionIdTailConfigItem> configItems;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 策略名称标识
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String name() {
|
|
|
|
|
+ return "app_type_tail_ecpm_strategy";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 核心预测方法:根据 appType 和 rootSessionId 尾号填充数据
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param ctx 预测上下文,包含 appType、rootSessionId 等信息
|
|
|
|
|
+ * @return 预测结果 Map,包含:
|
|
|
|
|
+ * - 匹配的配置信息(如 ecpmValue)
|
|
|
|
|
+ * - 返回 null 表示未匹配到任何配置,跳过该策略
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Map<String, Object> predict(PredictContext ctx) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ String rootSessionId = ctx.getRootSessionId();
|
|
|
|
|
+ String appType = ctx.getAppType();
|
|
|
|
|
+
|
|
|
|
|
+ // 前置校验:配置为空或必要参数为空时,返回 null(跳过该策略)
|
|
|
|
|
+ if (CollectionUtils.isEmpty(configItems) || StringUtils.isAnyBlank(rootSessionId, appType)) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 提取 rootSessionId 的最后一个字符作为尾号
|
|
|
|
|
+ String tail = rootSessionId.substring(rootSessionId.length() - 1);
|
|
|
|
|
+
|
|
|
|
|
+ // 遍历配置项,查找同时匹配 appType 和尾号的配置
|
|
|
|
|
+ for (RootSessionIdTailConfigItem item : configItems) {
|
|
|
|
|
+ if (item.getAppType() != null && item.getTail() != null) {
|
|
|
|
|
+ // 检查是否同时匹配 appType 和 tail
|
|
|
|
|
+ if (item.getAppType().contains(appType) && item.getTail().contains(tail)) {
|
|
|
|
|
+ // 匹配成功,填充数据
|
|
|
|
|
+ Map<String, Object> rtnMap = new HashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 将配置中的所有参数填充到返回结果中
|
|
|
|
|
+ if (item.getConfig() != null) {
|
|
|
|
|
+ rtnMap.putAll(item.getConfig());
|
|
|
|
|
+ }
|
|
|
|
|
+ rtnMap.put("ecpm_model",name());
|
|
|
|
|
+ rtnMap.put("ecpm_rootSessionId", ctx.getRootSessionId());
|
|
|
|
|
+
|
|
|
|
|
+ return rtnMap;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 未匹配到任何配置,返回 null
|
|
|
|
|
+ return null;
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("[PredictStrategyByAppTypeTail] predict error, ctx: {}", ctx, e);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|