浏览代码

coding:投流代码提交

zhangqin 3 天之前
父节点
当前提交
d37349a1c2
共有 19 个文件被更改,包括 557 次插入217 次删除
  1. 90 9
      fs-ad-new-api/src/main/java/com/fs/app/controller/CallbackController.java
  2. 1 31
      fs-ad-new-api/src/main/java/com/fs/app/controller/TestController.java
  3. 4 2
      fs-ad-new-api/src/main/java/com/fs/app/integration/client/IAccessTokenClient.java
  4. 1 1
      fs-ad-new-api/src/main/java/com/fs/app/integration/client/IApiClient.java
  5. 32 75
      fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/BaiduApiClient.java
  6. 1 1
      fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/IQIYIApiClient.java
  7. 1 1
      fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/OPPOApiClient.java
  8. 41 2
      fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/OceanEngineApiClient.java
  9. 5 3
      fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/TencentApiClient.java
  10. 1 1
      fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/VIVOApiClient.java
  11. 135 26
      fs-ad-new-api/src/main/java/com/fs/app/task/DataSyncTask.java
  12. 17 0
      fs-company/src/main/java/com/fs/company/controller/newAdv/PromotionAccountController.java
  13. 22 0
      fs-company/src/main/java/com/fs/company/controller/newAdv/SiteController.java
  14. 78 8
      fs-company/src/main/java/com/fs/company/controller/newAdv/StatisticsController.java
  15. 11 1
      fs-service/src/main/java/com/fs/newAdv/domain/Lead.java
  16. 84 55
      fs-service/src/main/java/com/fs/newAdv/domain/SiteStatistics.java
  17. 1 1
      fs-service/src/main/java/com/fs/newAdv/service/impl/SiteServiceImpl.java
  18. 19 0
      fs-service/src/main/java/com/fs/newAdv/vo/AccessTokenByAuthCodeVo.java
  19. 13 0
      fs-service/src/main/java/com/fs/newAdv/vo/AccessTokenVo.java

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

@@ -1,15 +1,19 @@
 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.common.result.Result;
+import com.fs.newAdv.domain.PromotionAccount;
 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 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;
 
 import javax.servlet.http.HttpServletRequest;
