Ver Fonte

coding:投流代码提交

zhangqin há 4 dias atrás
pai
commit
35ce72acd4
44 ficheiros alterados com 522 adições e 404 exclusões
  1. 4 4
      fs-ad-new-api/src/main/java/com/fs/app/controller/CallbackController.java
  2. 1 1
      fs-ad-new-api/src/main/java/com/fs/app/controller/TestController.java
  3. 52 0
      fs-ad-new-api/src/main/java/com/fs/app/controller/WeChatController.java
  4. 3 4
      fs-ad-new-api/src/main/java/com/fs/app/facade/CallbackProcessingFacadeServiceImpl.java
  5. 3 4
      fs-ad-new-api/src/main/java/com/fs/app/facade/ConversionServiceImpl.java
  6. 2 2
      fs-ad-new-api/src/main/java/com/fs/app/task/ConversionRetryTask.java
  7. 3 163
      fs-ad-new-api/src/main/java/com/fs/app/task/DataSyncTask.java
  8. 19 0
      fs-ad-new-api/src/main/java/com/fs/framework/config/CorsConfig.java
  9. 1 1
      fs-company/src/main/java/com/fs/company/controller/newAdv/AdvChannelController.java
  10. 46 0
      fs-company/src/main/java/com/fs/company/controller/newAdv/AdvConfigController.java
  11. 1 1
      fs-company/src/main/java/com/fs/company/controller/newAdv/AdvProjectController.java
  12. 1 1
      fs-company/src/main/java/com/fs/company/controller/newAdv/AdvertiserController.java
  13. 1 1
      fs-company/src/main/java/com/fs/company/controller/newAdv/CallbackAccountController.java
  14. 1 1
      fs-company/src/main/java/com/fs/company/controller/newAdv/ConversionLogController.java
  15. 1 1
      fs-company/src/main/java/com/fs/company/controller/newAdv/DomainController.java
  16. 1 1
      fs-company/src/main/java/com/fs/company/controller/newAdv/LandingPageTemplateController.java
  17. 1 1
      fs-company/src/main/java/com/fs/company/controller/newAdv/PromotionAccountController.java
  18. 1 1
      fs-company/src/main/java/com/fs/company/controller/newAdv/SiteController.java
  19. 1 1
      fs-company/src/main/java/com/fs/company/controller/newAdv/StatisticsController.java
  20. 1 1
      fs-company/src/main/java/com/fs/company/controller/newAdv/TrackingLinkController.java
  21. 8 0
      fs-service/src/main/java/com/fs/newAdv/constant/AdvConfigConstant.java
  22. 44 0
      fs-service/src/main/java/com/fs/newAdv/dto/res/AdvConfigRes.java
  23. 1 1
      fs-service/src/main/java/com/fs/newAdv/dto/res/LandingIndexRes.java
  24. 1 1
      fs-service/src/main/java/com/fs/newAdv/integration/adapter/BaiduAdapter.java
  25. 1 1
      fs-service/src/main/java/com/fs/newAdv/integration/adapter/IAdvertiserAdapter.java
  26. 1 1
      fs-service/src/main/java/com/fs/newAdv/integration/adapter/OPPOAdapter.java
  27. 1 1
      fs-service/src/main/java/com/fs/newAdv/integration/adapter/OceanEngineAdapter.java
  28. 1 1
      fs-service/src/main/java/com/fs/newAdv/integration/adapter/TencentAdapter.java
  29. 1 2
      fs-service/src/main/java/com/fs/newAdv/integration/adapter/VIVOAdapter.java
  30. 1 1
      fs-service/src/main/java/com/fs/newAdv/integration/client/AbstractApiClient.java
  31. 2 4
      fs-service/src/main/java/com/fs/newAdv/integration/client/IAccessTokenClient.java
  32. 1 1
      fs-service/src/main/java/com/fs/newAdv/integration/client/IApiClient.java
  33. 40 136
      fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/BaiduApiClient.java
  34. 2 3
      fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/IQIYIApiClient.java
  35. 2 3
      fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/OPPOApiClient.java
  36. 28 8
      fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/OceanEngineApiClient.java
  37. 38 38
      fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/TencentApiClient.java
  38. 2 3
      fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/VIVOApiClient.java
  39. 3 3
      fs-service/src/main/java/com/fs/newAdv/integration/factory/AdvertiserHandlerFactory.java
  40. 5 1
      fs-service/src/main/java/com/fs/newAdv/service/ILeadService.java
  41. 1 0
      fs-service/src/main/java/com/fs/newAdv/service/ISiteStatisticsService.java
  42. 27 6
      fs-service/src/main/java/com/fs/newAdv/service/impl/LeadServiceImpl.java
  43. 165 0
      fs-service/src/main/java/com/fs/newAdv/service/impl/SiteStatisticsServiceImpl.java
  44. 2 0
      fs-service/src/main/java/com/fs/qw/domain/QwExternalContact.java

+ 4 - 4
fs-ad-new-api/src/main/java/com/fs/app/controller/CallbackController.java

@@ -1,9 +1,9 @@
 package com.fs.app.controller;
 
 import cn.hutool.core.util.ObjectUtil;
-import com.fs.app.integration.client.IAccessTokenClient;
-import com.fs.app.integration.client.IApiClient;
-import com.fs.app.integration.factory.AdvertiserHandlerFactory;
+import com.fs.newAdv.integration.client.IAccessTokenClient;
+import com.fs.newAdv.integration.client.IApiClient;
+import com.fs.newAdv.integration.factory.AdvertiserHandlerFactory;
 import com.fs.common.result.Result;
 import com.fs.newAdv.domain.PromotionAccount;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
@@ -63,7 +63,7 @@ public class CallbackController {
      */
     @GetMapping("/tencent/getAuthCode")
     public Result<String> getTencentTAuthCode(HttpServletRequest request) {
-        getAccessToken(AdvertiserTypeEnum.OCEANENGINE, request.getParameter("state"), request.getParameter("auth_code"));
+        getAccessToken(AdvertiserTypeEnum.TENCENT, request.getParameter("state"), request.getParameter("authorization_code"));
         return Result.success("");
     }
 

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/controller/TestController.java

@@ -1,6 +1,6 @@
 package com.fs.app.controller;
 
-import com.fs.app.integration.client.advertiser.BaiduApiClient;
+import com.fs.newAdv.integration.client.advertiser.BaiduApiClient;
 import com.fs.newAdv.enums.SystemEventTypeEnum;
 import com.fs.newAdv.event.ConversionEventPublisher;
 import com.fs.newAdv.service.IPromotionAccountService;

+ 52 - 0
fs-ad-new-api/src/main/java/com/fs/app/controller/WeChatController.java

@@ -0,0 +1,52 @@
+package com.fs.app.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fs.app.facade.CallbackProcessingFacadeService;
+import com.fs.common.core.domain.R;
+import com.fs.common.result.Result;
+import com.fs.common.utils.http.HttpUtils;
+import com.fs.wx.miniapp.config.WxMaProperties;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 广告商监测链接
+ *
+ * @author zhangqin
+ */
+@Slf4j
+@RestController
+@RequestMapping("/scheme")
+public class WeChatController {
+
+    @Autowired
+    private CallbackProcessingFacadeService facadeService;
+
+    @Autowired
+    private WxMaProperties properties;
+
+    @GetMapping("/getSchemeUrl")
+    public Result<String> getSchemeUrl(@RequestParam(value = "traceId") String traceId) {
+/*        String appId = properties.getConfigs().get(0).getAppid();
+        String secret = properties.getConfigs().get(0).getSecret();
+        String rspStr = HttpUtils.sendGet("https://api.weixin.qq.com/cgi-bin/token", "grant_type=client_credential&" + "appid=" + appId + "&secret=" + secret);
+        JSONObject obj = JSONObject.parseObject(rspStr);
+        String access_token = obj.getString("access_token");
+        JSONObject jsonObject = new JSONObject();
+        JSONObject jump_wxaObj = new JSONObject();
+        jump_wxaObj.put("path", "/pages_company/card");
+        jump_wxaObj.put("query", "traceId=" + traceId);
+        jsonObject.put("jump_wxa", jump_wxaObj);
+        jsonObject.put("is_expire", false);
+        String paramStr = jsonObject.toJSONString();
+        String postStr = HttpUtils.sendPost("https://api.weixin.qq.com/wxa/generatescheme?access_token=" + access_token, paramStr);
+        obj = JSONObject.parseObject(postStr);*/
+        //response.addHeader("Access-Control-Allow-Origin", "*");
+        return Result.success("weixin://dl/business/?t=GqPFu1oh2fn");
+    }
+}

+ 3 - 4
fs-ad-new-api/src/main/java/com/fs/app/facade/CallbackProcessingFacadeServiceImpl.java

@@ -1,14 +1,12 @@
 package com.fs.app.facade;
 
-import cn.hutool.core.thread.ThreadUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.fs.app.integration.adapter.IAdvertiserAdapter;
-import com.fs.app.integration.factory.AdvertiserHandlerFactory;
+import com.fs.newAdv.integration.adapter.IAdvertiserAdapter;
+import com.fs.newAdv.integration.factory.AdvertiserHandlerFactory;
 import com.fs.common.exception.base.BusinessException;
 import com.fs.common.utils.RedisUtil;
 import com.fs.newAdv.domain.LandingPageTemplate;
@@ -180,6 +178,7 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
         LandingIndexRes res = new LandingIndexRes();
         redisUtil.set(TEMPLATE_DATA + traceId, templateData, 24, TimeUnit.HOURS);
         res.setTemplateData(templateData);
+        res.setTraceId(byTraceId.getTraceId());
         return res;
     }
 

