Ver Fonte

coding:投流代码提交

zhangqin há 4 semanas atrás
pai
commit
c25b797218

+ 4 - 3
fs-ad-new-api/src/main/java/com/fs/app/event/ConversionEvent.java

@@ -1,5 +1,6 @@
 package com.fs.app.event;
 
+import com.fs.app.enums.AdvertiserTypeEnum;
 import com.fs.app.enums.ConversionTypeEnum;
 import lombok.Getter;
 import org.springframework.context.ApplicationEvent;
@@ -27,9 +28,9 @@ public class ConversionEvent extends ApplicationEvent {
     private final String traceId;
 
     /**
-     * 广告商名称(BAIDU, OCEANENGINE, SINA, GDT)
+     * 广告商名称(
      */
-    private final String advertiser;
+    private final AdvertiserTypeEnum advertiser;
 
     /**
      * 转化事件类型(如:register、form_submit、add_wechat)
@@ -47,7 +48,7 @@ public class ConversionEvent extends ApplicationEvent {
     private final Long leadId;
 
     public ConversionEvent(Object source, Long siteId, String traceId,
-                           String advertiser, ConversionTypeEnum eventType, Double value, Long leadId) {
+                           AdvertiserTypeEnum advertiser, ConversionTypeEnum eventType, Double value, Long leadId) {
         super(source);
         this.siteId = siteId;
         this.traceId = traceId;

+ 2 - 1
fs-ad-new-api/src/main/java/com/fs/app/event/ConversionEventListener.java

@@ -1,5 +1,6 @@
 package com.fs.app.event;
 
+import com.fs.app.enums.AdvertiserTypeEnum;
 import com.fs.app.mq.message.ConversionMessage;
 import com.fs.app.mq.producer.ConversionMessageProducer;
 import com.fs.app.enums.ConversionTypeEnum;
@@ -35,7 +36,7 @@ public class ConversionEventListener {
     public void handleConversionEvent(ConversionEvent event) {
         Long siteId = event.getSiteId();
         String traceId = event.getTraceId();
-        String advertiser = event.getAdvertiser();
+        AdvertiserTypeEnum advertiser = event.getAdvertiser();
         ConversionTypeEnum eventType = event.getEventType();
         Double value = event.getValue();
         Long leadId = event.getLeadId();

+ 3 - 2
fs-ad-new-api/src/main/java/com/fs/app/event/ConversionEventPublisher.java

@@ -1,5 +1,6 @@
 package com.fs.app.event;
 
+import com.fs.app.enums.AdvertiserTypeEnum;
 import com.fs.app.enums.ConversionTypeEnum;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -30,7 +31,7 @@ public class ConversionEventPublisher {
      * @param value 转化价值(元)
      * @param leadId 线索ID(可选)
      */
-    public void publishConversionEvent(Long siteId, String clickId, String advertiser,
+    public void publishConversionEvent(Long siteId, String clickId, AdvertiserTypeEnum advertiser,
                                        ConversionTypeEnum eventType, Double value, Long leadId) {
         log.info("发布转化事件 | siteId={}, clickId={}, advertiser={}, eventType={}, value={}",
                 siteId, clickId, advertiser, eventType, value);
@@ -47,7 +48,7 @@ public class ConversionEventPublisher {
      * @param advertiser 广告商名称
      * @param eventType 转化事件类型
      */
-    public void publishConversionEvent(Long siteId, String clickId, String advertiser, ConversionTypeEnum eventType) {
+    public void publishConversionEvent(Long siteId, String clickId, AdvertiserTypeEnum advertiser, ConversionTypeEnum eventType) {
         publishConversionEvent(siteId, clickId, advertiser, eventType, null, null);
     }
 }

+ 17 - 30
fs-ad-new-api/src/main/java/com/fs/app/facade/CallbackProcessingFacadeServiceImpl.java

@@ -1,24 +1,20 @@
 package com.fs.app.facade;
 
-import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import com.fs.app.enums.AdvertiserTypeEnum;
+import com.fs.app.enums.ConversionTypeEnum;
 import com.fs.app.event.ConversionEventPublisher;
-import com.fs.app.integration.adapter.IAdvertiserAdapter;
 import com.fs.app.integration.client.BaiduApiClient;
 import com.fs.app.integration.factory.AdvertiserHandlerFactory;
-import com.fs.app.integration.strategy.ICallbackStrategy;
 import com.fs.app.mq.message.ClickMessage;
 import com.fs.common.exception.base.BusinessException;
-import com.fs.common.utils.SnowflakeUtil;
 import com.fs.newAdv.domain.ClickTrace;
 import com.fs.newAdv.domain.LandingPageTemplate;
 import com.fs.newAdv.domain.Lead;
 import com.fs.newAdv.domain.Site;
 import com.fs.newAdv.dto.req.LeadSubmitRequest;
 import com.fs.newAdv.dto.res.LandingIndexRes;
-import com.fs.app.enums.ConversionTypeEnum;
 import com.fs.newAdv.service.IClickTraceService;
 import com.fs.newAdv.service.ILandingPageTemplateService;
 import com.fs.newAdv.service.ILeadService;
@@ -28,9 +24,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import java.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.Map;
@@ -41,8 +34,6 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
     @Autowired
     private IClickTraceService clickTraceService;
 
-    @Autowired
-    private BaiduApiClient baiduApiClient;
 
     @Autowired
     private AdvertiserHandlerFactory handlerFactory;
@@ -78,18 +69,6 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
         log.info("点击追踪记录保存成功:traceId={}", trace.getTraceId());
     }
 
-    @Override
-    public void updateClickTrace(ClickMessage clickMessage) {
-        Map<String, String> params = getTraceIdByPlatformParams(clickMessage.getAllParams());
-        Site site = siteService.getById(clickMessage.getSiteId());
-        ClickTrace byTraceId = iClickTraceService.getByTraceId(params.get("traceId"));
-        byTraceId.setSiteId(site.getId());
-        byTraceId.setAdvertiserId(site.getAdvertiserId());
-        byTraceId.setAdvertiserName(site.getAdvertiserName());
-        byTraceId.setViewUrl(clickMessage.getViewUrl());
-        siteService.updateById(site);
-    }
-
     /**
      * 获取traceId和source平台信息
      *
@@ -100,7 +79,6 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
         Map<String, String> traceId = new HashMap<>();
         if (StrUtil.isNotEmpty(allParams.get("bd_vid"))) {
             traceId.put("traceId", allParams.get("bd_vid"));
-            traceId.put("source", AdvertiserTypeEnum.BAIDU.getName());
             return traceId;
         } else {
             throw new BusinessException("回传参数错误 缺少traceId");
@@ -108,6 +86,18 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
 
     }
 
+    @Override
+    public void updateClickTrace(ClickMessage clickMessage) {
+        Map<String, String> params = getTraceIdByPlatformParams(clickMessage.getAllParams());
+        Site site = siteService.getById(clickMessage.getSiteId());
+        ClickTrace byTraceId = iClickTraceService.getByTraceId(params.get("traceId"));
+        byTraceId.setSiteId(site.getId());
+        byTraceId.setAdvertiserId(site.getAdvertiserId());
+        byTraceId.setAdvertiserName(site.getAdvertiserName());
+        byTraceId.setViewUrl(clickMessage.getViewUrl());
+        siteService.updateById(site);
+    }
+
     /**
      * 提取不同平台的参数
      */
@@ -129,19 +119,19 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
     }
 
     @Override
-    @Transactional(rollbackFor = Exception.class)
     public void submitForm(LeadSubmitRequest request) {
         Map<String, String> params = getTraceIdByPlatformParams(request.getRawParams());
         String traceId = params.get("traceId");
-        String source = params.get("source");
         if (StrUtil.isEmpty(traceId)) {
             throw new BusinessException("缺少traceId");
         }
+        Site byId = siteService.getById(request.getSiteId());
         // 2. 构建Lead对象
         Lead lead = new Lead();
         lead.setSiteId(request.getSiteId());
         lead.setViewUrl(request.getViewUrl());
-        lead.setSource(source);
+        lead.setAdvertiserId(byId.getAdvertiserId());
+        lead.setAdvertiserName(byId.getAdvertiserName());
         lead.setTraceId(traceId);
         lead.setStatus(0); // 新线索
 
@@ -180,7 +170,7 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
         conversionEventPublisher.publishConversionEvent(
                 lead.getSiteId(),           // 站点ID
                 lead.getTraceId(),          // 点击ID
-                lead.getSource().toUpperCase(), // 广告商(BAIDU/OCEANENGINE/SINA/GDT)
+                AdvertiserTypeEnum.getByCode(lead.getAdvertiserId()), // 广告商(BAIDU/OCEANENGINE/SINA/GDT)
                 ConversionTypeEnum.FORM_SUBMIT,              // 事件类型:提交表单
                 null,                       // 转化价值(可选)
                 lead.getId()                // 线索ID
@@ -188,9 +178,6 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
     }
 
 
-
-
-
     /**
      * 转换为String Map
      */

+ 18 - 74
fs-ad-new-api/src/main/java/com/fs/app/facade/ConversionServiceImpl.java

@@ -5,7 +5,9 @@ import cn.hutool.json.JSONUtil;
 import com.fs.app.enums.AdvertiserTypeEnum;
 import com.fs.app.enums.ConversionTypeEnum;
 import com.fs.app.integration.client.BaiduApiClient;
+import com.fs.app.integration.client.IApiClient;
 import com.fs.app.integration.client.OceanEngineApiClient;
+import com.fs.app.integration.factory.AdvertiserHandlerFactory;
 import com.fs.common.constant.RedisKeyConstant;
 import com.fs.common.exception.base.BusinessException;
 import com.fs.common.utils.RedisUtil;
@@ -49,10 +51,7 @@ public class ConversionServiceImpl implements IConversionService {
     private ConversionTargetMapper conversionTargetMapper;
 
     @Autowired
-    private OceanEngineApiClient oceanEngineApiClient;
-
-    @Autowired
-    private BaiduApiClient baiduApiClient;
+    private AdvertiserHandlerFactory advertiserHandlerFactory;
 
     @Autowired
     private RedisUtil redisUtil;
@@ -63,59 +62,11 @@ public class ConversionServiceImpl implements IConversionService {
     private ILeadService leadService;
 
     @Override
-    @Async("asyncExecutor")
-    public boolean reportToOceanEngine(Long siteId, ConversionTypeEnum eventType, String traceId, Double value) {
-  /*      log.info("开始回传转化数据到巨量引擎:站点ID={},点击ID={},事件类型={}",
-                siteId, traceId, eventType);
-
-        // 查询站点信息
-        Site site = siteService.getById(siteId);
-        if (site == null) {
-            throw new BusinessException("站点不存在");
-        }
-
-        // 查询回传账号
-        CallbackAccount callbackAccount = callbackAccountService.getById(site.getCallbackAccountId());
-        if (callbackAccount == null) {
-            throw new BusinessException("回传账号不存在");
-        }
-
-        // 4. 获取AccessToken
-        String accessToken = callbackAccount.get;
-
-        // 5. 构建转化数据
-        Map<String, Object> conversionData = buildOceanEngineConversionData(
-                clickId, eventType, value);
-
-        // 6. 保存转化日志(待回传状态)
-        ConversionLog conversionLog = saveConversionLog(
-                siteId, site.getAdvertiserId(), site.getAdvertiserName(),
-                eventType, clickId, conversionData, 0);
-
-        // 7. 调用巨量引擎API回传
-        boolean success = oceanEngineApiClient.reportConversion(accessToken, conversionData);
-
-        // 8. 更新转化日志状态
-        if (success) {
-            conversionLog.setCallbackStatus(1); // 成功
-            conversionLog.setSuccessTime(LocalDateTime.now());
-            log.info("巨量引擎转化回传成功,日志ID:{}", conversionLog.getId());
-        } else {
-            conversionLog.setCallbackStatus(2); // 失败
-            conversionLog.setErrorMsg("回传失败");
-            log.error("巨量引擎转化回传失败,日志ID:{}", conversionLog.getId());
-        }
-        conversionLog.setUpdateTime(LocalDateTime.now());
-        conversionLogMapper.updateById(conversionLog);*/
-
-        return false;
-    }
-
-    @Override
-    @Async("asyncExecutor")
-    public Boolean reportToBaidu(Long siteId, ConversionTypeEnum eventType, String traceId, Long leadId,Double value) {
-        log.info("开始回传转化数据到百度:站点ID={},点击ID={},事件类型={}",
-                siteId, traceId, eventType);
+    public boolean reportToAdvertiser(AdvertiserTypeEnum advertiser,Long siteId, ConversionTypeEnum eventType, String traceId, Long leadId,Double value) {
+        log.info("开始回传转化数据:广告商={},站点ID={},点击ID={},事件类型={}",
+                advertiser,siteId, traceId, eventType);
+        // 广告商client
+        IApiClient apiClient = advertiserHandlerFactory.getApiClient(advertiser);
         // 询站点信息
         Site site = siteService.getById(siteId);
         if (site == null) {
@@ -137,17 +88,23 @@ public class ConversionServiceImpl implements IConversionService {
         Lead lead = leadService.getById(leadId);
         // 构建回传参数
         Map<String, Object> conversionData = new HashMap<>();
+        // token
         conversionData.put("token", accessToken);
+        // 点击id
+        conversionData.put("traceId", lead.getTraceId());
+        // 百度落地页
         conversionData.put("logidUrl", lead.getViewUrl());
-        conversionData.put("newType", eventType.getAdvertiserType(AdvertiserTypeEnum.BAIDU));
-        conversionData.put("conversionTime", System.currentTimeMillis() / 1000);
+        // 事件
+        conversionData.put("eventType", eventType.getAdvertiserType(AdvertiserTypeEnum.BAIDU));
+        // 回传时间
+        conversionData.put("timestamp", System.currentTimeMillis() / 1000);
 
         if (value != null && value > 0) {
-            conversionData.put("convertValue", value);
+            conversionData.put("value", value);
         }
 
         // 调用百度API回传
-        boolean b = baiduApiClient.reportConversion(conversionData);
+        boolean b = apiClient.reportConversion(conversionData);
 
         // 保存转化日志(待回传状态)
         ConversionLog conversionLog = saveConversionLog(
@@ -156,20 +113,7 @@ public class ConversionServiceImpl implements IConversionService {
         return b;
     }
 
-    /**
-     * 获取巨量引擎AccessToken
-     */
-    private String getAccessToken(PromotionAccount account) {
-        String appId = account.getAppId();
-        String appSecret = account.getAppSecret();
-
-        if (StrUtil.isBlank(appId) || StrUtil.isBlank(appSecret)) {
-            throw new BusinessException("推广账号未配置AppId或AppSecret");
-        }
 
-        // 实际生产环境中,AccessToken应该缓存到Redis,避免频繁获取
-        return oceanEngineApiClient.getAccessToken(appId, appSecret);
-    }
 
     /**
      * 构建巨量引擎转化数据

+ 3 - 14
fs-ad-new-api/src/main/java/com/fs/app/facade/IConversionService.java

@@ -1,5 +1,6 @@
 package com.fs.app.facade;
 
+import com.fs.app.enums.AdvertiserTypeEnum;
 import com.fs.app.enums.ConversionTypeEnum;
 
 /**
@@ -10,20 +11,8 @@ import com.fs.app.enums.ConversionTypeEnum;
  * @updated 2025-11-05 使用clickId参数直接回传
  */
 public interface IConversionService {
-
-    /**
-     * 回传转化数据到巨量引擎
-     *
-     * @param siteId 站点ID
-     * @param eventType 事件类型
-     * @param traceId 点击ID(广告平台提供)
-     * @param value 转化价值
-     * @return 是否成功
-     */
-    boolean reportToOceanEngine(Long siteId, ConversionTypeEnum eventType, String traceId, Double value);
-
     /**
-     * 回传转化数据到百度
+     * 回传转化数据到广告商
      *
      * @param siteId 站点ID
      * @param eventType 事件类型
@@ -31,6 +20,6 @@ public interface IConversionService {
      * @param value 转化价值
      * @return 是否成功
      */
-    Boolean reportToBaidu(Long siteId, ConversionTypeEnum eventType, String traceId, Long leadId,Double value);
+    boolean reportToAdvertiser(AdvertiserTypeEnum advertiser,Long siteId, ConversionTypeEnum eventType, String traceId, Long leadId, Double value);
 }
 

+ 4 - 4
fs-ad-new-api/src/main/java/com/fs/app/integration/client/BaiduApiClient.java

@@ -118,18 +118,18 @@ public class BaiduApiClient implements IApiClient{
         conversion.put("logidUrl", logidUrl);
 
         // 转化类型(必填)
-        conversion.put("newType", conversionData.get("newType"));
+        conversion.put("newType", conversionData.get("eventType"));
 
         // 转化时间(毫秒时间戳)
-        Long conversionTime = (Long) conversionData.get("conversionTime");
+        Long conversionTime = (Long) conversionData.get("timestamp");
         if (conversionTime == null) {
             conversionTime = System.currentTimeMillis() / 1000;
         }
         conversion.put("convertTime", conversionTime);
 
         // 转化价值(可选)
-        if (conversionData.containsKey("convertValue")) {
-            conversion.put("convertValue", conversionData.get("convertValue"));
+        if (conversionData.containsKey("value")) {
+            conversion.put("convertValue", conversionData.get("value"));
         }
 
         conversionList.add(conversion);

+ 167 - 0
fs-ad-new-api/src/main/java/com/fs/app/integration/client/GDTApiClient.java

@@ -0,0 +1,167 @@
+package com.fs.app.integration.client;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.fs.app.enums.AdvertiserTypeEnum;
+import com.fs.common.constant.SystemConstant;
+import com.fs.common.exception.ThirdPartyException;
+import com.fs.newAdv.service.IApiCallLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 腾讯广点通API客户端
+ * 用于调用巨量引擎的API接口(转化回传等)
+ *
+ * @author zhangqin
+ * @date 2025-11-03
+ * @see <a href="https://developers.e.qq.com/v3.0/docs/api/leads_action_type_report/add">巨量引擎API文档</a>
+ */
+@Slf4j
+@Component
+public class GDTApiClient implements IApiClient{
+
+    /**
+     * 巨量引擎转化回传API地址
+     */
+    private static final String CONVERSION_API_URL = "https://api.e.qq.com/user_actions/add";
+
+    @Autowired
+    private IApiCallLogService apiCallLogService;
+
+    /**
+     * 回传转化数据
+     *
+     * @param conversionData 转化数据
+     * @return 是否成功
+     */
+    @Override
+    public boolean reportConversion(Map<String, Object> conversionData) {
+        String apiUrl = CONVERSION_API_URL;
+        long startTime = System.currentTimeMillis();
+
+        log.info("开始回传转化数据到广点通,URL:{}", apiUrl);
+
+        try {
+            // 构建请求参数
+            Map<String, Object> requestParams = buildConversionParams(conversionData);
+            String requestBody = JSONUtil.toJsonStr(requestParams);
+
+            // 发送HTTP请求
+            HttpResponse response = HttpRequest.post(apiUrl)
+                    .header("Access-Token", (String) conversionData.get("token"))
+                    .header("Content-Type", "application/json")
+                    .body(requestBody)
+                    .timeout(SystemConstant.API_TIMEOUT)
+                    .execute();
+
+            String responseBody = response.body();
+            int statusCode = response.getStatus();
+
+            log.info("巨量引擎API响应:状态码={},响应={}", statusCode, responseBody);
+
+            // 记录API调用日志
+            apiCallLogService.saveApiCallLog(apiUrl, "POST", requestBody, statusCode, responseBody,
+                    statusCode == 200 ? 1 : 2, null, startTime);
+
+            // 解析响应
+            if (statusCode == 200) {
+                JSONObject result = JSONUtil.parseObj(responseBody);
+                Integer code = result.getInt("code");
+                if (code != null && code == 0) {
+                    log.info("巨量引擎转化回传成功");
+                    return true;
+                } else {
+                    String message = result.getStr("message", "未知错误");
+                    log.error("巨量引擎转化回传失败:code={},message={}", code, message);
+                }
+            } else {
+                log.error("巨量引擎API调用失败,状态码:{}", statusCode);
+            }
+        } catch (Exception e) {
+            log.error("调用巨量引擎API异常", e);
+            apiCallLogService.saveApiCallLog(apiUrl, "POST", JSONUtil.toJsonStr(conversionData),
+                    0, null, 2, e.getMessage(), startTime);
+        }
+        return false;
+    }
+
+    /**
+     * 构建转化回传参数
+     *
+     * @param conversionData 转化数据
+     * @return 请求参数
+     */
+    private Map<String, Object> buildConversionParams(Map<String, Object> conversionData) {
+        Map<String, Object> params = new HashMap<>();
+
+        // 必填参数
+        params.put("event_type", conversionData.get("eventType")); // 转化类型
+        params.put("context", buildContext(conversionData)); // 上下文信息
+
+        // 可选参数
+        if (conversionData.containsKey("value")) {
+            params.put("value", conversionData.get("value")); // 转化价值
+        }
+
+        if (conversionData.containsKey("description")) {
+            params.put("description", conversionData.get("description")); // 描述
+        }
+
+        return params;
+    }
+
+    /**
+     * 构建上下文信息
+     *
+     * @param conversionData 转化数据
+     * @return 上下文Map
+     */
+    private Map<String, Object> buildContext(Map<String, Object> conversionData) {
+        Map<String, Object> context = new HashMap<>();
+
+        // 必填:点击ID
+        context.put("ad", buildAdContext(conversionData));
+
+        // 用户信息
+        if (conversionData.containsKey("userId")) {
+            Map<String, Object> user = new HashMap<>();
+            user.put("user_id", conversionData.get("userId"));
+            context.put("user", user);
+        }
+
+        return context;
+    }
+
+    /**
+     * 构建广告上下文
+     *
+     * @param conversionData 转化数据
+     * @return 广告上下文
+     */
+    private Map<String, Object> buildAdContext(Map<String, Object> conversionData) {
+        Map<String, Object> ad = new HashMap<>();
+
+        // 点击ID(必填)
+        String clickId = (String) conversionData.get("traceId");
+        if (StrUtil.isBlank(clickId)) {
+            throw new ThirdPartyException("点击ID不能为空");
+        }
+        ad.put("callback", clickId);
+
+        return ad;
+    }
+
+    @Override
+    public AdvertiserTypeEnum getAdvertiserType() {
+        return AdvertiserTypeEnum.BAIDU;
+    }
+}
+

+ 14 - 82
fs-ad-new-api/src/main/java/com/fs/app/integration/client/OceanEngineApiClient.java

@@ -5,17 +5,14 @@ import cn.hutool.http.HttpRequest;
 import cn.hutool.http.HttpResponse;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
-
+import com.fs.app.enums.AdvertiserTypeEnum;
 import com.fs.common.constant.SystemConstant;
 import com.fs.common.exception.ThirdPartyException;
-import com.fs.common.utils.SnowflakeUtil;
-import com.fs.newAdv.domain.ApiCallLog;
-import com.fs.newAdv.mapper.ApiCallLogMapper;
+import com.fs.newAdv.service.IApiCallLogService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import java.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -29,7 +26,7 @@ import java.util.Map;
  */
 @Slf4j
 @Component
-public class OceanEngineApiClient {
+public class OceanEngineApiClient implements IApiClient{
 
     /**
      * 巨量引擎转化回传API地址
@@ -37,16 +34,16 @@ public class OceanEngineApiClient {
     private static final String CONVERSION_API_URL = "https://ad.oceanengine.com/open_api/2/conversion/";
 
     @Autowired
-    private ApiCallLogMapper apiCallLogMapper;
+    private IApiCallLogService apiCallLogService;
 
     /**
      * 回传转化数据到巨量引擎
      *
-     * @param accessToken 访问令牌
      * @param conversionData 转化数据
      * @return 是否成功
      */
-    public boolean reportConversion(String accessToken, Map<String, Object> conversionData) {
+    @Override
+    public boolean reportConversion(Map<String, Object> conversionData) {
         String apiUrl = CONVERSION_API_URL;
         long startTime = System.currentTimeMillis();
 
@@ -59,7 +56,7 @@ public class OceanEngineApiClient {
 
             // 发送HTTP请求
             HttpResponse response = HttpRequest.post(apiUrl)
-                    .header("Access-Token", accessToken)
+                    .header("Access-Token", (String) conversionData.get("token"))
                     .header("Content-Type", "application/json")
                     .body(requestBody)
                     .timeout(SystemConstant.API_TIMEOUT)
@@ -71,33 +68,29 @@ public class OceanEngineApiClient {
             log.info("巨量引擎API响应:状态码={},响应={}", statusCode, responseBody);
 
             // 记录API调用日志
-            saveApiCallLog(apiUrl, "POST", requestBody, statusCode, responseBody,
+            apiCallLogService.saveApiCallLog(apiUrl, "POST", requestBody, statusCode, responseBody,
                     statusCode == 200 ? 1 : 2, null, startTime);
 
             // 解析响应
             if (statusCode == 200) {
                 JSONObject result = JSONUtil.parseObj(responseBody);
                 Integer code = result.getInt("code");
-
                 if (code != null && code == 0) {
                     log.info("巨量引擎转化回传成功");
                     return true;
                 } else {
                     String message = result.getStr("message", "未知错误");
                     log.error("巨量引擎转化回传失败:code={},message={}", code, message);
-                    throw new ThirdPartyException("巨量引擎回传失败:" + message);
                 }
             } else {
                 log.error("巨量引擎API调用失败,状态码:{}", statusCode);
-                throw new ThirdPartyException("巨量引擎API调用失败,状态码:" + statusCode);
             }
-
         } catch (Exception e) {
             log.error("调用巨量引擎API异常", e);
-            saveApiCallLog(apiUrl, "POST", JSONUtil.toJsonStr(conversionData),
+            apiCallLogService.saveApiCallLog(apiUrl, "POST", JSONUtil.toJsonStr(conversionData),
                     0, null, 2, e.getMessage(), startTime);
-            throw new ThirdPartyException("调用巨量引擎API异常:" + e.getMessage(), e);
         }
+        return false;
     }
 
     /**
@@ -157,7 +150,7 @@ public class OceanEngineApiClient {
         Map<String, Object> ad = new HashMap<>();
 
         // 点击ID(必填)
-        String clickId = (String) conversionData.get("clickId");
+        String clickId = (String) conversionData.get("traceId");
         if (StrUtil.isBlank(clickId)) {
             throw new ThirdPartyException("点击ID不能为空");
         }
@@ -166,70 +159,9 @@ public class OceanEngineApiClient {
         return ad;
     }
 
-    /**
-     * 获取访问令牌
-     *
-     * @param appId 应用ID
-     * @param appSecret 应用密钥
-     * @return 访问令牌
-     */
-    public String getAccessToken(String appId, String appSecret) {
-        String apiUrl = "https://ad.oceanengine.com/open_api/oauth2/access_token/";
-
-        try {
-            Map<String, Object> params = new HashMap<>();
-            params.put("app_id", appId);
-            params.put("secret", appSecret);
-            params.put("grant_type", "auth_code");
-
-            String response = HttpRequest.post(apiUrl)
-                    .header("Content-Type", "application/json")
-                    .body(JSONUtil.toJsonStr(params))
-                    .timeout(SystemConstant.API_TIMEOUT)
-                    .execute()
-                    .body();
-
-            JSONObject result = JSONUtil.parseObj(response);
-
-            if (result.getInt("code") == 0) {
-                JSONObject data = result.getJSONObject("data");
-                return data.getStr("access_token");
-            } else {
-                throw new ThirdPartyException("获取巨量引擎AccessToken失败:" + result.getStr("message"));
-            }
-
-        } catch (Exception e) {
-            log.error("获取巨量引擎AccessToken异常", e);
-            throw new ThirdPartyException("获取AccessToken异常:" + e.getMessage(), e);
-        }
-    }
-
-    /**
-     * 保存API调用日志
-     */
-    private void saveApiCallLog(String apiUrl, String method, String requestParams,
-                                Integer responseStatus, String responseBody,
-                                Integer callStatus, String errorMsg, long startTime) {
-        try {
-            ApiCallLog log = new ApiCallLog();
-            log.setId(SnowflakeUtil.nextId());
-            log.setAdvertiserId(2L); // 巨量引擎
-            log.setAdvertiserName("巨量引擎");
-            log.setApiName("转化回传API");
-            log.setApiUrl(apiUrl);
-            log.setRequestMethod(method);
-            log.setRequestParams(requestParams);
-            log.setResponseStatus(responseStatus);
-            log.setResponseBody(responseBody);
-            log.setCallStatus(callStatus);
-            log.setErrorMsg(errorMsg);
-            log.setCostTime(System.currentTimeMillis() - startTime);
-            log.setCreateTime(LocalDateTime.now());
-
-            apiCallLogMapper.insert(log);
-        } catch (Exception e) {
-            log.error("保存API调用日志失败", e);
-        }
+    @Override
+    public AdvertiserTypeEnum getAdvertiserType() {
+        return AdvertiserTypeEnum.BAIDU;
     }
 }
 

+ 6 - 54
fs-ad-new-api/src/main/java/com/fs/app/mq/consumer/ConversionMessageConsumer.java

@@ -1,7 +1,7 @@
 package com.fs.app.mq.consumer;
 
-import cn.hutool.core.util.StrUtil;
 import com.fs.app.constant.MqTopicConstant;
+import com.fs.app.enums.AdvertiserTypeEnum;
 import com.fs.app.enums.ConversionTypeEnum;
 import com.fs.app.facade.IConversionService;
 import com.fs.app.mq.message.ConversionMessage;
@@ -50,9 +50,11 @@ public class ConversionMessageConsumer implements RocketMQListener<ConversionMes
      */
     @Override
     public void onMessage(ConversionMessage message) {
-        String advertiser = message.getAdvertiser();
+        AdvertiserTypeEnum advertiser = message.getAdvertiser();
         String traceId = message.getTraceId();
         String messageId = message.getMessageId();
+        Long siteId = message.getSiteId();
+        Long leadId = message.getLeadId();
         ConversionTypeEnum eventType = message.getEventType();
 
         log.info("消费者接收到转化消息 | traceId={}, advertiser={}, eventType={}",
@@ -65,7 +67,8 @@ public class ConversionMessageConsumer implements RocketMQListener<ConversionMes
         }
 
         // 2. 调用广告平台API回传
-        boolean success = callAdvertiserApi(message);
+        boolean success = conversionService.reportToAdvertiser(advertiser,siteId, eventType,
+                traceId, leadId, null);
 
         if (success) {
             // 3. 标记为已处理
@@ -99,56 +102,5 @@ public class ConversionMessageConsumer implements RocketMQListener<ConversionMes
         String key = "conversion:processed:" + messageId;
         redisUtil.set(key, "1", 7, TimeUnit.DAYS);
     }
-
-    /**
-     * 调用广告平台API
-     *
-     * @param message 转化消息
-     * @return 是否成功
-     */
-    private boolean callAdvertiserApi(ConversionMessage message) {
-        String advertiser = message.getAdvertiser();
-
-        if (StrUtil.isBlank(advertiser)) {
-            log.error("广告商名称为空 | traceId={}", message.getTraceId());
-            return false;
-        }
-
-        switch (advertiser.toUpperCase()) {
-            case "BAIDU":
-                return conversionService.reportToBaidu(
-                        message.getSiteId(),
-                        message.getEventType(),
-                        message.getTraceId(),
-                        message.getLeadId(),
-                        null
-                );
-
-            case "OCEAN_ENGINE":
-                return conversionService.reportToOceanEngine(
-                        message.getSiteId(),
-                        message.getEventType(),
-                        message.getTraceId(),
-                        null
-                );
-
-            case "SINA":
-                // TODO: 实现新浪API调用
-                log.warn("新浪平台回传暂未实现 | clickId={}", message.getTraceId());
-                return false;
-
-            case "GDT":
-                // TODO: 实现广点通API调用
-                log.warn("广点通平台回传暂未实现 | clickId={}", message.getTraceId());
-                return false;
-
-            default:
-                log.error("未知广告商 | advertiser={}, clickId={}",
-                        advertiser, message.getTraceId());
-                return false;
-        }
-    }
-
-
 }
 

+ 11 - 27
fs-ad-new-api/src/main/java/com/fs/app/mq/message/ConversionMessage.java

@@ -1,7 +1,9 @@
 package com.fs.app.mq.message;
 
+import com.fs.app.enums.AdvertiserTypeEnum;
 import com.fs.app.enums.ConversionTypeEnum;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 
 import java.io.Serializable;
 
@@ -11,6 +13,7 @@ import java.io.Serializable;
  * @author zhangqin
  * @date 2025-11-05
  */
+@EqualsAndHashCode(callSuper = true)
 @Data
 public class ConversionMessage extends MqMessageDto implements Serializable {
 
@@ -27,43 +30,24 @@ public class ConversionMessage extends MqMessageDto implements Serializable {
     private String traceId;
 
     /**
-     * 广告商名称:BAIDU, OCEANENGINE, SINA, GDT
+     * 广告商名称
      */
-    private String advertiser;
+    private AdvertiserTypeEnum advertiser;
 
     /**
-     * 事件类型:SUBMIT_FORM(提交表单), REGISTER(注册), PAY(支付)等
+     * 事件类型
      */
     private ConversionTypeEnum eventType;
 
-
+    /**
+     * 线索id
+     */
     private Long leadId;
 
-   /* *//**
+    /**
      * 转化价值(可选)
-     *//*
+     */
     private Double value;
-
-    *//**
-     * 消息时间戳
-     *//*
-    private Long timestamp;
-
-    *//**
-     * 消息ID(用于幂等性校验)
-     * 格式:clickId_eventType_timestamp
-     *//*
-    private String messageId;
-
-    *//**
-     * 线索ID(可选,用于关联业务数据)
-     *//*
-    private Long leadId;
-
-    *//**
-     * 转化日志ID(可选,用于更新转化记录状态)
-     *//*
-    private Long conversionLogId;*/
 }
 
 

+ 3 - 2
fs-service/src/main/java/com/fs/newAdv/domain/Lead.java

@@ -41,9 +41,10 @@ public class Lead implements Serializable {
     private String traceId;
 
     /**
-     * 来源平台(BAIDU, OCEANENGINE, SINA, GDT)
+     * 来源平台
      */
-    private String source;
+    private Long advertiserId;
+    private String advertiserName;
 
     /**
      * 原始参数JSON

+ 17 - 0
fs-service/src/main/java/com/fs/newAdv/service/IApiCallLogService.java

@@ -0,0 +1,17 @@
+package com.fs.newAdv.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.newAdv.domain.ApiCallLog;
+
+/**
+ * 站点管理服务
+ *
+ * @author zhangqin
+ */
+public interface IApiCallLogService extends IService<ApiCallLog> {
+
+    void saveApiCallLog(String apiUrl, String method, String requestParams,
+                        Integer responseStatus, String responseBody,
+                        Integer callStatus, String errorMsg, long startTime);
+}
+

+ 39 - 0
fs-service/src/main/java/com/fs/newAdv/service/impl/ApiCallLogServiceImpl.java

@@ -0,0 +1,39 @@
+package com.fs.newAdv.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.SnowflakeUtil;
+import com.fs.newAdv.domain.ApiCallLog;
+import com.fs.newAdv.mapper.ApiCallLogMapper;
+import com.fs.newAdv.service.IApiCallLogService;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+
+/**
+ * 站点管理服务
+ *
+ * @author zhangqin
+ */
+@Service
+public class ApiCallLogServiceImpl extends ServiceImpl<ApiCallLogMapper, ApiCallLog> implements IApiCallLogService {
+    @Override
+    public void saveApiCallLog(String apiUrl, String method, String requestParams,
+                               Integer responseStatus, String responseBody,
+                               Integer callStatus, String errorMsg, long startTime) {
+        ApiCallLog log = new ApiCallLog();
+        log.setId(SnowflakeUtil.nextId());
+        log.setAdvertiserId(2L); // 巨量引擎
+        log.setAdvertiserName("巨量引擎");
+        log.setApiName("转化回传API");
+        log.setApiUrl(apiUrl);
+        log.setRequestMethod(method);
+        log.setRequestParams(requestParams);
+        log.setResponseStatus(responseStatus);
+        log.setResponseBody(responseBody);
+        log.setCallStatus(callStatus);
+        log.setErrorMsg(errorMsg);
+        log.setCostTime(System.currentTimeMillis() - startTime);
+        log.setCreateTime(LocalDateTime.now());
+        this.save(log);
+    }
+}