@@ -27,19 +31,96 @@ public class CallbackController {
     @Autowired
     private IPromotionAccountService promotionAccountService;
 
+    /**
+     * 百度回调接口
+     *
+     * @param request
+     * @return
+     */
+    @GetMapping("/baidu/getAuthCode")
+    public Result<String> getBaiduAuthCode(HttpServletRequest request) {
+        getAccessToken(AdvertiserTypeEnum.BAIDU, request.getParameter("state"), request.getParameter("authCode"));
+        return Result.success("");
+    }
+
+    /**
+     * 巨量回调接口
+     *
+     * @param request
+     * @return
+     */
+    @GetMapping("/oceanEngine/getAuthCode")
+    public Result<String> getOceanEngineAuthCode(HttpServletRequest request) {
+        getAccessToken(AdvertiserTypeEnum.OCEANENGINE, request.getParameter("state"), request.getParameter("auth_code"));
+        return Result.success("");
+    }
 
+    /**
+     * 腾讯回调接口
+     *
+     * @param request
+     * @return
+     */
     @GetMapping("/tencent/getAuthCode")
-    public void getTencentAuthCode(
-            @RequestParam("authorization_code") Integer code,
-            @RequestParam("state") Long state) {
+    public Result<String> getTencentTAuthCode(HttpServletRequest request) {
+        getAccessToken(AdvertiserTypeEnum.OCEANENGINE, request.getParameter("state"), request.getParameter("auth_code"));
+        return Result.success("");
     }
 
-    @GetMapping("/baidu/getAuthCode")
-    public String getBaiduAuthCode(HttpServletRequest request) {
+    /**
+     * OPPO回调接口
+     *
+     * @param request
+     * @return
+     */
+    @GetMapping("/oppo/getAuthCode")
+    public Result<String> getOppoAuthCode(HttpServletRequest request) {
+        getAccessToken(AdvertiserTypeEnum.OCEANENGINE, request.getParameter("state"), request.getParameter("auth_code"));
+        return Result.success("");
+    }
+
+    /**
+     * vivo回调接口
+     *
+     * @param request
+     * @return
+     */
+    @GetMapping("/vivo/getAuthCode")
+    public Result<String> getViVoAuthCode(HttpServletRequest request) {
+        getAccessToken(AdvertiserTypeEnum.OCEANENGINE, request.getParameter("state"), request.getParameter("auth_code"));
+        return Result.success("");
+    }
+
+    /**
+     * iqiyi回调接口
+     *
+     * @param request
+     * @return
+     */
+    @GetMapping("/iqiyi/getAuthCode")
+    public Result<String> getiqiyiAuthCode(HttpServletRequest request) {
+        getAccessToken(AdvertiserTypeEnum.OCEANENGINE, request.getParameter("state"), request.getParameter("auth_code"));
+        return Result.success("");
+    }
+
+    private void getAccessToken(AdvertiserTypeEnum advertiserType, String state, String authCode) {
+        PromotionAccount byId = promotionAccountService.getById(state);
         // 获取请求参数
-        IApiClient apiClient = advertiserHandlerFactory.getApiClient(AdvertiserTypeEnum.BAIDU);
+        IApiClient apiClient = advertiserHandlerFactory.getApiClient(advertiserType);
         IAccessTokenClient tokenClient = (IAccessTokenClient) apiClient;
-        return tokenClient.getAccessTokenByAuthCode(request);
+        AccessTokenVo accessToken = tokenClient.getAccessTokenByAuthCode(AccessTokenByAuthCodeVo
+                .builder()
+                .userId(byId.getAdAccountId())
+                .appId(byId.getAppId())
+                .authCode(authCode)
+                .appSecret(byId.getAppSecret())
+                .build());
+        if (ObjectUtil.isNotEmpty(accessToken)) {
+            byId.setAccessToken(accessToken.getAccessToken());
+            byId.setRefreshToken(accessToken.getRefreshToken());
+            promotionAccountService.updateById(byId);
+        } else {
+            log.error("获取accessToken失败:{}", byId.getId());
+        }
     }
-
 }

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

@@ -37,36 +37,6 @@ public class TestController {
     @GetMapping("/test2/{traceId}")
     public void test2(@PathVariable("traceId") String traceId) {
         log.info("模拟 当日加微 事件完成");
-        conversionEventPublisher.publishConversionEvent(traceId, SystemEventTypeEnum.WEICHAT_TODAY);
-    }
-
-    @GetMapping("/submit/{traceId}")
-    public void test3(@PathVariable("traceId") String traceId) {
-        log.info("模拟 直播到课加群 事件完成");
-        conversionEventPublisher.publishConversionEvent(traceId, SystemEventTypeEnum.TO_CLASS_AND_GROUP_TODAY);
-    }
-
-    @GetMapping("/test4/{traceId}")
-    public void test4(@PathVariable("traceId") String traceId) {
-        log.info("模拟 直播到课加微 事件完成");
-        conversionEventPublisher.publishConversionEvent(traceId, SystemEventTypeEnum.TO_CLASS_AND_WEICHAT_TODAY);
-    }
-
-    @GetMapping("/test5/{traceId}")
-    public void test5(@PathVariable("traceId") String traceId) {
-        log.info("模拟 商品购买订单 事件完成");
-        conversionEventPublisher.publishConversionEvent(traceId, SystemEventTypeEnum.BUY_ORDER);
-    }
-
-    @GetMapping("/test6/{traceId}")
-    public void test6(@PathVariable("traceId") String traceId) {
-        log.info("模拟微信搜权且当日创建事件完成");
-        conversionEventPublisher.publishConversionEvent(traceId, SystemEventTypeEnum.AUTH_TODAY_CREATE);
-    }
-
-    @GetMapping("/test7/{id}")
-    public void test7(@PathVariable("id") String traceId) {
-        log.info("模拟微信搜权且当日创建事件完成");
-        // baiduApiClient.getDataReport(promotionAccountService.getById(traceId));
+        conversionEventPublisher.publishConversionEvent(traceId, SystemEventTypeEnum.WEI_CHAT_TODAY);
     }
 }

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

@@ -1,10 +1,12 @@
 package com.fs.app.integration.client;
 
-import javax.servlet.http.HttpServletRequest;
+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);
 
-    String getAccessTokenByAuthCode(HttpServletRequest request);
+    AccessTokenVo getAccessTokenByAuthCode(AccessTokenByAuthCodeVo codeVo);
 }

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

@@ -20,6 +20,6 @@ public interface IApiClient {
 
     AdvertiserTypeEnum getAdvertiserType();
 
-    SiteStatistics getDataReport(PromotionAccount account, Site site);
+    SiteStatistics getDataReport(PromotionAccount account,String startDate,String endDate);
 }
 

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

@@ -13,18 +13,17 @@ import com.fs.baidu.utils.AESUtils;
 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.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 javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
 import java.time.LocalDateTime;
@@ -136,14 +135,14 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
     }
 
     @Override