+ 3 - 4
fs-ad-new-api/src/main/java/com/fs/app/facade/ConversionServiceImpl.java

@@ -2,11 +2,10 @@ package com.fs.app.facade;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
-import com.fs.app.integration.adapter.IAdvertiserAdapter;
-import com.fs.app.integration.client.IApiClient;
-import com.fs.app.integration.factory.AdvertiserHandlerFactory;
+import com.fs.newAdv.integration.adapter.IAdvertiserAdapter;
+import com.fs.newAdv.integration.client.IApiClient;
+import com.fs.newAdv.integration.factory.AdvertiserHandlerFactory;
 import com.fs.common.utils.RedisUtil;
 import com.fs.common.utils.SnowflakeUtil;
 import com.fs.newAdv.domain.*;

+ 2 - 2
fs-ad-new-api/src/main/java/com/fs/app/task/ConversionRetryTask.java

@@ -3,8 +3,8 @@ package com.fs.app.task;
 
 import cn.hutool.json.JSONUtil;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
-import com.fs.app.integration.client.IApiClient;
-import com.fs.app.integration.factory.AdvertiserHandlerFactory;
+import com.fs.newAdv.integration.client.IApiClient;
+import com.fs.newAdv.integration.factory.AdvertiserHandlerFactory;
 import com.fs.common.annotation.DistributeLock;
 import com.fs.common.constant.SystemConstant;
 import com.fs.newAdv.domain.ConversionLog;

+ 3 - 163
fs-ad-new-api/src/main/java/com/fs/app/task/DataSyncTask.java

@@ -1,18 +1,6 @@
 package com.fs.app.task;
 
-import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.fs.app.integration.client.IApiClient;
-import com.fs.app.integration.factory.AdvertiserHandlerFactory;
-import com.fs.newAdv.domain.Lead;
-import com.fs.newAdv.domain.PromotionAccount;
-import com.fs.newAdv.domain.Site;
-import com.fs.newAdv.domain.SiteStatistics;
-import com.fs.newAdv.enums.AdvertiserTypeEnum;
-import com.fs.newAdv.service.ILeadService;
-import com.fs.newAdv.service.IPromotionAccountService;
-import com.fs.newAdv.service.ISiteService;
 import com.fs.newAdv.service.ISiteStatisticsService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -20,9 +8,6 @@ import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
 import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
 
 /**
  * 数据同步定时任务
@@ -35,14 +20,7 @@ import java.util.stream.Collectors;
 @Component
 public class DataSyncTask {
 
-    @Autowired
-    private ISiteService siteService;
-    @Autowired
-    private IPromotionAccountService promotionAccountService;
-    @Autowired
-    private AdvertiserHandlerFactory advertiserHandlerFactory;
-    @Autowired
-    private ILeadService leadService;
+
     @Autowired
     private ISiteStatisticsService statisticsService;
 
@@ -54,7 +32,7 @@ public class DataSyncTask {
     @Scheduled(cron = "0 0 1 * * ?")
     public void syncYesterdayData() {
         String batchNo = DateUtil.format(LocalDateTime.now().minusDays(1), "yyyy-MM-dd");
-        syncData(batchNo);
+        statisticsService.syncData(batchNo,1);
     }
 
     /**
@@ -65,148 +43,10 @@ public class DataSyncTask {
     @Scheduled(cron = "0 0/1 * * * ?")
     public void syncTodayData() {
         String batchNo = DateUtil.format(LocalDateTime.now(), "yyyy-MM-dd");
-        syncData(batchNo);
-    }
-
-    private void syncData(String batchNo) {
-        log.info("========== 数据同步任务开始,批次号:{} ==========", batchNo);
-        try {
-            List<Site> sites = siteService.selectEnabledSite();
-            if (CollUtil.isEmpty(sites)) {
-                log.info("没有启用的站点,跳过同步");
-                return;
-            }
-
-            // 查询所有API账户并过滤启用的
-            List<Long> accountIds = sites.stream()
-                    .map(Site::getPromotionAccountId)
-                    .distinct()
-                    .collect(Collectors.toList());
-
-            List<PromotionAccount> enabledAccounts = promotionAccountService.listByIds(accountIds).stream()
-                    .filter(account -> Integer.valueOf(1).equals(account.getApiSwitch()))
-                    .collect(Collectors.toList());
-
-            if (enabledAccounts.isEmpty()) {
-                log.info("没有启用API的账户,跳过同步");
-                return;
-            }
-
-            log.info("查询到 {} 个需要同步的账户", enabledAccounts.size());
-
-            // 构建账户ID到站点的映射
-            Map<Long, Site> siteMap = sites.stream()
-                    .collect(Collectors.toMap(Site::getPromotionAccountId, site -> site, (s1, s2) -> s1));
-
-            // 同步数据并统计成功数量
-            long successCount = enabledAccounts.stream()
-                    .filter(account -> {
-                        try {
-                            Site site = siteMap.get(account.getId());
-                            if (site == null) {
-                                log.warn("账户 {} 未关联站点,跳过同步", account.getAccountName());
-                                return false;
-                            }
-                            syncAccountData(account, site.getId(), batchNo);
-                            return true;
-                        } catch (Exception e) {
-                            log.error("同步账户 {} 数据失败", account.getAccountName(), e);
-                            return false;
-                        }
-                    })
-                    .count();
-
-            log.info("========== 数据同步任务完成,成功同步 {}/{} 个账户 ==========", successCount, enabledAccounts.size());
-        } catch (Exception e) {
-            log.error("数据同步任务执行失败", e);
-        }
+        statisticsService.syncData(batchNo,1);
     }
 
-    /**
-     * 同步账户数据
-     */
-    private void syncAccountData(PromotionAccount account, Long siteId, String batchNo) {
-        log.info("开始同步账户:{}", account.getAccountName());
 
-        // 查询现有统计记录
-        SiteStatistics existingStats = statisticsService.getOne(new LambdaQueryWrapper<SiteStatistics>()
-                .eq(SiteStatistics::getSiteId, siteId)
-                .eq(SiteStatistics::getStatDate, batchNo));
-
-        SiteStatistics siteStatistics;
-        Long existingId = existingStats != null ? existingStats.getId() : null;
-
-        // 从广告平台获取数据
-        if (Integer.valueOf(1).equals(account.getApiSwitch())) {
-            IApiClient apiClient = advertiserHandlerFactory.getApiClient(AdvertiserTypeEnum.getByCode(account.getAdvertiserId()));
-            try {
-                siteStatistics = apiClient.getDataReport(account, batchNo, batchNo);
-            } catch (Exception e) {
-                siteStatistics = new SiteStatistics();
-                log.error("获取账户数据失败:{} {}", account.getAccountName(), siteId, e);
-            }
-        } else {
-            siteStatistics = new SiteStatistics();
-        }
-
-        // 设置站点基础信息
-        siteStatistics.setSiteId(siteId);
-        siteStatistics.setStatDate(java.time.LocalDate.parse(batchNo));
-
-        // 聚合线索数据
-        aggregateLeadData(siteStatistics, siteId, batchNo);
-
-        // 计算比率
-        siteStatistics.calculateRates();
-
-        // 保存或更新
-        if (existingId != null) {
-            siteStatistics.setId(existingId);
-            statisticsService.updateById(siteStatistics);
-        } else {
-            statisticsService.save(siteStatistics);
-        }
-
-        log.info("账户 {} 同步完成", account.getAccountName());
-    }
-
-    /**
-     * 聚合线索数据到站点统计
-     */
-    private void aggregateLeadData(SiteStatistics stats, Long siteId, String batchNo) {
-        List<Lead> leads = leadService.list(new LambdaQueryWrapper<Lead>()
-                .eq(Lead::getSiteId, siteId)
-                .between(Lead::getCreateTime, batchNo + " 00:00:00", batchNo + " 23:59:59"));
-
-        if (CollUtil.isEmpty(leads)) {
-            return;
-        }
-
-        // 将多个Lead聚合为一个Lead
-        Lead aggregatedLead = leads.stream()
-                .reduce(new Lead(), (lead1, lead2) -> {
-                    Lead result = new Lead();
-                    result.setMiniAuth(lead1.getMiniAuth() + lead2.getMiniAuth());
-                    result.setMiniQrCodeIndex(lead1.getMiniQrCodeIndex() + lead2.getMiniQrCodeIndex());
-                    result.setMiniAuthIndex(lead1.getMiniAuthIndex() + lead2.getMiniAuthIndex());
-                    result.setMiniLaunchIndexCount(lead1.getMiniLaunchIndexCount() + lead2.getMiniLaunchIndexCount());
-                    result.setWechatDelete(lead1.getWechatDelete() + lead2.getWechatDelete());
-                    result.setAddContactQwGroup(lead1.getAddContactQwGroup() + lead2.getAddContactQwGroup());
-                    result.setAddContactQw(lead1.getAddContactQw() + lead2.getAddContactQw());
-                    result.setLandingPageTrigger(lead1.getLandingPageTrigger() + lead2.getLandingPageTrigger());
-                    return result;
-                });
-
-        // 直接用聚合后的Lead设置stats
-        stats.setMiniAuthCount(aggregatedLead.getMiniAuth());
-        stats.setMiniQrCodeIndexCount(aggregatedLead.getMiniQrCodeIndex());
-        stats.setMiniAuthIndexCount(aggregatedLead.getMiniAuthIndex());
-        stats.setMiniLaunchIndexCount(aggregatedLead.getMiniLaunchIndexCount());
-        stats.setWechatDeleteCount(aggregatedLead.getWechatDelete());
-        stats.setWechatGroupCount(aggregatedLead.getAddContactQwGroup());
-        stats.setWechatAddCount(aggregatedLead.getAddContactQw());
-        stats.setSysClickCount(aggregatedLead.getLandingPageTrigger());
-    }
 
 
 }

