zhaotx 1 год назад
Родитель
Сommit
09d91dbf1b

+ 24 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/ReportsAdPushMapper.java

@@ -0,0 +1,24 @@
+package cn.com.ctop.job.bytedance.data.mapper;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ReportsAdPushMapper {
+
+    List<JSONObject> selectUserList();
+
+    Map<String, String> tmpPushData(@Param("date") Long date);
+    Map<String, String> tmpPushHourData(@Param("date") Long date, @Param("hour") String hour);
+    List<Map<String, String>> tmpTopMovie(@Param("date") Long date);
+
+    //所有优化师消耗排名
+    List<Map<String, String>> optimizerCostRank(@Param("date") Long date, @Param("hour") String hour);
+    List<Map<String, String>> optimizerDateData(@Param("date") Long date);
+    List<Map<String, String>> optimizerHourData(@Param("date") Long date, @Param("hour") String hour);
+
+    //漏绑账户预警
+    List<Map<String, String>> unbindData(@Param("date") Long date, @Param("last30Day") Long last30Day);
+}

+ 157 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/ReportsAdPushMapper.xml

@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="cn.com.ctop.job.bytedance.data.mapper.ReportsAdPushMapper">
+    <select id="tmpPushData" resultType="java.util.Map">
+        select concat(round(if(ifnull(sum(cost_amount),0)=0, 0, (ifnull(sum(iaa_amount),0)+ifnull(sum(pay_amount), 0))/sum(cost_amount)*100 ), 2), '%') AS water_roi,
+        concat(round(if(ifnull(sum(real_cost_amount),0)=0, 0, (ifnull(sum(share_iaa_amount),0)+ifnull(sum(share_pay_amount),0))/sum(real_cost_amount)*100), 2), '%') AS real_roi,
+        ifnull(sum(cost_amount),0) AS cost_amount,
+        round(ifnull(sum(pay_amount), 0), 2) AS pay_amount,
+        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) - ifnull(round(sum(real_cost_amount), 2), 0) AS real_profit,
+        ifnull(sum(iaa_amount), 0) + ifnull(sum(pay_amount), 0) - ifnull(sum(cost_amount), 0) AS water_profit,
+        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as end_amount,
+        date_format(update_time, '%Y-%m-%d %H:%i:%s') as update_time
+        from
+        (select sum(cost_amount) as cost_amount, sum(real_cost_amount) as real_cost_amount, max(update_time) as update_time
+        from application.media_ad_report_daily
+        where stat_date = #{date}
+        ) tb1,
+        (select sum(pay_amount) as pay_amount, sum(iaa_amount) as iaa_amount,
+        sum(share_iaa_amount) as share_iaa_amount, sum(share_pay_amount) as share_pay_amount
+        from application.user_data_report_daily
+        where stat_date = #{date}
+        ) tb2
+    </select>
+    <select id="tmpPushHourData" resultType="java.util.Map">
+        select concat(round(if(ifnull(sum(cost_amount),0)=0, 0, (ifnull(sum(iaa_amount),0)+ifnull(sum(pay_amount), 0))/sum(cost_amount)*100 ), 2), '%') AS water_roi,
+        concat(round(if(ifnull(sum(real_cost_amount),0)=0, 0, (ifnull(sum(share_iaa_amount),0)+ifnull(sum(share_pay_amount),0))/sum(real_cost_amount)*100), 2), '%') AS real_roi,
+        ifnull(sum(cost_amount),0) AS cost_amount,
+        round(ifnull(sum(pay_amount), 0), 2) AS pay_amount,
+        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) - ifnull(round(sum(real_cost_amount), 2), 0) AS real_profit,
+        ifnull(sum(iaa_amount), 0) + ifnull(sum(pay_amount), 0) - ifnull(sum(cost_amount), 0) AS water_profit,
+        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as end_amount
+        from
+        (select sum(cost_amount) as cost_amount, sum(real_cost_amount) as real_cost_amount
+        from application.media_ad_report_hourly
+        where stat_date = #{date} and `hour`= #{hour}
+        ) tb1,
+        (select sum(pay_amount) as pay_amount, sum(iaa_amount) as iaa_amount,
+        sum(share_iaa_amount) as share_iaa_amount, sum(share_pay_amount) as share_pay_amount
+        from application.user_data_report_hourly
+        where stat_date = #{date} and `hour`= #{hour}
+        ) tb2
+    </select>
+    <select id="tmpTopMovie" resultType="java.util.Map">
+        select tb1.*, ifnull(COALESCE (tb2.movie_name, tb3.movie_name), tb1.movie_id) as movie_name
+        from
+        (select rank() over (order by ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) desc) as rk,
+        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as end_amount, movie_id
+        from
+        application.user_data_report_daily udrd
+        where stat_date = #{date}
+        group by movie_id
+        having ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0)>0
+        order by end_amount desc
+        limit 10
+        ) tb1
+        left join
+        (select distinct movie_id, movie_name from deepthink.channel_changdunovel_info cci) tb2 on tb1.movie_id = tb2.movie_id
+        left JOIN
+        (select id, movie_name from hr_playlet.m_movie mm) tb3 on tb1.movie_id = tb3.id
+        order by rk
+    </select>
+
+
+    <select id="optimizerCostRank" resultType="java.util.Map">
+        select tb1.user_id, tb4.phonenumber,
+        rank() over (order by sum(cost_amount) desc) as rk,
+        sum(cost_amount) as cost_amount,
+        concat(round(if(ifnull(sum(real_cost_amount),0)=0, 0, (ifnull(sum(share_iaa_amount),0)+ifnull(sum(share_pay_amount),0))/sum(real_cost_amount)*100), 2), '%') AS real_roi
+        from
+        (select cast(advertiser_id as char) advertiser_id, user_id from advertiser_bind_info) tb1
+        left join (select advertiser_id, sum(cost_amount) as cost_amount, sum(real_cost_amount) as real_cost_amount
+        from application.media_ad_report_daily where stat_date=#{date} group by advertiser_id) tb2
+        on tb1.advertiser_id = tb2.advertiser_id
+        left join (select advertiser_id, sum(share_iaa_amount) as share_iaa_amount, sum(share_pay_amount) as share_pay_amount
+        from application.user_data_report_daily where stat_date=#{date} group by advertiser_id) tb3
+        on tb1.advertiser_id = tb3.advertiser_id
+        left join (select user_id, nick_name, phonenumber from sys_user) tb4
+        on tb1.user_id=tb4.user_id
+        group by tb1.user_id
+        having sum(cost_amount)>1
+        order by rk asc
+        limit 10
+    </select>
+    <select id="optimizerDateData" resultType="java.util.Map">
+        select tb1.user_id,tb4.phonenumber,tb4.nick_name,
+        concat(round(if(ifnull(sum(cost_amount),0)=0, 0, (ifnull(sum(iaa_amount),0)+ifnull(sum(pay_amount), 0))/sum(cost_amount)*100 ), 2), '%') AS water_roi,
+        concat(round(if(ifnull(sum(real_cost_amount),0)=0, 0, (ifnull(sum(share_iaa_amount),0)+ifnull(sum(share_pay_amount),0))/sum(real_cost_amount)*100), 2), '%') AS real_roi,
+        ifnull(sum(cost_amount),0) AS cost_amount,
+        round(ifnull(sum(pay_amount), 0), 2) AS pay_amount,
+        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as end_amount,
+        rank() over (order by sum(cost_amount) desc) as rk
+        from
+        (select advertiser_id, user_id from advertiser_bind_info) tb1
+        left join
+        (select advertiser_id, sum(cost_amount) as cost_amount, sum(real_cost_amount) as real_cost_amount, max(update_time) as update_time
+        from application.media_ad_report_daily
+        where stat_date = #{date}
+        group by advertiser_id) tb2 on tb1.advertiser_id = tb2.advertiser_id
+        left join
+        (select advertiser_id, sum(pay_amount) as pay_amount, sum(iaa_amount) as iaa_amount,
+        sum(share_iaa_amount) as share_iaa_amount, sum(share_pay_amount) as share_pay_amount
+        from application.user_data_report_daily
+        where stat_date = #{date}
+        group by advertiser_id) tb3 on tb1.advertiser_id = tb3.advertiser_id
+        left join (select user_id, nick_name, phonenumber from sys_user) tb4
+        on tb1.user_id=tb4.user_id
+        group by tb1.user_id
+        having sum(cost_amount)>0
+    </select>
+    <select id="optimizerHourData" resultType="java.util.Map">
+        select tb1.user_id,tb4.phonenumber,
+        concat(round(if(ifnull(sum(cost_amount),0)=0, 0, (ifnull(sum(iaa_amount),0)+ifnull(sum(pay_amount), 0))/sum(cost_amount)*100 ), 2), '%') AS water_roi,
+        concat(round(if(ifnull(sum(real_cost_amount),0)=0, 0, (ifnull(sum(share_iaa_amount),0)+ifnull(sum(share_pay_amount),0))/sum(real_cost_amount)*100), 2), '%') AS real_roi,
+        ifnull(sum(cost_amount),0) AS cost_amount,
+        round(ifnull(sum(pay_amount), 0), 2) AS pay_amount,
+        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as end_amount
+        from
+        (select advertiser_id, user_id from advertiser_bind_info) tb1
+        left join
+        (select advertiser_id, sum(cost_amount) as cost_amount, sum(real_cost_amount) as real_cost_amount, max(update_time) as update_time
+        from application.media_ad_report_hourly
+        where stat_date = #{date} and `hour`= #{hour}
+        group by advertiser_id) tb2 on tb1.advertiser_id = tb2.advertiser_id
+        left join
+        (select advertiser_id, sum(pay_amount) as pay_amount, sum(iaa_amount) as iaa_amount,
+        sum(share_iaa_amount) as share_iaa_amount, sum(share_pay_amount) as share_pay_amount
+        from application.user_data_report_hourly
+        where stat_date = #{date} and `hour`= #{hour}
+        group by advertiser_id) tb3 on tb1.advertiser_id = tb3.advertiser_id
+        left join (select user_id, nick_name, phonenumber from sys_user) tb4
+        on tb1.user_id=tb4.user_id
+        group by tb1.user_id
+    </select>
+
+
+
+    <select id="unbindData" resultType="java.util.Map">
+        select tb1.advertiser_id, tb1.advertiser_name, ifnull(tb2.ca,0) as today_cost, tb3.ca as last_30_cost from
+        (select advertiser_id, advertiser_name from advertiser_unbind_info) tb1
+        left join
+        (select advertiser_id, ifnull(sum(cost_amount),0) as ca from application.media_ad_report_daily where stat_date=#{date} group by advertiser_id) tb2
+        on tb1.advertiser_id = tb2.advertiser_id
+        left join
+        (select advertiser_id, ifnull(sum(cost_amount),0) as ca from application.media_ad_report_daily
+        where stat_date &gt;= #{last30Day} and stat_date &lt;= #{date} group by advertiser_id) tb3
+        on tb1.advertiser_id = tb3.advertiser_id
+        where tb3.ca >1
+        order by tb3.ca desc
+    </select>
+    <select id="selectUserList" resultType="com.alibaba.fastjson.JSONObject">
+        select user_id, nick_name, phonenumber, remark
+        from sys_user u
+        left join sys_dept d on u.dept_id = d.dept_id
+        where u.del_flag = '0'
+        AND (u.dept_id = 300 OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(300, ancestors) ))
+    </select>
+</mapper>

+ 6 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IOptimizerRankPushService.java

@@ -0,0 +1,6 @@
+package cn.com.ctop.job.bytedance.data.service;
+
+public interface IOptimizerRankPushService {
+
+    void send();
+}

+ 6 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IReportsAdPushService.java

@@ -0,0 +1,6 @@
+package cn.com.ctop.job.bytedance.data.service;
+
+public interface IReportsAdPushService {
+
+    void send();
+}

+ 6 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IUnbindWarnPushService.java

@@ -0,0 +1,6 @@
+package cn.com.ctop.job.bytedance.data.service;
+
+public interface IUnbindWarnPushService {
+
+    void send();
+}

+ 159 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/OptimizerRankPushServiceImpl.java

@@ -0,0 +1,159 @@
+package cn.com.ctop.job.bytedance.data.service.impl;
+
+import cn.com.ctop.job.bytedance.data.mapper.ReportsAdPushMapper;
+import cn.com.ctop.job.bytedance.data.service.IOptimizerRankPushService;
+import cn.com.ctop.job.bytedance.data.utils.DateUtils;
+import cn.com.ctop.job.bytedance.utils.FeishuRobotUtil;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import javafx.util.Pair;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+public class OptimizerRankPushServiceImpl implements IOptimizerRankPushService {
+    @Autowired
+    ReportsAdPushMapper reportsPushMapper;
+
+    private static final String TEMPLATE_VERSION = "1.0.8";
+    private static final String TEMPLATE_ID = "AAqBGPmrbiJtk";
+
+    public void send() {
+        List<JSONObject> users = reportsPushMapper.selectUserList();
+        List<String> mobiles = users.stream().map(user -> user.getString("phonenumber"))
+                .filter(s -> s != null && s.length() == 11)
+                .collect(Collectors.toList());
+        String token;
+        Map<String, String> mobileMap;
+        try {
+            token = FeishuRobotUtil.getFsZtTenantToken();
+            mobileMap = FeishuRobotUtil.getFsUserIds(token, mobiles); //mobile -> userId
+        }catch (IOException e){
+            log.error("获取飞书token失败", e);
+            return;
+        }
+
+        JSONObject template_variable = new JSONObject();
+        String today = DateUtils.getNowDate("yyyy-MM-dd");
+        Long todayLong = Long.parseLong(today.replaceAll("-", ""));
+        int hour = Integer.parseInt(DateUtils.getNowDate("H"));
+        int lastHour = hour -1;
+        int lastLastHour = hour -2;
+        template_variable.put("todaydate", today.substring(5, 10));
+        template_variable.put("thisHour", String.valueOf(hour));
+        template_variable.put("lastHour", String.valueOf(lastHour));
+        template_variable.put("lastLastHour", String.valueOf(lastLastHour));
+        JSONArray object_list_4 = new JSONArray();//优化师排名
+        List<Map<String, String>> optimizerCostRank = reportsPushMapper.optimizerCostRank(todayLong, String.valueOf(lastHour));
+        for (int i = 0; i < optimizerCostRank.size(); i++) {
+            Map<String, String> map = optimizerCostRank.get(i);
+            JSONObject object = new JSONObject();
+            object.put("rankRank", String.valueOf(map.getOrDefault("rk", "-")));
+            if (mobileMap.containsKey(map.get("phonenumber"))){
+                object.put("user_id", Arrays.asList(mobileMap.get(map.get("phonenumber"))));
+            }else {
+                object.put("user_id", Collections.emptyList());
+            }
+            object.put("rankTodayData", String.valueOf(map.getOrDefault("cost_amount", "0")));
+//            object.put("rankThisHourData", String.valueOf(map.getOrDefault("hour_cost_amount", "0")));
+            object.put("rankRealROI", String.valueOf(map.getOrDefault("real_roi", "0")));
+            object_list_4.add(object);
+        }
+        template_variable.put("object_list_4", object_list_4);
+
+        List<Map<String, String>> todayData = reportsPushMapper.optimizerDateData(todayLong);
+        List<Map<String, String>> hourData = reportsPushMapper.optimizerHourData(todayLong, String.valueOf(lastHour));
+        List<Map<String, String>> lastHourData = reportsPushMapper.optimizerHourData(todayLong, String.valueOf(lastLastHour));
+        Map<String, Map<String, String>> hourDataMap = hourData.stream().collect(Collectors.toMap(m -> String.valueOf(m.get("user_id")), m -> m));
+        Map<String, Map<String, String>> lastHourDataMap = lastHourData.stream().collect(Collectors.toMap(m -> String.valueOf(m.get("user_id")), m -> m));
+        List<Pair<String, String>> columns = Arrays.asList(
+                new Pair<>("cost_amount", "消耗金额"),
+                new Pair<>("end_amount", "结算金额"),
+                new Pair<>("pay_amount", "充值金额"),
+                new Pair<>("water_roi", "流水ROI"),
+                new Pair<>("real_roi", "真实ROI")
+        );
+
+//        int index = 0;
+        for (Map<String, String> optToday : todayData){
+//            if (index++ >= 1){
+//                break;
+//            }
+            JSONArray object_list_5 = new JSONArray();//本人数据
+            Map<String, String> optHour = hourDataMap.get(String.valueOf(optToday.get("user_id")));
+            Map<String, String> optLastHour = lastHourDataMap.get(String.valueOf(optToday.get("user_id")));
+            String rank = String.valueOf(optToday.get("rk"));
+            String nickName = String.valueOf(optToday.get("nick_name"));
+            String phoneNumber = String.valueOf(optToday.get("phonenumber"));
+            String userId = String.valueOf(optToday.get("user_id"));
+            String costAmount = "";
+            template_variable.put("myRank", rank);
+            for (Pair<String, String> column : columns){
+                JSONObject object = new JSONObject();
+                object.put("indicatorName", column.getValue());
+                object.put("todayData", String.valueOf(optToday.getOrDefault(column.getKey(), "0")));
+                object.put("thisHourData", String.valueOf(optHour.getOrDefault(column.getKey(), "0")));
+                object.put("lastHourData", String.valueOf(optLastHour.getOrDefault(column.getKey(), "0")));
+                object_list_5.add(object);
+                if (column.getKey().equals("cost_amount")){
+                    costAmount = String.valueOf(optToday.getOrDefault(column.getKey(), "0"));
+                }
+            }
+            template_variable.put("object_list_5", object_list_5);
+            System.out.println("发送目标: nickName: "+nickName+", userId: "+ userId + ", feishuUserId: "+mobileMap.get(phoneNumber)+", rank: "+rank+", costAmount: "+costAmount);
+            sendToFeishu(token, template_variable, mobileMap.get(phoneNumber));
+            //clear
+            template_variable.remove("myRank");
+            template_variable.remove("object_list_5");
+        }
+
+        template_variable.put("myRank", "0");
+        sendToFeishu(token, template_variable, "44fbaed6");//zhaotx
+        sendToFeishu(token, template_variable, "c5f6b59d");//yuyang
+        sendToFeishu(token, template_variable, "265g9ef1");//wangshu
+        sendToFeishu(token, template_variable, "58e7a98a");//xsq
+    }
+
+    private void sendToFeishu(String token, JSONObject template_variable, String receive_id) {
+        JSONObject content = new JSONObject();
+        JSONObject contentData = new JSONObject();
+        contentData.put("template_id",TEMPLATE_ID);
+        contentData.put("template_version_name", TEMPLATE_VERSION);
+        contentData.put("template_variable", template_variable);
+        content.put("type", "template");
+        content.put("data", contentData);
+
+        JSONObject request = new JSONObject();
+        request.put("receive_id", receive_id);
+//        request.put("receive_id", "44fbaed6");
+        request.put("msg_type", "interactive");
+        request.put("content", content.toJSONString());
+        //发送
+        try (CloseableHttpClient client = HttpClients.createDefault()){
+            HttpPost httpPost = new HttpPost("https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=user_id");
+            httpPost.setHeader("Authorization", "Bearer "+token);
+            HttpEntity entity = new StringEntity(request.toJSONString(), "UTF-8");
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
+            CloseableHttpResponse respon = client.execute(httpPost);
+//            System.out.println("发送结果: "+ EntityUtils.toString(respon.getEntity(), "UTF-8"));
+        }catch (IOException e){
+            log.error("发送飞书消息失败", e);
+        }
+    }
+}

Разница между файлами не показана из-за своего большого размера
+ 125 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/ReportsAdPushServiceImpl.java


+ 278 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/UnbindWarnPushServiceImpl.java

@@ -0,0 +1,278 @@
+package cn.com.ctop.job.bytedance.data.service.impl;
+
+import cn.com.ctop.job.bytedance.data.mapper.ReportsAdPushMapper;
+import cn.com.ctop.job.bytedance.data.service.IUnbindWarnPushService;
+import cn.com.ctop.job.bytedance.data.utils.Check;
+import cn.com.ctop.job.bytedance.data.utils.DateUtils;
+import cn.com.ctop.job.bytedance.utils.FeishuRobotUtil;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+public class UnbindWarnPushServiceImpl implements IUnbindWarnPushService {
+
+    private static final String TEMPLATE_VERSION = "1.0.5";
+    private static final String TEMPLATE_ID = "AAqRuaOUCifK9";
+    @Autowired
+    ReportsAdPushMapper reportsPushMapper;
+
+
+    public void send() {
+        List<JSONObject> users = reportsPushMapper.selectUserList();
+        users.forEach(u -> {
+            if (u.containsKey("remark") && u.getString("remark") != null && u.getString("remark").length() < 2){
+                u.remove("remark");
+            }
+        });
+        List<String> mobiles = users.stream().map(user -> user.getString("phonenumber"))
+                .filter(s -> s != null && s.length() == 11)
+                .collect(Collectors.toList());
+
+        String today = DateUtils.getNowDate("yyyy-MM-dd");
+        Long todayLong = Long.parseLong(today.replaceAll("-", ""));
+        String last30Day = DateUtils.getNowAddDate("yyyy-MM-dd", -30);
+        Long last30DayLong = Long.parseLong(last30Day.substring(0, 10).replaceAll("-", ""));
+
+        int unFindCount = 0;
+        List<Map<String, String>> unbindData = reportsPushMapper.unbindData(todayLong, last30DayLong);
+
+        List<Unbind> unbinds = new ArrayList<>();
+        List<Map<String, String>> unFindList = new ArrayList<>();
+        for (JSONObject user : users) {
+            Unbind ub = new Unbind();
+            ub.setName(user.getString("nick_name"));
+            ub.setRemark(user.getString("remark"));
+            ub.setPhoneNumber(user.getString("phonenumber"));
+            unbinds.add(ub);
+        }
+        for (Map<String, String> map : unbindData) {
+            String advertiserName = map.get("advertiser_name");
+
+            boolean sameFlag = false;
+            for (Unbind ub : unbinds) {
+                if (same(advertiserName, ub)) {
+                    ub.setUnbindNum(ub.getUnbindNum() + 1);
+                    ub.addToList(map);
+                    sameFlag = true;
+                }
+            }
+            if (!sameFlag) {
+                unFindCount++;
+                if (unFindList.size()<20) {
+                    unFindList.add(map);
+                }
+            }
+        }
+
+        System.out.println(">>>> 未匹配的账户数:" + unFindCount);
+
+        String alarm_time = DateUtils.getNowDate("yyyy-MM-dd HH:mm:ss");
+
+        String token = null, token2 = null, token3 = null;
+        Map<String, String> mobileMap = null;
+        try {
+            token = FeishuRobotUtil.getFsAdlbTenantToken();
+            mobileMap = FeishuRobotUtil.getFsUserIds(token, mobiles); //mobile -> userId
+            token2 = FeishuRobotUtil.getTenantToken(FeishuRobotUtil.getFsAdlb2Auth());
+            token3 = FeishuRobotUtil.getTenantToken(FeishuRobotUtil.getFsAdlb3Auth());
+        }catch (IOException e){
+            log.error("获取飞书token失败", e);
+            if (token == null || mobileMap == null){
+                return;
+            }
+        }
+        for (Unbind ub : unbinds){
+            String userId = mobileMap.get(ub.getPhoneNumber());
+            if (!Check.isNull(userId) && ub.getUnbindNum() > 0){
+                JSONObject template_variable = new JSONObject();
+                template_variable.put("unbindNum", ub.getUnbindNum());
+                template_variable.put("alarm_time", alarm_time);
+                JSONArray object_list_1 = new JSONArray();
+                for (Map<String, String> map : ub.getTop10List()){
+                    JSONObject object = new JSONObject();
+                    object.put("advertiserName", map.get("advertiser_name"));
+                    object.put("advertiserId", String.valueOf(map.get("advertiser_id")));
+                    object.put("todayCost", String.valueOf(map.getOrDefault("today_cost", "0")));
+                    object.put("last30Cost", String.valueOf(map.getOrDefault("last_30_cost", "0")));
+                    object_list_1.add(object);
+                }
+                template_variable.put("object_list_1", object_list_1);
+
+                JSONObject content = new JSONObject();
+                JSONObject contentData = new JSONObject();
+                contentData.put("template_id",TEMPLATE_ID);
+                contentData.put("template_version_name", TEMPLATE_VERSION);
+                contentData.put("template_variable", template_variable);
+                content.put("type", "template");
+                content.put("data", contentData);
+
+                JSONObject request = new JSONObject();
+                request.put("receive_id", userId);
+//                request.put("receive_id", "44fbaed6");
+                request.put("msg_type", "interactive");
+                request.put("content", content.toJSONString());
+                System.out.println("发送给: "+ub.getName());
+                sendToFeishu(token, request);
+
+                //todo 临时加的
+                if (token2 != null){
+                    System.out.println("应用2 发送给: "+ub.getName());
+                    sendToFeishu(token2, request);
+                }
+                if (token3 != null){
+                    System.out.println("应用3 发送给: "+ub.getName());
+                    sendToFeishu(token3, request);
+                }
+            }
+        }
+
+        //给指定人员发送汇总
+        if (1==1){
+            JSONObject template_variable = new JSONObject();
+            template_variable.put("unbindNum", unbindData.size());
+            template_variable.put("alarm_time", alarm_time);
+            JSONArray object_list_1 = new JSONArray();
+            for (Map<String, String> map : unbindData.subList(0, Math.min(10, unbindData.size()))){
+                JSONObject object = new JSONObject();
+                object.put("advertiserName", map.get("advertiser_name"));
+                object.put("advertiserId", String.valueOf(map.get("advertiser_id")));
+                object.put("todayCost", String.valueOf(map.getOrDefault("today_cost", "0")));
+                object.put("last30Cost", String.valueOf(map.getOrDefault("last_30_cost", "0")));
+                object_list_1.add(object);
+            }
+            template_variable.put("object_list_1", object_list_1);
+
+            JSONObject content = new JSONObject();
+            JSONObject contentData = new JSONObject();
+            contentData.put("template_id",TEMPLATE_ID);
+            contentData.put("template_version_name", TEMPLATE_VERSION);
+            contentData.put("template_variable", template_variable);
+            content.put("type", "template");
+            content.put("data", contentData);
+
+            List<String> userIds = Arrays.asList(
+                    "44fbaed6" //zhaotx
+                    ,"c5f6b59d" //yuyang
+                    ,"d834552b" //hemenghao
+                    ,"a3235a61" //louzijun
+                    ,"ebeeced9" //yumeng
+                    ,"58e7a98a"  //xsq
+            );
+
+            for (String userId : userIds){
+                JSONObject request = new JSONObject();
+                request.put("receive_id", userId);
+                request.put("msg_type", "interactive");
+                request.put("content", content.toJSONString());
+                System.out.println("发送给: "+userId);
+                sendToFeishu(token, request);
+            }
+        }
+
+
+
+    }
+
+    private void sendToFeishu(String token, JSONObject requestBody){
+        //发送
+        try (CloseableHttpClient client = HttpClients.createDefault()){
+            HttpPost httpPost = new HttpPost("https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=user_id");
+            httpPost.setHeader("Authorization", "Bearer "+token);
+            HttpEntity entity = new StringEntity(requestBody.toJSONString(), "UTF-8");
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
+            CloseableHttpResponse respon = client.execute(httpPost);
+            System.out.println("发送结果: "+ EntityUtils.toString(respon.getEntity(), "UTF-8"));
+        } catch (IOException e) {
+            log.error("发送飞书消息失败", e);
+        }
+    }
+
+    private boolean same(String advertiserName, Unbind ub){
+        if (advertiserName.contains(ub.getName())){
+            return true;
+        }
+        if (!Check.isNull(ub.getRemark())){
+            String lower = ub.getRemark().toLowerCase();
+            String upper = ub.getRemark().toUpperCase();
+            String firstUpper = ub.getRemark().substring(0, 1).toUpperCase() + ub.getRemark().substring(1).toLowerCase();
+            return advertiserName.contains(lower) || advertiserName.contains(upper) || advertiserName.contains(firstUpper);
+        }
+        return false;
+    }
+
+    public static class Unbind {
+        String phoneNumber;
+        String name;
+        String remark;
+        int unbindNum;
+        List<Map<String, String>> top10List;
+
+        public String getPhoneNumber() {
+            return phoneNumber;
+        }
+
+        public void setPhoneNumber(String phoneNumber) {
+            this.phoneNumber = phoneNumber;
+        }
+
+        public int getUnbindNum() {
+            return unbindNum;
+        }
+
+        public void setUnbindNum(int unbindNum) {
+            this.unbindNum = unbindNum;
+        }
+
+        public List<Map<String, String>> getTop10List() {
+            return top10List;
+        }
+
+        public void setTop10List(List<Map<String, String>> top10List) {
+            this.top10List = top10List;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getRemark() {
+            return remark;
+        }
+
+        public void setRemark(String remark) {
+            this.remark = remark;
+        }
+
+        public void addToList(Map<String, String> map) {
+            if (top10List == null) {
+                top10List = new ArrayList<>();
+            }
+            if (top10List.size() < 10) {
+                top10List.add(map);
+            }
+        }
+    }
+}

+ 35 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/FeishuPushJob.java

@@ -0,0 +1,35 @@
+package cn.com.ctop.job.bytedance.handler;
+
+import cn.com.ctop.job.bytedance.data.service.IOptimizerRankPushService;
+import cn.com.ctop.job.bytedance.data.service.IReportsAdPushService;
+import cn.com.ctop.job.bytedance.data.service.IUnbindWarnPushService;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class FeishuPushJob {
+    @Autowired
+    IUnbindWarnPushService unbindWarnPushService;
+    @Autowired
+    IReportsAdPushService reportsAdPushService;
+    @Autowired
+    IOptimizerRankPushService optimizerRankPushService;
+
+    @XxlJob("unbindWarnPush")
+    public void unbindWarnPush(){
+        unbindWarnPushService.send();
+    }
+
+    @XxlJob("reportsAdPush")
+    public void reportsAdPush(){
+        reportsAdPushService.send();
+    }
+
+    @XxlJob("optimizerRankPush")
+    public void optimizerRankPush(){
+        optimizerRankPushService.send();
+    }
+}

+ 112 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/utils/FeishuRobotUtil.java

@@ -0,0 +1,112 @@
+package cn.com.ctop.job.bytedance.utils;
+
+import cn.com.ctop.job.bytedance.data.utils.Check;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FeishuRobotUtil {
+
+    public static final String fs_zt_appid = "cli_a75d0ab0f538100e"; //智投助手应用
+    public static final String fs_zt_appkey = "w7ZNwahiXkmh1xCVSjt8ccJsaleIL3Fc";
+    public static final String fs_adlb_appid = "cli_a76b8d81943a1013";//广告账号漏绑预警应用
+    public static final String fs_adlb_appkey = "uQPlbJwPHs6wdYBFcGEAycMf4XpHXJTu";
+    public static final String fs_adlb2_appid = "cli_a76e77522638500d";//广告账号漏绑预警应用
+    public static final String fs_adlb2_appkey = "NRclULz12k35lkAktx9FigX7oGh2kCw1";
+    public static final String fs_adlb3_appid = "cli_a76e77711cce900e";//广告账号漏绑预警应用
+    public static final String fs_adlb3_appkey = "eG6gExoSotb4gRlkB4PuvccgGdfBNTTS";
+
+    //"{\"app_id\":\"cli_a75d0ab0f538100e\",\"app_secret\":\"w7ZNwahiXkmh1xCVSjt8ccJsaleIL3Fc\"}"
+    public static String getFsZtAuth() {
+        return "{\"app_id\":\"" + fs_zt_appid + "\",\"app_secret\":\"" + fs_zt_appkey + "\"}";
+    }
+    public static String getFsAdlbAuth() {
+        return "{\"app_id\":\"" + fs_adlb_appid + "\",\"app_secret\":\"" + fs_adlb_appkey + "\"}";
+    }
+    public static String getFsAdlb2Auth() {
+        return "{\"app_id\":\"" + fs_adlb2_appid + "\",\"app_secret\":\"" + fs_adlb2_appkey + "\"}";
+    }
+    public static String getFsAdlb3Auth() {
+        return "{\"app_id\":\"" + fs_adlb3_appid + "\",\"app_secret\":\"" + fs_adlb3_appkey + "\"}";
+    }
+
+    public static String getFsZtTenantToken() throws IOException {
+        return getTenantToken(getFsZtAuth());
+    }
+    public static String getFsAdlbTenantToken() throws IOException {
+        return getTenantToken(getFsAdlbAuth());
+    }
+
+    public static String getTenantToken(String entity) throws IOException {
+        //获取token
+        String tokenUrl = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";
+        HttpPost tokenPost = new HttpPost(tokenUrl);
+        tokenPost.setHeader("Content-Type", "application/json; charset=utf-8");
+        //添加body
+        HttpEntity tokenEntity = new StringEntity(entity);
+        tokenPost.setEntity(tokenEntity);
+        //发送请求
+        CloseableHttpResponse response = null;
+        CloseableHttpClient client = HttpClients.createDefault();
+        response = client.execute(tokenPost);
+        if (response.getStatusLine().getStatusCode() != 200){
+            System.out.println("获取token失败");
+        }
+        String res = EntityUtils.toString(response.getEntity(), "UTF-8");
+        JSONObject obj111 = JSON.parseObject(res);
+        String token = obj111.getString("tenant_access_token");
+        System.out.println(token);
+        return token;
+    }
+
+    public static Map<String, String> getFsUserIds(String token, List<String> telephones) throws IOException {
+        if (Check.isNull(telephones)){
+            return null;
+        }
+        //获取token
+        String url = "https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id?user_id_type=user_id";
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
+        httpPost.setHeader("Authorization", "Bearer "+token);
+        JSONObject request = new JSONObject(1);
+        request.put("mobiles", telephones);
+        //添加body
+        HttpEntity tokenEntity = new StringEntity(request.toJSONString(), "UTF-8");
+        httpPost.setEntity(tokenEntity);
+        //发送请求
+        CloseableHttpResponse response = null;
+        CloseableHttpClient client = HttpClients.createDefault();
+        response = client.execute(httpPost);
+        if (response.getStatusLine().getStatusCode() != 200){
+            System.out.println("获取token失败");
+        }
+        String str = EntityUtils.toString(response.getEntity(), "UTF-8");
+        JSONObject result = JSON.parseObject(str);
+        if (result.getInteger("code")==0 && result.containsKey("data")){
+            Map<String, String> map = new HashMap<>();
+            JSONArray array = result.getJSONObject("data").getJSONArray("user_list");
+            for (int i = 0; i < array.size(); i++) {
+                JSONObject obj = array.getJSONObject(i);
+                String mobile = obj.getString("mobile");
+                String userId = obj.getString("user_id");
+                map.put(mobile, userId);
+            }
+            return map;
+        }else {
+            System.out.println(result);
+            return null;
+        }
+    }
+}