-    public SiteStatistics getDataReport(PromotionAccount account, Site site) {
+    public SiteStatistics getDataReport(PromotionAccount account, String startDate, String endDate) {
         // 构建请求参数
-        Map<String,Object> map = new HashMap<>();
-        Map<String,Object> header = new HashMap<>();
+        Map<String, Object> map = new HashMap<>();
+        Map<String, Object> header = new HashMap<>();
         header.put("accessToken", account.getAccessToken());
-        header.put("userName", site.getPromotionAccountName());
-        map.put("header",header);
-        Map<String,Object> body = new HashMap<>();
+        header.put("userName", "BDCC-yyt19");
+        map.put("header", header);
+        Map<String, Object> body = new HashMap<>();
         // 基础信息
         //报告名称: 信息流整体账户报告
         //reportType: 2172649
@@ -152,15 +151,15 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
         body.put("reportType", 2172649);
         // 查询当天的数据
         LocalDateTime yesterday = LocalDateTime.now().minusDays(1);
-        body.put("startDate", cn.hutool.core.date.DateUtil.format(yesterday, "yyyy-MM-dd"));
-        body.put("endDate", cn.hutool.core.date.DateUtil.format(LocalDateTime.now(), "yyyy-MM-dd"));
+        body.put("startDate", startDate);
+        body.put("endDate", endDate);
         body.put("timeUnit", "DAY");
         body.put("userIds", Collections.singletonList(account.getAdAccountId()));
         // 基础指标
-        body.put("columns", Arrays.asList("impression", "click", "cost", "ctr", "cpc","cpm","phoneButtonClicks"));
+        body.put("columns", Arrays.asList("impression", "click", "cost", "ctr", "cpc", "cpm", "phoneButtonClicks"));
         body.put("startRow", 0);
         body.put("rowCount", 1000);
-        map.put("body",body);
+        map.put("body", body);
 
         HttpResponse execute = HttpRequest.post(REPORT_DATA_API_URL)
                 .header("Content-Type", "application/json")
@@ -168,13 +167,13 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
                 .timeout(SystemConstant.API_TIMEOUT)
                 .execute();
         JSONObject jsonObject = JSONUtil.parseObj(execute.body());
+        log.info("百度数据返回结果:{}", execute.body());
         JSONObject data = jsonObject.getJSONObject("body").getJSONObject("data");
         JSONArray rows = data.getJSONArray("rows");
         JSONObject jsonObject1 = rows.getJSONObject(0);
         SiteStatistics siteStatistics = new SiteStatistics();
-        siteStatistics.setSiteId(site.getId());
-        siteStatistics.setImpressionCount(Long.valueOf(jsonObject1.getStr("impression")));
-        siteStatistics.setClickCount(Long.valueOf(jsonObject1.getStr("click")));
+        siteStatistics.setImpressionCount(Integer.valueOf(jsonObject1.getStr("impression")));
+        siteStatistics.setClickCount(Integer.valueOf(jsonObject1.getStr("click")));
         siteStatistics.setActualCost(new BigDecimal(jsonObject1.getStr("cost")));
         siteStatistics.setAccountCost(new BigDecimal(jsonObject1.getStr("cost")));
         siteStatistics.setClickRate(new BigDecimal(jsonObject1.getStr("ctr")));
@@ -183,48 +182,15 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
     }
 
 
-
     @Override
     public Map<String, String> refreshAccessToken(String appId, String appSecret, String refreshToken) {
         return Collections.emptyMap();
     }
 
     @Override
-    public String getAccessTokenByAuthCode(HttpServletRequest request) {
-
-        // 获取请求参数
-        Map<String, String> params = new HashMap<>();
-        this.fillParams(params, request);
-        log.info("callback: params = {}", org.json.JSONObject.valueToString(params));
-        int userIdInt = 0;
-        try {
-            userIdInt = Integer.parseInt(params.get(USERID));
-        } catch (Exception e) {
-            return this.getResponseJson(600011, "参数错误", new Object());
-        }
-        // 对签名内容进行判空
-        if (StrUtil.isBlank(params.get(SIGNATURE))) {
-            return this.getResponseJson(600011, "参数错误", new Object());
-        }
-
-        // 获取应用对应的密钥
-        PromotionAccount app = promotionAccountService.getAppByAppId(params.get(APPID), AdvertiserTypeEnum.BAIDU);
-        String sk = app.getAppSecret();
-        // 开发者进行验签
-        // 检查状态码
-        if (!this.checkState(params.get(APPID), Long.valueOf(app.getAdAccountId()), params.get(STATE))) {
-            log.info("callback: state check fail");
-            return this.getResponseJson(600011, "状态码错误", new Object());
-        }
-
-        // 签名验证
-        if (!this.checkSignature(params, sk)) {
-            log.info("callback: signature check fail");
-            return this.getResponseJson(600011, "签名错误", new Object());
-        }
-
+    public AccessTokenVo getAccessTokenByAuthCode(AccessTokenByAuthCodeVo codeVo) {
         // 调用接口换取授权令牌
-        Map<String, Object> response = getBaiDuAccessToken(params, sk, userIdInt);
+        Map<String, Object> response = getBaiDuAccessToken(codeVo);
         log.info("callback:getAccesstoken, response={}", response);
         String accessToken = null;
         String refreshToken = null;
@@ -236,16 +202,13 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
                 JSONObject data = res.getJSONObject("data");
                 accessToken = (String) data.get("accessToken");
                 refreshToken = (String) data.get("refreshToken");
-                app.setAccessToken(accessToken);
-                app.setRefreshToken(refreshToken);
-                promotionAccountService.updateById(app);
-            } else {
-                return this.getResponseJson(600011, "未获取到 access_token", new Object());
+                return AccessTokenVo.builder()
+                        .accessToken(accessToken)
+                        .refreshToken(refreshToken)
+                        .build();
             }
         }
-        Map<String, String> data = new HashMap<>();
-        data.put("accessToken", accessToken);
-        return this.getResponseJson(0, "success", data);
+        return null;
     }
 
     /**
@@ -254,13 +217,10 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
      * @param params
      * @param request
      */
-    private void fillParams(Map<String, String> params, HttpServletRequest request) {
-        params.put(APPID, request.getParameter(APPID));
-        params.put(AUTH_CODE, request.getParameter(AUTH_CODE));
-        params.put(USERID, request.getParameter(USERID));
-        params.put(TIMESTAMP, request.getParameter(TIMESTAMP));
-        params.put(SIGNATURE, request.getParameter(SIGNATURE));
-        params.put(STATE, request.getParameter(STATE));
+    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());
     }
 
     /**
@@ -337,19 +297,16 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
     /**
      * 换取授权令牌
      *
-     * @param params
-     * @param sk
-     * @param userIdInt 授权账户ID
      * @return
      */