+ 19 - 0
fs-ad-new-api/src/main/java/com/fs/framework/config/CorsConfig.java

@@ -0,0 +1,19 @@
+package com.fs.framework.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class CorsConfig implements WebMvcConfigurer {
+
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**")
+                .allowedOrigins("*") // ★旧版本用这个
+                .allowedMethods("*")
+                .allowedHeaders("*")
+                .allowCredentials(false) // ★注意:allowedOrigins("*") 不能和 true 一起用
+                .maxAge(3600);
+    }
+}

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/newAdv/AdvChannelController.java

@@ -24,7 +24,7 @@ import java.util.List;
  * @since 2025-11-27
  */
 @RestController
-@RequestMapping("/channel")
+@RequestMapping("/adv/channel")
 public class AdvChannelController {
 
     @Autowired

+ 46 - 0
fs-company/src/main/java/com/fs/company/controller/newAdv/AdvConfigController.java

@@ -0,0 +1,46 @@
+package com.fs.company.controller.newAdv;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONUtil;
+import com.fs.common.result.Result;
+import com.fs.newAdv.constant.AdvConfigConstant;
+import com.fs.newAdv.dto.res.AdvConfigRes;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.service.ISysConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/adv/config")
+public class AdvConfigController {
+
+    @Autowired
+    private ISysConfigService sysConfigService;
+
+    /**
+     * 删除站点
+     */
+    @GetMapping("/detail")
+    public Result<AdvConfigRes> detail() {
+        return Result.success(JSONUtil.toBean(sysConfigService.selectConfigByKey(AdvConfigConstant.ADV_CONFIG), AdvConfigRes.class));
+    }
+
+    /**
+     * 新增更新配置
+     */
+    @PostMapping("/addOrUpdate")
+    public Result<Integer> addOrUpdate(@RequestBody AdvConfigRes advConfigRes) {
+        SysConfig sysConfig = sysConfigService.selectConfigByConfigKey(AdvConfigConstant.ADV_CONFIG);
+        int flag = 0;
+        if (ObjectUtil.isEmpty(sysConfig)) {
+            sysConfig = new SysConfig();
+            sysConfig.setConfigKey(AdvConfigConstant.ADV_CONFIG);
+            sysConfig.setConfigValue(JSONUtil.toJsonStr(advConfigRes));
+            flag = sysConfigService.insertConfig(sysConfig);
+        } else {
+            sysConfig.setConfigValue(JSONUtil.toJsonStr(advConfigRes));
+            flag = sysConfigService.updateConfig(sysConfig);
+        }
+        return Result.success(flag);
+    }
+}

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/newAdv/AdvProjectController.java

@@ -20,7 +20,7 @@ import org.springframework.web.bind.annotation.*;
  * @since 2025-11-27
  */
 @RestController
-@RequestMapping("/project")
+@RequestMapping("/adv/project")
 public class AdvProjectController {
 
     @Autowired

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/newAdv/AdvertiserController.java

@@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.*;
 @Slf4j
 @Validated
 @RestController
-@RequestMapping("/advertiser")
+@RequestMapping("/adv/advertiser")
 public class AdvertiserController {
 
     @Autowired

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/newAdv/CallbackAccountController.java

@@ -26,7 +26,7 @@ import java.util.List;
 @Slf4j
 @Validated
 @RestController
-@RequestMapping("/callback-account")
+@RequestMapping("/adv/callback-account")
 public class CallbackAccountController {
 
     @Autowired

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/newAdv/ConversionLogController.java

@@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.RestController;
 
 @Slf4j
 @RestController
-@RequestMapping("/conversion-log")
+@RequestMapping("/adv/conversion-log")
 @Validated
 public class ConversionLogController {
 

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/newAdv/DomainController.java

@@ -25,7 +25,7 @@ import java.util.List;
  */
 @Slf4j
 @RestController
-@RequestMapping("/domains")
+@RequestMapping("/adv/domains")
 @Validated
 public class DomainController {
 

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/newAdv/LandingPageTemplateController.java

@@ -34,7 +34,7 @@ import java.util.Objects;
  */
 @Slf4j
 @RestController
-@RequestMapping("/landing-page-templates")
+@RequestMapping("/adv/landing-page-templates")
 @Validated
 public class LandingPageTemplateController {
 

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/newAdv/PromotionAccountController.java

@@ -24,7 +24,7 @@ import javax.validation.constraints.NotNull;
 @Slf4j
 @Validated
 @RestController
-@RequestMapping("/promotion-account")
+@RequestMapping("/adv/promotion-account")
 public class PromotionAccountController {
 
     @Autowired

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/newAdv/SiteController.java

@@ -21,7 +21,7 @@ import java.util.List;
  */
 @Slf4j
 @RestController
-@RequestMapping("/site")
+@RequestMapping("/adv/site")
 public class SiteController {
 
     @Autowired

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/newAdv/StatisticsController.java

@@ -33,7 +33,7 @@ import java.util.stream.Collectors;
 @Slf4j
 @Validated
 @RestController
-@RequestMapping("/site-statistics")
+@RequestMapping("/adv/site-statistics")
 public class StatisticsController {
 
     @Autowired

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/newAdv/TrackingLinkController.java

@@ -25,7 +25,7 @@ import java.util.List;
 @Slf4j
 @Validated
 @RestController
-@RequestMapping("/tracking-link")
+@RequestMapping("/adv/tracking-link")
 public class TrackingLinkController {
 
     @Autowired

+ 8 - 0
fs-service/src/main/java/com/fs/newAdv/constant/AdvConfigConstant.java

@@ -0,0 +1,8 @@
+package com.fs.newAdv.constant;
+
+public class AdvConfigConstant {
+    /**
+     * 落地页回传topic
+     */
+    public static final String ADV_CONFIG = "adv-config";
+}

+ 44 - 0
fs-service/src/main/java/com/fs/newAdv/dto/res/AdvConfigRes.java

@@ -0,0 +1,44 @@
+package com.fs.newAdv.dto.res;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+@Data
+public class AdvConfigRes implements Serializable {
+    // 站点重复报名设置
+    private DuplicateRegistrationSettings duplicateRegistrationSettings;
+    // 全局熟客设置
+    private RegularCustomerSettings regularCustomerSettings;
+    // 重复回传设置
+    private RepeatPostbackSettings repeatPostbackSettings;
+
+    @Getter
+    @Setter
+    static class DuplicateRegistrationSettings {
+        // 报名逻辑 1学员可正常报名.2学员不可重复报名
+        private Integer signUpLogic;
+        // 处理逻辑的天数
+        private Integer day;
+    }
+
+    @Getter
+    @Setter
+    static class RegularCustomerSettings {
+        // 全局熟客开关 1开启 2关闭
+        private Integer repeatSwitch;
+        // 小程序表单/加粉站点判断微信授权熟客 1开启 2关闭
+        private Integer authSwitch;
+    }
+
+    @Getter
+    @Setter
+    static class RepeatPostbackSettings {
+        // 重复回传过滤 1开启 2关闭
+        private Integer filter;
+        // 过滤方式 1按行为过滤,2按手机号过滤
+        private Integer method;
+    }
+}

+ 1 - 1
fs-service/src/main/java/com/fs/newAdv/dto/res/LandingIndexRes.java

@@ -11,5 +11,5 @@ public class LandingIndexRes implements Serializable {
     // 模板数据
     private String templateData;
 
-
+    private String traceId;
 }

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/integration/adapter/BaiduAdapter.java → fs-service/src/main/java/com/fs/newAdv/integration/adapter/BaiduAdapter.java

@@ -1,4 +1,4 @@
-package com.fs.app.integration.adapter;
+package com.fs.newAdv.integration.adapter;
 
 import com.fs.newAdv.domain.Lead;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/integration/adapter/IAdvertiserAdapter.java → fs-service/src/main/java/com/fs/newAdv/integration/adapter/IAdvertiserAdapter.java

@@ -1,4 +1,4 @@
-package com.fs.app.integration.adapter;
+package com.fs.newAdv.integration.adapter;
 
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
 import com.fs.newAdv.domain.Lead;

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/integration/adapter/OPPOAdapter.java → fs-service/src/main/java/com/fs/newAdv/integration/adapter/OPPOAdapter.java

@@ -1,4 +1,4 @@
-package com.fs.app.integration.adapter;
+package com.fs.newAdv.integration.adapter;
 
 import cn.hutool.core.util.StrUtil;
 import com.fs.newAdv.domain.Lead;

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/integration/adapter/OceanEngineAdapter.java → fs-service/src/main/java/com/fs/newAdv/integration/adapter/OceanEngineAdapter.java

@@ -1,4 +1,4 @@
-package com.fs.app.integration.adapter;
+package com.fs.newAdv.integration.adapter;
 
 import cn.hutool.core.util.StrUtil;
 import com.fs.newAdv.domain.Lead;

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/integration/adapter/TencentAdapter.java → fs-service/src/main/java/com/fs/newAdv/integration/adapter/TencentAdapter.java

@@ -1,4 +1,4 @@
-package com.fs.app.integration.adapter;
+package com.fs.newAdv.integration.adapter;
 
 import cn.hutool.core.util.StrUtil;
 import com.fs.newAdv.domain.Lead;

+ 1 - 2
fs-ad-new-api/src/main/java/com/fs/app/integration/adapter/VIVOAdapter.java → fs-service/src/main/java/com/fs/newAdv/integration/adapter/VIVOAdapter.java

@@ -1,6 +1,5 @@
-package com.fs.app.integration.adapter;
+package com.fs.newAdv.integration.adapter;
 
-import cn.hutool.core.util.StrUtil;
 import com.fs.newAdv.domain.Lead;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
 import lombok.extern.slf4j.Slf4j;

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/integration/client/AbstractApiClient.java → fs-service/src/main/java/com/fs/newAdv/integration/client/AbstractApiClient.java

@@ -1,4 +1,4 @@
-package com.fs.app.integration.client;
+package com.fs.newAdv.integration.client;
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.HttpResponse;

+ 2 - 4
fs-ad-new-api/src/main/java/com/fs/app/integration/client/IAccessTokenClient.java → fs-service/src/main/java/com/fs/newAdv/integration/client/IAccessTokenClient.java

@@ -1,12 +1,10 @@
-package com.fs.app.integration.client;
+package com.fs.newAdv.integration.client;
 
 import com.fs.newAdv.vo.AccessTokenByAuthCodeVo;
 import com.fs.newAdv.vo.AccessTokenVo;
 
-import java.util.Map;
-
 public interface IAccessTokenClient extends IApiClient {
-    Map<String, String> refreshAccessToken(String appId, String appSecret, String refreshToken);
+    AccessTokenVo refreshAccessToken(String appId, String appSecret, String refreshToken);
 
     AccessTokenVo getAccessTokenByAuthCode(AccessTokenByAuthCodeVo codeVo);
 }

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/integration/client/IApiClient.java → fs-service/src/main/java/com/fs/newAdv/integration/client/IApiClient.java

@@ -1,4 +1,4 @@
-package com.fs.app.integration.client;
+package com.fs.newAdv.integration.client;
 
 import com.fs.newAdv.domain.PromotionAccount;
 import com.fs.newAdv.domain.Site;

+ 40 - 136
fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/BaiduApiClient.java → fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/BaiduApiClient.java

@@ -1,4 +1,4 @@
-package com.fs.app.integration.client.advertiser;
+package com.fs.newAdv.integration.client.advertiser;
 
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HttpRequest;
@@ -6,26 +6,19 @@ import cn.hutool.http.HttpResponse;
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
-import com.fs.ad.yk.utils.Md5Util;
-import com.fs.app.integration.client.AbstractApiClient;
-import com.fs.app.integration.client.IAccessTokenClient;
-import com.fs.baidu.utils.AESUtils;
+import com.fs.newAdv.integration.client.AbstractApiClient;
+import com.fs.newAdv.integration.client.IAccessTokenClient;
 import com.fs.common.constant.SystemConstant;
 import com.fs.common.exception.ThirdPartyException;
 import com.fs.newAdv.domain.PromotionAccount;
 import com.fs.newAdv.domain.SiteStatistics;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
-import com.fs.newAdv.service.IPromotionAccountService;
 import com.fs.newAdv.vo.AccessTokenByAuthCodeVo;
 import com.fs.newAdv.vo.AccessTokenVo;
-import com.google.gson.Gson;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import javax.crypto.SecretKey;
 import java.math.BigDecimal;
-import java.nio.charset.StandardCharsets;
 import java.time.LocalDateTime;
 import java.util.*;
 
@@ -46,19 +39,9 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
      */
     private static final String CONVERSION_API_URL = "https://ocpc.baidu.com/ocpcapi/api/uploadConvertData";
     private static final String REPORT_DATA_API_URL = "https://api.baidu.com/json/sms/service/OpenApiReportService/getReportData";
-
-
-    private static final String APPID = "appId";
-    private static final String AUTH_CODE = "authCode";
-    private static final String USERID = "userId";
-    private static final String TIMESTAMP = "timestamp";
-    private static final String SIGNATURE = "signature";
-    private static final String STATE = "state";
     private static final String ACCESSTOKEN_URL = "https://u.baidu.com/oauth/accessToken";
-    private static final String AES_OFFSET = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+    private static final String REFRESHTOKEN_URL = "https://u.baidu.com/oauth/refreshToken";
 
-    @Autowired
-    private IPromotionAccountService promotionAccountService;
 
     /**
      * 回传转化数据到百度
@@ -183,127 +166,38 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
 
 
     @Override
-    public Map<String, String> refreshAccessToken(String appId, String appSecret, String refreshToken) {
-        return Collections.emptyMap();
-    }
-
-    @Override
-    public AccessTokenVo getAccessTokenByAuthCode(AccessTokenByAuthCodeVo codeVo) {
+    public AccessTokenVo refreshAccessToken(String appId, String appSecret, String refreshToken) {
         // 调用接口换取授权令牌
-        Map<String, Object> response = getBaiDuAccessToken(codeVo);
-        log.info("callback:getAccesstoken, response={}", response);
-        String accessToken = null;
-        String refreshToken = null;
-
-        if (!response.isEmpty()) {
-            JSONObject res = new JSONObject(response);
-            int code = (int) res.get("code");
-            if (code == 0) {
-                JSONObject data = res.getJSONObject("data");
-                accessToken = (String) data.get("accessToken");
-                refreshToken = (String) data.get("refreshToken");
-                return AccessTokenVo.builder()
-                        .accessToken(accessToken)
-                        .refreshToken(refreshToken)
-                        .build();
-            }
+        Map<String, Object> requestMap = new HashMap<>();
+        requestMap.put("appId", appId);
+        requestMap.put("secretKey", appSecret);
+        requestMap.put("refreshToken", refreshToken);
+        // requestMap.put("userId", userId);
+        String paramsJson = JSONUtil.toJsonStr(requestMap);
+        HttpResponse execute = HttpRequest.post(REFRESHTOKEN_URL)
+                .header("Content-Type", "application/json")
+                .body(paramsJson)
+                .timeout(SystemConstant.API_TIMEOUT)
+                .execute();
+        JSONObject res = new JSONObject(execute.body());
+        int code = (int) res.get("code");
+        if (code == 0) {
+            JSONObject data = res.getJSONObject("data");
+            return AccessTokenVo.builder()
+                    .accessToken(data.getStr("accessToken"))
+                    .refreshToken(data.getStr("refreshToken"))
+                    .build();
         }
         return null;
     }
 
-    /**
-     * 填充请求参数,开发者可用实体代替map
-     *
-     * @param params
-     * @param request
-     */
-    private void fillParams(Map<String, String> params, AccessTokenByAuthCodeVo request) {
-        params.put(APPID, request.getAppId());
-        params.put(AUTH_CODE, request.getAuthCode());
-        params.put(USERID, request.getUserId());
-    }
-
-    /**
-     * 校验状态码是否符合预期
-     *
-     * @param appId  应用ID
-     * @param userId 创建应用的ID
-     * @param state  请求参数中的状态码
-     * @return
-     */
-    private boolean checkState(String appId, Long userId, String state) {
-        // md5util 开发者自行开发即可
-        String newState = Md5Util.MD5(appId.concat("_").concat(String.valueOf(userId)));
-        return newState.toLowerCase().equals(state);
-    }
-
-    /**
-     * 封装返回json串
-     *
-     * @param code
-     * @param message
-     * @param data
-     * @return
-     */
-    private String getResponseJson(int code, String message, Object data) {
-        // 封装返回字段的map
-        Map<String, Object> responseMap = new HashMap<>();
-        responseMap.put("code", code);
-        responseMap.put("message", message);
-        responseMap.put("data", data);
-        return org.json.JSONObject.valueToString(responseMap);
-    }
-
-    /**
-     * 签名验证方法
-     *
-     * @param params
-     * @param sk
-     * @return
-     */
-    private boolean checkSignature(Map<String, String> params, String sk) {
-        // 获取验签字符串:按key自然排序,拼接成 json 字符串,
-        // 示例:str1 = {"appId": xxxxx, "authCode": xxx, "state": xxx,"timestamp": xxx}
-        TreeMap<String, Object> map = new TreeMap<>();
-        map.put(APPID, params.get(APPID));
-        map.put(AUTH_CODE, params.get(AUTH_CODE));
-        map.put(USERID, params.get(USERID));
-        map.put(STATE, params.get(STATE));
-        map.put(TIMESTAMP, params.get(TIMESTAMP));
-        // 根据上述签名算法对接收到的参数签名
-        String sign = null;
-        try {
-            sign = paramsSign(sk, map);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-        log.info("callback: signature = {}", sign);
-        // 判断签名和URL传参签名是否一致
-        return params.get(SIGNATURE).equals(sign);
-    }
-
-    public String paramsSign(String secretKey, TreeMap<String, Object> map) throws Exception {
-
-        // 拼接成 json 字符串, 保证生成的json串字段顺序和Map中的顺序一致即可
-        String json = new Gson().toJson(map);        // base64编码
-        byte[] bytes = Base64.getEncoder()
-                .encodeToString(json.getBytes(StandardCharsets.UTF_8))
-                .getBytes(StandardCharsets.UTF_8);
-        // AES加密 密钥为 应用sk 的前16位字符
-        SecretKey keyAES = AESUtils.loadKeyAES(secretKey.substring(0, 16));
-        return AESUtils.encryptAES(bytes, keyAES, AES_OFFSET);
-    }
-
-    /**
-     * 换取授权令牌
-     *
-     * @return
-     */
-    private Map<String, Object> getBaiDuAccessToken(AccessTokenByAuthCodeVo codeVo) {
+    @Override
+    public AccessTokenVo getAccessTokenByAuthCode(AccessTokenByAuthCodeVo codeVo) {
+        // 调用接口换取授权令牌
         Map<String, Object> requestMap = new HashMap<>();
-        requestMap.put(APPID, codeVo.getAppId());
+        requestMap.put("appId", codeVo.getAppId());
         requestMap.put("secretKey", codeVo.getAppSecret());
-        requestMap.put(AUTH_CODE, codeVo.getAuthCode());
+        requestMap.put("authCode", codeVo.getAuthCode());
         requestMap.put("grantType", "access_token");
         requestMap.put("userId", codeVo.getUserId());
         String paramsJson = JSONUtil.toJsonStr(requestMap);
@@ -313,7 +207,17 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
                 .body(paramsJson)
                 .timeout(SystemConstant.API_TIMEOUT)
                 .execute();
-        return JSONUtil.parseObj(execute.body());
+
+        JSONObject res = new JSONObject(execute.body());
+        int code = (int) res.get("code");
+        if (code == 0) {
+            JSONObject data = res.getJSONObject("data");
+            return AccessTokenVo.builder()
+                    .accessToken(data.getStr("accessToken"))
+                    .refreshToken(data.getStr("refreshToken"))
+                    .build();
+        }
+        return null;
     }
 }
 

+ 2 - 3
fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/IQIYIApiClient.java → fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/IQIYIApiClient.java

@@ -1,10 +1,9 @@
-package com.fs.app.integration.client.advertiser;
+package com.fs.newAdv.integration.client.advertiser;
 
 import cn.hutool.http.HttpRequest;
-import com.fs.app.integration.client.AbstractApiClient;
+import com.fs.newAdv.integration.client.AbstractApiClient;
 import com.fs.common.constant.SystemConstant;
 import com.fs.newAdv.domain.PromotionAccount;
-import com.fs.newAdv.domain.Site;
 import com.fs.newAdv.domain.SiteStatistics;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
 import lombok.extern.slf4j.Slf4j;

+ 2 - 3
fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/OPPOApiClient.java → fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/OPPOApiClient.java

@@ -1,13 +1,12 @@
-package com.fs.app.integration.client.advertiser;
+package com.fs.newAdv.integration.client.advertiser;
 
 import cn.hutool.core.codec.Base64;
 import cn.hutool.http.HttpRequest;
 import cn.hutool.json.JSONUtil;
 import com.fs.newAdv.domain.PromotionAccount;
-import com.fs.newAdv.domain.Site;
 import com.fs.newAdv.domain.SiteStatistics;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
-import com.fs.app.integration.client.AbstractApiClient;
+import com.fs.newAdv.integration.client.AbstractApiClient;
 import com.fs.common.constant.SystemConstant;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.util.crypto.SHA1;

+ 28 - 8
fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/OceanEngineApiClient.java → fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/OceanEngineApiClient.java

@@ -1,16 +1,15 @@
-package com.fs.app.integration.client.advertiser;
+package com.fs.newAdv.integration.client.advertiser;
 
 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.integration.client.AbstractApiClient;
-import com.fs.app.integration.client.IAccessTokenClient;
+import com.fs.newAdv.integration.client.AbstractApiClient;
+import com.fs.newAdv.integration.client.IAccessTokenClient;
 import com.fs.common.constant.SystemConstant;
 import com.fs.common.exception.ThirdPartyException;
 import com.fs.newAdv.domain.PromotionAccount;
-import com.fs.newAdv.domain.Site;
 import com.fs.newAdv.domain.SiteStatistics;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
 import com.fs.newAdv.vo.AccessTokenByAuthCodeVo;
@@ -18,8 +17,6 @@ import com.fs.newAdv.vo.AccessTokenVo;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
-import javax.servlet.http.HttpServletRequest;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -43,6 +40,10 @@ public class OceanEngineApiClient extends AbstractApiClient implements IAccessTo
      * 获取Access Token
      */
     private static final String ACCESS_TOKEN_URL = "https://api.oceanengine.com/open_api/oauth2/access_token";
+    /**
+     * 刷新 Token
+     */
+    private static final String REFRESH_TOKEN_URL = "https://api.oceanengine.com/open_api/oauth2/refresh_token/";
 
     /**
      * 回传转化数据到巨量引擎
@@ -131,10 +132,29 @@ public class OceanEngineApiClient extends AbstractApiClient implements IAccessTo
     }
 
     @Override
-    public Map<String, String> refreshAccessToken(String appId, String appSecret, String refreshToken) {
-        return Collections.emptyMap();
+    public AccessTokenVo refreshAccessToken(String appId, String appSecret, String refreshToken) {
+        Map<String,Object> map = new HashMap<>();
+        map.put("app_id", appId);
+        map.put("secret", appSecret);
+        map.put("refresh_token", refreshToken);
+        HttpResponse response = HttpRequest.post(ACCESS_TOKEN_URL)
+                .header("Content-Type", "application/json")
+                .form(JSONUtil.toJsonStr(map))
+                .timeout(SystemConstant.API_TIMEOUT)
+                .execute();
+        JSONObject res = new JSONObject(response.body());
+        int code = (int) res.get("code");
+        if (code == 0) {
+            JSONObject data = res.getJSONObject("data");
+            return AccessTokenVo.builder()
+                    .accessToken((String) data.get("access_token"))
+                    .refreshToken((String) data.get("refresh_token"))
+                    .build();
+        }
+        return null;
     }
 
+
     @Override
     public AccessTokenVo getAccessTokenByAuthCode(AccessTokenByAuthCodeVo request) {
         Map<String,Object> map = new HashMap<>();

+ 38 - 38
fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/TencentApiClient.java → fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/TencentApiClient.java

@@ -1,23 +1,21 @@
-package com.fs.app.integration.client.advertiser;
+package com.fs.newAdv.integration.client.advertiser;
 
 import cn.hutool.http.HttpRequest;
 import cn.hutool.http.HttpResponse;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
+import com.fs.newAdv.integration.client.AbstractApiClient;
+import com.fs.newAdv.integration.client.IAccessTokenClient;
+import com.fs.common.constant.SystemConstant;
+import com.fs.common.utils.SnowflakeUtil;
 import com.fs.newAdv.domain.PromotionAccount;
-import com.fs.newAdv.domain.Site;
 import com.fs.newAdv.domain.SiteStatistics;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
-import com.fs.app.integration.client.AbstractApiClient;
-import com.fs.app.integration.client.IAccessTokenClient;
-import com.fs.common.constant.SystemConstant;
-import com.fs.common.utils.SnowflakeUtil;
 import com.fs.newAdv.vo.AccessTokenByAuthCodeVo;
 import com.fs.newAdv.vo.AccessTokenVo;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
-import javax.servlet.http.HttpServletRequest;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -35,6 +33,7 @@ import java.util.Map;
 public class TencentApiClient extends AbstractApiClient implements IAccessTokenClient {
 
     private static final String CONVERSION_API_URL = "https://api.e.qq.com/user_actions/add";
+    private static final String TOKEN_API_URL = "https://api.e.qq.com/oauth/token";
 
     /**
      * 回传转化数据
@@ -84,50 +83,51 @@ public class TencentApiClient extends AbstractApiClient implements IAccessTokenC
     }
 
     @Override
-    public SiteStatistics getDataReport(PromotionAccount account,String startDate,String endDate) {
+    public SiteStatistics getDataReport(PromotionAccount account, String startDate, String endDate) {
         return null;
     }
 
 
-    // @Override
-    public Map<String, String> getAccessToken(Integer code, String appId, String appSecret, String callbackUrl) {
-        // 发送HTTP请求
-        HttpResponse response = HttpRequest.post("CONVERSION_API_URL")
-                .form("client_id", appId)
-                .form("client_secret", appSecret)
-                .form("grant_type", "authorization_code")
-                .form("authorization_code", code)
-                .form("redirect_uri", callbackUrl)
-                .timeout(SystemConstant.API_TIMEOUT)
-                .execute();
-        String responseBody = response.body();
-        JSONObject jsonObject = JSONUtil.parseObj(responseBody);
-        Map<String, String> map = new HashMap<>();
-        map.put("access_token", jsonObject.getJSONObject("data").getStr("access_token"));
-        map.put("refresh_token", jsonObject.getJSONObject("data").getStr("refresh_token"));
-        return map;
-    }
-
-    @Override
-    public Map<String, String> refreshAccessToken(String appId, String appSecret, String refreshToken) {
-        // 发送HTTP请求
-        HttpResponse response = HttpRequest.post("CONVERSION_API_URL")
+    public AccessTokenVo refreshAccessToken(String appId, String appSecret, String refreshToken) {
+        HttpResponse response = HttpRequest.get(TOKEN_API_URL)
                 .form("client_id", appId)
                 .form("client_secret", appSecret)
                 .form("grant_type", "refresh_token")
                 .form("refresh_token", refreshToken)
                 .timeout(SystemConstant.API_TIMEOUT)
                 .execute();
-        String responseBody = response.body();
-        JSONObject jsonObject = JSONUtil.parseObj(responseBody);
-        Map<String, String> map = new HashMap<>();
-        map.put("access_token", jsonObject.getJSONObject("data").getStr("access_token"));
-        map.put("refresh_token", jsonObject.getJSONObject("data").getStr("refresh_token"));
-        return map;
+
+        JSONObject res = new JSONObject(response.body());
+        int code = (int) res.get("code");
+        if (code == 0) {
+            JSONObject data = res.getJSONObject("data");
+            return AccessTokenVo.builder()
+                    .accessToken(data.getStr("access_token"))
+                    .refreshToken(data.getStr("refresh_token"))
+                    .build();
+        }
+        return null;
     }
 
     @Override
-    public AccessTokenVo getAccessTokenByAuthCode(AccessTokenByAuthCodeVo request) {
+    public AccessTokenVo getAccessTokenByAuthCode(AccessTokenByAuthCodeVo codeVo) {
+        HttpResponse response = HttpRequest.get(TOKEN_API_URL)
+                .form("client_id", codeVo.getAppId())
+                .form("client_secret", codeVo.getAppSecret())
+                .form("grant_type", "authorization_code")
+                .form("authorization_code", codeVo.getAuthCode())
+                .form("redirect_uri", "authorization_code")
+                .timeout(SystemConstant.API_TIMEOUT)
+                .execute();
+        JSONObject res = new JSONObject(response.body());
+        int code = (int) res.get("code");
+        if (code == 0) {
+            JSONObject data = res.getJSONObject("data");
+            return AccessTokenVo.builder()
+                    .accessToken(data.getStr("access_token"))
+                    .refreshToken(data.getStr("refresh_token"))
+                    .build();
+        }
         return null;
     }
 }

+ 2 - 3
fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/VIVOApiClient.java → fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/VIVOApiClient.java

@@ -1,12 +1,11 @@
-package com.fs.app.integration.client.advertiser;
+package com.fs.newAdv.integration.client.advertiser;
 
 import cn.hutool.http.HttpRequest;
 import cn.hutool.json.JSONUtil;
-import com.fs.app.integration.client.AbstractApiClient;
+import com.fs.newAdv.integration.client.AbstractApiClient;
 import com.fs.common.constant.SystemConstant;
 import com.fs.common.utils.SnowflakeUtil;
 import com.fs.newAdv.domain.PromotionAccount;
-import com.fs.newAdv.domain.Site;
 import com.fs.newAdv.domain.SiteStatistics;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
 import lombok.extern.slf4j.Slf4j;

+ 3 - 3
fs-ad-new-api/src/main/java/com/fs/app/integration/factory/AdvertiserHandlerFactory.java → fs-service/src/main/java/com/fs/newAdv/integration/factory/AdvertiserHandlerFactory.java

@@ -1,9 +1,9 @@
-package com.fs.app.integration.factory;
+package com.fs.newAdv.integration.factory;
 
 
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
-import com.fs.app.integration.adapter.IAdvertiserAdapter;
-import com.fs.app.integration.client.IApiClient;
+import com.fs.newAdv.integration.adapter.IAdvertiserAdapter;
+import com.fs.newAdv.integration.client.IApiClient;
 import com.fs.common.exception.base.BusinessException;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;

+ 5 - 1
fs-service/src/main/java/com/fs/newAdv/service/ILeadService.java

@@ -46,6 +46,10 @@ public interface ILeadService extends IService<Lead> {
      */
     void updateAuthIndex(String traceId,Integer type);
 
-
+    /**
+     * 发起进入小程序
+     * @param traceId
+     */
+    void updateAuthIndex(String traceId);
 }
 

+ 1 - 0
fs-service/src/main/java/com/fs/newAdv/service/ISiteStatisticsService.java

@@ -6,5 +6,6 @@ import com.fs.newAdv.domain.SiteStatistics;
 
 public interface ISiteStatisticsService extends IService<SiteStatistics> {
 
+    void syncData(String batchNo,Integer type);
 }
 

+ 27 - 6
fs-service/src/main/java/com/fs/newAdv/service/impl/LeadServiceImpl.java

@@ -9,6 +9,8 @@ import com.fs.newAdv.enums.SystemEventTypeEnum;
 import com.fs.newAdv.event.ConversionEventPublisher;
 import com.fs.newAdv.mapper.LeadMapper;
 import com.fs.newAdv.service.ILeadService;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qwApi.domain.QwExternalContactResult;
 import com.fs.qwApi.service.QwApiService;
 import lombok.extern.slf4j.Slf4j;
@@ -34,7 +36,9 @@ public class LeadServiceImpl extends ServiceImpl<LeadMapper, Lead> implements IL
     private ConversionEventPublisher conversionEventPublisher;
 
     @Autowired
-    QwApiService qwApiService;
+    private QwApiService qwApiService;
+    @Autowired
+    private QwExternalContactMapper qwExternalContactMapper;
 
     @Override
     public Lead getByTraceId(String traceId) {
@@ -89,12 +93,22 @@ public class LeadServiceImpl extends ServiceImpl<LeadMapper, Lead> implements IL
             lead.setAddContactQwGroup(1);
             this.updateById(lead);
 
+            // 绑定企微用户线索关系
+            QwExternalContact qwExternalContact = qwExternalContactMapper.selectQwExternalByExternalIdAndCompanyIdToIdAndFs(externalUserID, userID, corpId);
+            if (qwExternalContact != null){
+                QwExternalContact temp = new QwExternalContact();
+                temp.setId(qwExternalContact.getId());
+                temp.setTraceId(lead.getTraceId());
+                qwExternalContactMapper.updateById(temp);
+            }else {
+                log.info("广告归因企微用户信息不存在:{} {} {} ", externalUserID, userID,corpId);
+            }
             if (ObjectUtil.isNotEmpty(lead.getLandingPageTs()) && lead.getLandingPageTs().toLocalDate().isEqual(LocalDate.now())) {
                 // 当日加微事件回调
                 conversionEventPublisher.publishConversionEvent(lead.getTraceId(), SystemEventTypeEnum.WEI_CHAT_TODAY);
             }
         } else {
-            log.info("用户加微线索信息不存在:{} {}", externalUserID, userID);
+            log.info("广告归因线索不存在:{} {} {}", externalUserID, userID,corpId);
         }
 
     }
@@ -106,7 +120,7 @@ public class LeadServiceImpl extends ServiceImpl<LeadMapper, Lead> implements IL
         if (byTraceId == null) {
             return;
         }
-        boolean update = this.update(new LambdaUpdateWrapper<Lead>()
+        this.update(new LambdaUpdateWrapper<Lead>()
                 .eq(Lead::getTraceId, traceId)
                 .set(Lead::getUnionid, unionId)
                 .set(Lead::getPhone, phone)
@@ -116,9 +130,6 @@ public class LeadServiceImpl extends ServiceImpl<LeadMapper, Lead> implements IL
             // 微信授权且当日创建事件
             conversionEventPublisher.publishConversionEvent(traceId, SystemEventTypeEnum.AUTH_TODAY_CREATE);
         }
-        if (!update) {
-            log.error("微信授权线索信息不存在:{}", traceId);
-        }
     }
 
     @Override
@@ -135,5 +146,15 @@ public class LeadServiceImpl extends ServiceImpl<LeadMapper, Lead> implements IL
             log.error("更新授权页标识失败:{}", traceId);
         }
     }
+
+    @Override
+    public void updateAuthIndex(String traceId) {
+        boolean update = this.update(new LambdaUpdateWrapper<Lead>()
+                .eq(Lead::getTraceId, traceId)
+                .set(Lead::getMiniLaunchIndexCount, 1));
+        if (!update){
+            log.error("发起进入小程序失败:{}", traceId);
+        }
+    }
 }
 

+ 165 - 0
fs-service/src/main/java/com/fs/newAdv/service/impl/SiteStatisticsServiceImpl.java

@@ -1,12 +1,28 @@
 package com.fs.newAdv.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.newAdv.domain.Lead;
+import com.fs.newAdv.domain.PromotionAccount;
+import com.fs.newAdv.domain.Site;
 import com.fs.newAdv.domain.SiteStatistics;
+import com.fs.newAdv.enums.AdvertiserTypeEnum;
+import com.fs.newAdv.integration.client.IApiClient;
+import com.fs.newAdv.integration.factory.AdvertiserHandlerFactory;
 import com.fs.newAdv.mapper.SiteStatisticsMapper;
+import com.fs.newAdv.service.ILeadService;
+import com.fs.newAdv.service.IPromotionAccountService;
+import com.fs.newAdv.service.ISiteService;
 import com.fs.newAdv.service.ISiteStatisticsService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
 /**
  * 域名管理Service实现类
  *
@@ -17,5 +33,154 @@ import org.springframework.stereotype.Service;
 @Service
 public class SiteStatisticsServiceImpl extends ServiceImpl<SiteStatisticsMapper, SiteStatistics> implements ISiteStatisticsService {
 
+    @Autowired
+    private ISiteService siteService;
+    @Autowired
+    private IPromotionAccountService promotionAccountService;
+    @Autowired
+    private AdvertiserHandlerFactory advertiserHandlerFactory;
+    @Autowired
+    private ILeadService leadService;
+
+    @Override
+    public void syncData(String batchNo, Integer type) {
+        log.info("========== 数据同步任务开始,批次号:{} ==========", batchNo);
+        try {
+            List<Site> sites = siteService.selectEnabledSite();
+            if (CollUtil.isEmpty(sites)) {
+                log.info("没有启用的站点,跳过同步");
+                return;
+            }
+
+            // 查询所有API账户并过滤启用的
+            List<Long> accountIds = sites.stream()
+                    .map(Site::getPromotionAccountId)
+                    .distinct()
+                    .collect(Collectors.toList());
+
+            List<PromotionAccount> enabledAccounts = promotionAccountService.listByIds(accountIds).stream()
+                    .filter(account -> Integer.valueOf(1).equals(account.getApiSwitch()))
+                    .collect(Collectors.toList());
+
+            if (enabledAccounts.isEmpty()) {
+                log.info("没有启用API的账户,跳过同步");
+                return;
+            }
+
+            log.info("查询到 {} 个需要同步的账户", enabledAccounts.size());
+
+            // 构建账户ID到站点的映射
+            Map<Long, Site> siteMap = sites.stream()
+                    .collect(Collectors.toMap(Site::getPromotionAccountId, site -> site, (s1, s2) -> s1));
+
+            // 同步数据并统计成功数量
+            long successCount = enabledAccounts.stream()
+                    .filter(account -> {
+                        try {
+                            Site site = siteMap.get(account.getId());
+                            if (site == null) {
+                                log.warn("账户 {} 未关联站点,跳过同步", account.getAccountName());
+                                return false;
+                            }
+                            syncAccountData(account, site.getId(), batchNo);
+                            return true;
+                        } catch (Exception e) {
+                            log.error("同步账户 {} 数据失败", account.getAccountName(), e);
+                            return false;
+                        }
+                    })
+                    .count();
+
+            log.info("========== 数据同步任务完成,成功同步 {}/{} 个账户 ==========", successCount, enabledAccounts.size());
+        } catch (Exception e) {
+            log.error("数据同步任务执行失败", e);
+        }
+    }
+
+    /**
+     * 同步账户数据
+     */
+    private void syncAccountData(PromotionAccount account, Long siteId, String batchNo) {
+        log.info("开始同步账户:{}", account.getAccountName());
+
+        // 查询现有统计记录
+        SiteStatistics existingStats = this.getOne(new LambdaQueryWrapper<SiteStatistics>()
+                .eq(SiteStatistics::getSiteId, siteId)
+                .eq(SiteStatistics::getStatDate, batchNo));
+
+        SiteStatistics siteStatistics;
+        Long existingId = existingStats != null ? existingStats.getId() : null;
+
+        // 从广告平台获取数据
+        if (Integer.valueOf(1).equals(account.getApiSwitch())) {
+            IApiClient apiClient = advertiserHandlerFactory.getApiClient(AdvertiserTypeEnum.getByCode(account.getAdvertiserId()));
+            try {
+                siteStatistics = apiClient.getDataReport(account, batchNo, batchNo);
+            } catch (Exception e) {
+                siteStatistics = new SiteStatistics();
+                log.error("获取账户数据失败:{} {}", account.getAccountName(), siteId, e);
+            }
+        } else {
+            siteStatistics = new SiteStatistics();
+        }
+
+        // 设置站点基础信息
+        siteStatistics.setSiteId(siteId);
+        siteStatistics.setStatDate(java.time.LocalDate.parse(batchNo));
+
+        // 聚合线索数据
+        aggregateLeadData(siteStatistics, siteId, batchNo);
+
+        // 计算比率
+        siteStatistics.calculateRates();
+
+        // 保存或更新
+        if (existingId != null) {
+            siteStatistics.setId(existingId);
+            this.updateById(siteStatistics);
+        } else {
+            this.save(siteStatistics);
+        }
+
+        log.info("账户 {} 同步完成", account.getAccountName());
+    }
+
+    /**
+     * 聚合线索数据到站点统计
+     */
+    private void aggregateLeadData(SiteStatistics stats, Long siteId, String batchNo) {
+        List<Lead> leads = leadService.list(new LambdaQueryWrapper<Lead>()
+                .eq(Lead::getSiteId, siteId)
+                .between(Lead::getCreateTime, batchNo + " 00:00:00", batchNo + " 23:59:59"));
+
+        if (CollUtil.isEmpty(leads)) {
+            return;
+        }
+
+        // 将多个Lead聚合为一个Lead
+        Lead aggregatedLead = leads.stream()
+                .reduce(new Lead(), (lead1, lead2) -> {
+                    Lead result = new Lead();
+                    result.setMiniAuth(lead1.getMiniAuth() + lead2.getMiniAuth());
+                    result.setMiniQrCodeIndex(lead1.getMiniQrCodeIndex() + lead2.getMiniQrCodeIndex());
+                    result.setMiniAuthIndex(lead1.getMiniAuthIndex() + lead2.getMiniAuthIndex());
+                    result.setMiniLaunchIndexCount(lead1.getMiniLaunchIndexCount() + lead2.getMiniLaunchIndexCount());
+                    result.setWechatDelete(lead1.getWechatDelete() + lead2.getWechatDelete());
+                    result.setAddContactQwGroup(lead1.getAddContactQwGroup() + lead2.getAddContactQwGroup());
+                    result.setAddContactQw(lead1.getAddContactQw() + lead2.getAddContactQw());
+                    result.setLandingPageTrigger(lead1.getLandingPageTrigger() + lead2.getLandingPageTrigger());
+                    return result;
+                });
+
+        // 直接用聚合后的Lead设置stats
+        stats.setMiniAuthCount(aggregatedLead.getMiniAuth());
+        stats.setMiniQrCodeIndexCount(aggregatedLead.getMiniQrCodeIndex());
+        stats.setMiniAuthIndexCount(aggregatedLead.getMiniAuthIndex());
+        stats.setMiniLaunchIndexCount(aggregatedLead.getMiniLaunchIndexCount());
+        stats.setWechatDeleteCount(aggregatedLead.getWechatDelete());
+        stats.setWechatGroupCount(aggregatedLead.getAddContactQwGroup());
+        stats.setWechatAddCount(aggregatedLead.getAddContactQw());
+        stats.setSysClickCount(aggregatedLead.getLandingPageTrigger());
+    }
 }
 

+ 2 - 0
fs-service/src/main/java/com/fs/qw/domain/QwExternalContact.java

@@ -150,5 +150,7 @@ public class QwExternalContact extends BaseEntity
 
     // 是否被邀请进群0否1是
     private Integer joinGroup;
+    // 广告链路唯一id
+    private String traceId;
 
 }