-    private Map<String, Object> getBaiDuAccessToken(Map<String, String> params, String sk, int userIdInt) {
+    private Map<String, Object> getBaiDuAccessToken(AccessTokenByAuthCodeVo codeVo) {
         Map<String, Object> requestMap = new HashMap<>();
-        requestMap.put(APPID, params.get(APPID));
-        requestMap.put("secretKey", sk);
-        requestMap.put(AUTH_CODE, params.get(AUTH_CODE));
+        requestMap.put(APPID, codeVo.getAppId());
+        requestMap.put("secretKey", codeVo.getAppSecret());
+        requestMap.put(AUTH_CODE, codeVo.getAuthCode());
         requestMap.put("grantType", "access_token");
-        requestMap.put("userId", userIdInt);
-        String paramsJson = org.json.JSONObject.valueToString(requestMap);
+        requestMap.put("userId", codeVo.getUserId());
+        String paramsJson = JSONUtil.toJsonStr(requestMap);
 
         HttpResponse execute = HttpRequest.post(ACCESSTOKEN_URL)
                 .header("Content-Type", "application/json")

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/IQIYIApiClient.java

@@ -71,7 +71,7 @@ public class IQIYIApiClient extends AbstractApiClient {
     }
 
     @Override
-    public SiteStatistics getDataReport(PromotionAccount account, Site site) {
+    public SiteStatistics getDataReport(PromotionAccount account,String startDate,String endDate) {
         return null;
     }
 }

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/OPPOApiClient.java

@@ -93,7 +93,7 @@ public class OPPOApiClient extends AbstractApiClient {
     }
 
     @Override
-    public SiteStatistics getDataReport(PromotionAccount account, Site site) {
+    public SiteStatistics getDataReport(PromotionAccount account,String startDate,String endDate) {
         return null;
     }
 }

+ 41 - 2
fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/OceanEngineApiClient.java

@@ -2,17 +2,24 @@ package com.fs.app.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.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;
+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;
 
@@ -26,12 +33,16 @@ import java.util.Map;
  */
 @Slf4j
 @Component
-public class OceanEngineApiClient extends AbstractApiClient {
+public class OceanEngineApiClient extends AbstractApiClient implements IAccessTokenClient {
 
     /**
      * 巨量引擎转化回传API地址
      */
     private static final String CONVERSION_API_URL = "https://ad.oceanengine.com/track/activate";
+    /**
+     * 获取Access Token
+     */
+    private static final String ACCESS_TOKEN_URL = "https://api.oceanengine.com/open_api/oauth2/access_token";
 
     /**
      * 回传转化数据到巨量引擎
@@ -115,7 +126,35 @@ public class OceanEngineApiClient extends AbstractApiClient {
     }
 
     @Override
-    public SiteStatistics getDataReport(PromotionAccount account, Site site) {
+    public SiteStatistics getDataReport(PromotionAccount account,String startDate,String endDate) {
+        return null;
+    }
+
+    @Override
+    public Map<String, String> refreshAccessToken(String appId, String appSecret, String refreshToken) {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public AccessTokenVo getAccessTokenByAuthCode(AccessTokenByAuthCodeVo request) {
+        Map<String,Object> map = new HashMap<>();
+        map.put("app_id", request.getAppId());
+        map.put("secret", request.getAppSecret());
+        map.put("auth_code", request.getAuthCode());
+        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;
     }
 }

+ 5 - 3
fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/TencentApiClient.java

@@ -12,6 +12,8 @@ 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;
 
@@ -82,7 +84,7 @@ public class TencentApiClient extends AbstractApiClient implements IAccessTokenC
     }
 
     @Override
-    public SiteStatistics getDataReport(PromotionAccount account, Site site) {
+    public SiteStatistics getDataReport(PromotionAccount account,String startDate,String endDate) {
         return null;
     }
 
@@ -125,8 +127,8 @@ public class TencentApiClient extends AbstractApiClient implements IAccessTokenC
     }
 
     @Override
-    public String getAccessTokenByAuthCode(HttpServletRequest request) {
-        return "";
+    public AccessTokenVo getAccessTokenByAuthCode(AccessTokenByAuthCodeVo request) {
+        return null;
     }
 }
 

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/integration/client/advertiser/VIVOApiClient.java

@@ -87,7 +87,7 @@ public class VIVOApiClient extends AbstractApiClient {
     }
 
     @Override
-    public SiteStatistics getDataReport(PromotionAccount account, Site site) {
+    public SiteStatistics getDataReport(PromotionAccount account,String startDate,String endDate) {
         return null;
     }
 }

+ 135 - 26
fs-ad-new-api/src/main/java/com/fs/app/task/DataSyncTask.java

@@ -1,24 +1,27 @@
 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;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
 import java.time.LocalDateTime;
-import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.stream.Collectors;
 
 /**
@@ -38,49 +41,82 @@ public class DataSyncTask {
     private IPromotionAccountService promotionAccountService;
     @Autowired
     private AdvertiserHandlerFactory advertiserHandlerFactory;
+    @Autowired
+    private ILeadService leadService;
+    @Autowired
+    private ISiteStatisticsService statisticsService;
 
 
     /**
-     * 数据同步任务
-     * cron: 每1小时执行一次
+     * 数据同步任务->同步昨日数据
+     * cron: 每天凌晨一点统计昨日站点数据
      */
-    @Scheduled(cron = "0 0 */1 * * ?")
-    public void execute() {
-        String batchNo = DateUtil.format(LocalDateTime.now(), "yyyy-MM-dd-HH");
-        log.info("========== 数据同步任务开始,批次号:{} ==========", batchNo);
+    @Scheduled(cron = "0 0 1 * * ?")
+    public void syncYesterdayData() {
+        String batchNo = DateUtil.format(LocalDateTime.now().minusDays(1), "yyyy-MM-dd");
+        syncData(batchNo);
+    }
 
+    /**
+     * 数据同步任务->当日数据
+     * cron: 每2小时统计站点数据
+     */
+    // @Scheduled(cron = "0 0/2 * * * ?")
+    @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 {
-            // 查询开启API读取的推广账号
             List<Site> sites = siteService.selectEnabledSite();
-            if (sites == null || sites.isEmpty()) {
+            if (CollUtil.isEmpty(sites)) {
+                log.info("没有启用的站点,跳过同步");
                 return;
             }
 
+            // 查询所有API账户并过滤启用的
             List<Long> accountIds = sites.stream()
                     .map(Site::getPromotionAccountId)
+                    .distinct()
                     .collect(Collectors.toList());
-            Collection<PromotionAccount> accounts = promotionAccountService.listByIds(accountIds);
-            List<PromotionAccount> enabledAccounts = accounts.stream()
-                    .filter(account -> account.getApiSwitch() == 1)
+
+            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));
+                    .collect(Collectors.toMap(Site::getPromotionAccountId, site -> site, (s1, s2) -> s1));
 
-            long count = enabledAccounts.stream()
-                    .mapToInt(account -> {
+            // 同步数据并统计成功数量
+            long successCount = enabledAccounts.stream()
+                    .filter(account -> {
                         try {
                             Site site = siteMap.get(account.getId());
-                            syncAccountData(account, site);
-                            return 1;
+                            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 0;
+                            return false;
                         }
                     })
-                    .sum();
-            log.info("========== 数据同步任务完成,成功同步 {} 个账户 ==========", count);
+                    .count();
+
+            log.info("========== 数据同步任务完成,成功同步 {}/{} 个账户 ==========", successCount, enabledAccounts.size());
         } catch (Exception e) {
             log.error("数据同步任务执行失败", e);
         }
@@ -89,16 +125,89 @@ public class DataSyncTask {
     /**
      * 同步账户数据
      */
-    private void syncAccountData(PromotionAccount account, Site site) {
+    private void syncAccountData(PromotionAccount account, Long siteId, String batchNo) {
         log.info("开始同步账户:{}", account.getAccountName());
 
-        // 根据广告商类型调用对应的API
-        IApiClient apiClient = advertiserHandlerFactory.getApiClient(AdvertiserTypeEnum.getByCode(account.getAdvertiserId()));
-        SiteStatistics siteStatistics = apiClient.getDataReport(account,site);
-        // 实际业务逻辑在Service层实现
+        // 查询现有统计记录
+        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());
+    }
+
+
 }
 

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

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.fs.common.result.Result;
 import com.fs.newAdv.domain.PromotionAccount;
+import com.fs.newAdv.enums.AdvertiserTypeEnum;
 import com.fs.newAdv.service.IPromotionAccountService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -73,10 +74,25 @@ public class PromotionAccountController {
      */
     @PostMapping
     public Result<Void> create(@RequestBody @Validated PromotionAccount account) {
+
         boolean success = promotionAccountService.save(account);
+        checkAuthUrl(account);
+        promotionAccountService.updateById(account);
         return success ? Result.success() : Result.error("创建失败");
     }
 
+    private void checkAuthUrl(PromotionAccount account) {
+        if (account.getAdvertiserId().equals(AdvertiserTypeEnum.OCEANENGINE.getCode())){
+            // 巨量
+            account.setAuthUrl("https://open.oceanengine.com/audit/oauth.html?app_id="+account.getAppId()+"&state="+account.getId()+"&redirect_uri=https://track.mynatapp.cc/callback/oceanEngine/getAuthCode");
+        }
+        if (account.getAdvertiserId().equals(AdvertiserTypeEnum.BAIDU.getCode())){
+            // 百度
+            account.setAuthUrl(account.getAuthUrl().replaceAll("state=[^&]*", "state=" + account.getId()));
+        }
+
+    }
+
     /**
      * 更新推广账号
      *
@@ -89,6 +105,7 @@ public class PromotionAccountController {
             @RequestBody @Validated PromotionAccount account) {
 
         account.setId(id);
+        checkAuthUrl(account);
         boolean success = promotionAccountService.updateById(account);
         return success ? Result.success() : Result.error("更新失败");
     }

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

@@ -1,6 +1,9 @@
 package com.fs.company.controller.newAdv;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.fs.common.result.Result;
 import com.fs.newAdv.domain.Site;
 import com.fs.newAdv.service.ISiteService;
@@ -24,6 +27,25 @@ public class SiteController {
     @Autowired
     private ISiteService siteService;
 
+    @GetMapping("/page")
+    public Result<IPage<Site>> pageSiteStatistics(
+            @RequestParam(defaultValue = "1") Long pageNum,
+            @RequestParam(defaultValue = "10") Long pageSize,
+            @RequestParam(required = false) Long siteName,
+            @RequestParam(required = false) Long launchType,
+            @RequestParam(required = false) Long advertiserId
+    ) {
+        Page<Site> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<Site> wrapper = new LambdaQueryWrapper<>();
+        wrapper.like(siteName != null, Site::getSiteName, siteName);
+        wrapper.eq(launchType != null, Site::getLaunchType, launchType);
+        wrapper.eq(advertiserId != null, Site::getAdvertiserId, advertiserId);
+        wrapper.orderByDesc(Site::getCreateTime);
+        IPage<Site> result = siteService.page(page, wrapper);
+        return Result.success(result);
+    }
+
+
     @GetMapping("/list")
     public Result<List<Site>> list() {
         List<Site> list = siteService.list(new QueryWrapper<>());

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

@@ -4,7 +4,10 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.fs.common.result.Result;
+import com.fs.newAdv.domain.Site;
 import com.fs.newAdv.domain.SiteStatistics;
+import com.fs.newAdv.service.ILeadService;
+import com.fs.newAdv.service.ISiteService;
 import com.fs.newAdv.service.ISiteStatisticsService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -14,6 +17,12 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
 /**
  * 站点统计控制器
  * 提供查询接口
@@ -30,6 +39,10 @@ public class StatisticsController {
     @Autowired
     private ISiteStatisticsService statisticsService;
 
+    @Autowired
+    private ISiteService siteService;
+    private ILeadService lead;
+
     /**
      * 分页查询所有站点统计数据
      */
@@ -37,15 +50,72 @@ public class StatisticsController {
     public Result<IPage<SiteStatistics>> pageSiteStatistics(
             @RequestParam(defaultValue = "1") Long pageNum,
             @RequestParam(defaultValue = "10") Long pageSize,
-            @RequestParam(required = false) Long siteId
-            ) {
-        Page<SiteStatistics> page = new Page<>(pageNum, pageSize);
-        LambdaQueryWrapper<SiteStatistics> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(siteId != null, SiteStatistics::getSiteId, siteId);
-        IPage<SiteStatistics> result = statisticsService.page(page, wrapper);
-        return Result.success(result);
-    }
+            @RequestParam(required = false) Long advertiserId,
+            @RequestParam(required = true) String startDate,
+            @RequestParam(required = true) String endDate
+    ) {
+        Page<Site> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<Site> siteLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        siteLambdaQueryWrapper.eq(advertiserId != null, Site::getAdvertiserId, advertiserId);
+        siteLambdaQueryWrapper.orderByDesc(Site::getCreateTime);
+        IPage<Site> result = siteService.page(page, siteLambdaQueryWrapper);
+        List<Site> siteList = result.getRecords();
+        List<Long> siteIds = siteList.stream().map(Site::getId).collect(Collectors.toList());
 
+        IPage<SiteStatistics> siteStatisticsIPage = new Page<>(pageNum, pageSize);
+        if (!siteIds.isEmpty()) {
+            LambdaQueryWrapper<SiteStatistics> statisticsLambdaQueryWrapper = new LambdaQueryWrapper<>();
+            statisticsLambdaQueryWrapper.in(SiteStatistics::getSiteId, siteIds);
+            statisticsLambdaQueryWrapper.orderByDesc(SiteStatistics::getId);
+            statisticsLambdaQueryWrapper.between(SiteStatistics::getStatDate, startDate, endDate);
+            List<SiteStatistics> list = statisticsService.list(statisticsLambdaQueryWrapper);
 
+            // 按照siteId分组并求和
+            Map<Long, SiteStatistics> groupedMap = list.stream()
+                    .collect(Collectors.groupingBy(
+                            SiteStatistics::getSiteId,
+                            Collectors.collectingAndThen(
+                                    Collectors.toList(),
+                                    siteStatsList -> {
+                                        SiteStatistics aggregated = new SiteStatistics();
+                                        if (!siteStatsList.isEmpty()) {
+                                            SiteStatistics first = siteStatsList.get(0);
+                                            aggregated.setSiteId(first.getSiteId());
+                                            aggregated.setSiteName(first.getSiteName());
+
+                                            // 对所有数值字段求和
+                                            aggregated.setPv(siteStatsList.stream().mapToInt(s -> s.getPv() != null ? s.getPv() : 0).sum());
+                                            aggregated.setUv(siteStatsList.stream().mapToInt(s -> s.getUv() != null ? s.getUv() : 0).sum());
+                                            aggregated.setImpressionCount(siteStatsList.stream().mapToInt(s -> s.getImpressionCount() != null ? s.getImpressionCount() : 0).sum());
+                                            aggregated.setSysClickCount(siteStatsList.stream().mapToInt(s -> s.getSysClickCount() != null ? s.getSysClickCount() : 0).sum());
+                                            aggregated.setClickCount(siteStatsList.stream().mapToInt(s -> s.getClickCount() != null ? s.getClickCount() : 0).sum());
+                                            aggregated.setAccountCost(siteStatsList.stream().map(s -> s.getAccountCost() != null ? s.getAccountCost() : BigDecimal.ZERO).reduce(BigDecimal.ZERO, BigDecimal::add));
+                                            aggregated.setActualCost(siteStatsList.stream().map(s -> s.getActualCost() != null ? s.getActualCost() : BigDecimal.ZERO).reduce(BigDecimal.ZERO, BigDecimal::add));
+                                            aggregated.setCardCount(siteStatsList.stream().mapToInt(s -> s.getCardCount() != null ? s.getCardCount() : 0).sum());
+                                            aggregated.setWechatAddCount(siteStatsList.stream().mapToInt(s -> s.getWechatAddCount() != null ? s.getWechatAddCount() : 0).sum());
+                                            aggregated.setRegisterSuccessCount(siteStatsList.stream().mapToInt(s -> s.getRegisterSuccessCount() != null ? s.getRegisterSuccessCount() : 0).sum());
+                                            aggregated.setWechatGroupCount(siteStatsList.stream().mapToInt(s -> s.getWechatGroupCount() != null ? s.getWechatGroupCount() : 0).sum());
+                                            aggregated.setWechatDeleteCount(siteStatsList.stream().mapToInt(s -> s.getWechatDeleteCount() != null ? s.getWechatDeleteCount() : 0).sum());
+                                            aggregated.setMiniLaunchIndexCount(siteStatsList.stream().mapToInt(s -> s.getMiniLaunchIndexCount() != null ? s.getMiniLaunchIndexCount() : 0).sum());
+                                            aggregated.setMiniAuthIndexCount(siteStatsList.stream().mapToInt(s -> s.getMiniAuthIndexCount() != null ? s.getMiniAuthIndexCount() : 0).sum());
+                                            aggregated.setMiniAuthCount(siteStatsList.stream().mapToInt(s -> s.getMiniAuthCount() != null ? s.getMiniAuthCount() : 0).sum());
+                                            aggregated.setMiniQrCodeIndexCount(siteStatsList.stream().mapToInt(s -> s.getMiniQrCodeIndexCount() != null ? s.getMiniQrCodeIndexCount() : 0).sum());
+
+                                            // 计算所有比率
+                                            aggregated.calculateRates();
+                                        }
+                                        return aggregated;
+                                    }
+                            )
+                    ));
+
+            // 转换为列表
+            List<SiteStatistics> aggregatedList = new ArrayList<>(groupedMap.values());
+            siteStatisticsIPage.setRecords(aggregatedList);
+        }
+        siteStatisticsIPage.setTotal(result.getTotal());
+        siteStatisticsIPage.setPages(result.getPages());
+        return Result.success(siteStatisticsIPage);
+    }
 }
 

+ 11 - 1
fs-service/src/main/java/com/fs/newAdv/domain/Lead.java

@@ -86,6 +86,11 @@ public class Lead implements Serializable {
      * 是否添加企微 1是 0否
      */
     private Integer addContactQw;
+
+    /**
+     * 是否删除企微 1是 0否
+     */
+    private Integer wechatDelete;
     /**
      * 是否添加企微群 1是 0否
      */
@@ -100,7 +105,7 @@ public class Lead implements Serializable {
      */
     private Integer miniAuth;
     /**
-     * 小程序授权页进入 1是 0否
+     * 小程序落地页进入 1是 0否
      */
     private Integer miniAuthIndex;
 
@@ -108,6 +113,11 @@ public class Lead implements Serializable {
      * 小程序出码页进入 1是 0否
      */
     private Integer miniQrCodeIndex;
+
+    /**
+     * 发起进入小程序 1是 0否
+     */
+    private Integer miniLaunchIndexCount;
     /**
      * /**
      * 创建时间

+ 84 - 55
fs-service/src/main/java/com/fs/newAdv/domain/SiteStatistics.java

@@ -1,12 +1,15 @@
 package com.fs.newAdv.domain;
 
-import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
-import java.time.LocalDateTime;
+import java.math.RoundingMode;
+import java.time.LocalDate;
 
 /**
  * 站点统计表
@@ -36,49 +39,29 @@ public class SiteStatistics implements Serializable {
      */
     private String siteName;
 
-    /**
-     * ROI
-     */
-    private BigDecimal roi;
-
-    /**
-     * 销售额
-     */
-    private BigDecimal salesAmount;
-
     /**
      * PV
      */
-    private Long pv;
+    private Integer pv;
 
     /**
      * UV
      */
-    private Long uv;
+    private Integer uv;
 
     /**
      * 展示数
      */
-    private Long impressionCount;
+    private Integer impressionCount;
     /**
      * 平台监测有效点击数
      */
-    private Long sysClickCount;
+    private Integer sysClickCount;
 
     /**
      * 点击数
      */
-    private Long clickCount;
-
-    /**
-     * 点击率(%)
-     */
-    private BigDecimal clickRate;
-
-    /**
-     * 平均点击单价
-     */
-    private BigDecimal avgClickPrice;
+    private Integer clickCount;
 
     /**
      * 账面花费
@@ -93,65 +76,111 @@ public class SiteStatistics implements Serializable {
     /**
      * 名片数
      */
-    private Long cardCount;
+    private Integer cardCount;
 
     /**
-     * 名片获取率(%)
+     * 企微添加人数
      */
-    private BigDecimal cardAcquireRate;
+    private Integer wechatAddCount;
+
 
     /**
-     * 名片获取成本
+     * 报名成功名片人数
      */
-    private BigDecimal cardAcquireCost;
+    private Integer registerSuccessCount;
+
 
     /**
-     * 企微加人数
+     * 企微加人数
      */
-    private Long wechatAddCount;
+    private Integer wechatGroupCount;
+
 
     /**
-     * 企微添加率(%)
+     * 企微删除人数
      */
-    private BigDecimal wechatAddRate;
+    private Integer wechatDeleteCount;
+
 
     /**
-     * 报名成功名片人数
+     * 发起进入小程序人数
      */
-    private Long registerSuccessCount;
+    private Integer miniLaunchIndexCount;
 
     /**
-     * 报名成功加微率(%)
+     * 进入小程序落地页
      */
-    private BigDecimal registerWechatRate;
+    private Integer miniAuthIndexCount;
 
     /**
-     * 企微添加成本
+     * 小程序授权人数
      */
-    private BigDecimal wechatAddCost;
-
+    private Integer miniAuthCount;
     /**
-     * 企微加群人数
+     * 进入出码页人数
      */
-    private Long wechatGroupCount;
+    private Integer miniQrCodeIndexCount;
 
     /**
-     * 企微加群率(%)
+     * 统计时间
      */
-    private BigDecimal wechatGroupRate;
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private LocalDate statDate;
+
+
+    // --- 计算字段 (Rates & Prices) ---
+    private BigDecimal clickRate;          // 点击率
+    private BigDecimal avgClickPrice;      // 平均点击单价
+    private BigDecimal cardAcquireRate;    // 名片获取率
+    private BigDecimal cardAcquireCost;    // 名片获取成本
+    private BigDecimal wechatAddRate;      // 企微添加率
+    private BigDecimal wechatAddCost;      // 企微添加成本
+    private BigDecimal registerWechatRate; // 报名成功加微率
+    private BigDecimal wechatGroupRate;    // 企微加群率
+    private BigDecimal wechatDeleteCountRate; // 企微删除率
 
     /**
-     * 创建时间
+     * 核心逻辑:从聚合的数值中计算所有比率
+     * 避免除以0错误
      */
-    @TableField(value = "create_time", strategy = FieldStrategy.NOT_NULL)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-    private LocalDateTime createTime;
+    public void calculateRates() {
+        // 点击率 = 点击数 / 展示数
+        this.clickRate = computeRate(clickCount, impressionCount);
+        // 平均点击单价 = 实际花费 / 点击数
+        this.avgClickPrice = computePrice(actualCost, clickCount);
+        // 名片获取率 = 名片数 / 点击数
+        this.cardAcquireRate = computeRate(cardCount, clickCount);
+        // 名片获取成本 = 实际花费 / 名片数
+        this.cardAcquireCost = computePrice(actualCost, cardCount);
+        // 企微添加率 = 添加数 / 点击数
+        this.wechatAddRate = computeRate(wechatAddCount, clickCount);
+        // 企微添加成本 = 实际花费 / 企微添加人数
+        this.wechatAddCost = computePrice(actualCost, wechatAddCount);
+        // 报名成功加微率 = 成功数 / 名片数
+        this.registerWechatRate = computeRate(registerSuccessCount, cardCount);
+        // 企微加群率 = 加群数 / 名片数
+        this.wechatGroupRate = computeRate(wechatGroupCount, cardCount);
+        // 企微删除率 = 删除数 / 添加数
+        this.wechatDeleteCountRate = computeRate(wechatDeleteCount, wechatAddCount);
+    }
+
+
+    private BigDecimal computeRate(Integer numerator, Integer denominator) {
+        if (numerator == null || denominator == null || denominator == 0) {
+            return BigDecimal.ZERO;
+        }
+        return new BigDecimal(numerator)
+                .divide(new BigDecimal(denominator), 4, RoundingMode.HALF_UP);
+    }
 
     /**
-     * 更新时间
+     * 计算单价/成本
      */
-    @TableField(value = "update_time", strategy = FieldStrategy.NOT_NULL)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-    private LocalDateTime updateTime;
+    private BigDecimal computePrice(BigDecimal cost, Integer count) {
+        if (cost == null || count == null || count == 0) {
+            return BigDecimal.ZERO;
+        }
+        return cost.divide(new BigDecimal(count), 2, RoundingMode.HALF_UP);
+    }
 }
 

+ 1 - 1
fs-service/src/main/java/com/fs/newAdv/service/impl/SiteServiceImpl.java

@@ -32,7 +32,7 @@ public class SiteServiceImpl extends ServiceImpl<SiteMapper, Site> implements IS
     @Override
     public List<Site> selectEnabledSite() {
         return this.list(new LambdaQueryWrapper<Site>()
-                .eq(Site::getStatus, 0));
+                .eq(Site::getStatus, 1));
     }
 
     @Override

+ 19 - 0
fs-service/src/main/java/com/fs/newAdv/vo/AccessTokenByAuthCodeVo.java

@@ -0,0 +1,19 @@
+package com.fs.newAdv.vo;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@Builder
+public class AccessTokenByAuthCodeVo implements Serializable {
+    private String appId;
+    private String appSecret;
+    private String authCode;
+    private String userId;
+
+
+
+
+}

+ 13 - 0
fs-service/src/main/java/com/fs/newAdv/vo/AccessTokenVo.java

@@ -0,0 +1,13 @@
+package com.fs.newAdv.vo;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@Builder
+public class AccessTokenVo implements Serializable {
+    private String accessToken;
+    private String refreshToken;
+}