Ver Fonte

feat(xwPush): 新增信维排名推送相关接口及实现

- 在ReportsAdPushMapper中新增xwTotalCostRank、xwDeptCostRank和xwOptimizerCostRank查询方法
- 新增IXwPushService接口及其实现类XwPushServiceImpl,封装信维数据飞书消息推送逻辑
- 实现按总成本排名、部门成本排名和优化师成本排名的数据查询及消息组装
- 通过FeishuRobotUtil调用飞书开放平台API,实现群聊及用户的模版消息发送
- 在FeishuPushJob中注入IXwPushService并新增调度任务xwOptimizerRankPush用于触发推送
- 完成推送内容的日期格式处理及数据过滤,保证数据有效展示
zhaotx há 2 meses atrás
pai
commit
bb7ce55d6b

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

@@ -71,4 +71,9 @@ public interface ReportsAdPushMapper {
     @DS("master")
     //七天内上架短剧项目列表
     List<JSONObject> publishUpMovieList();
+
+    //信维数据
+    Map<String, String> xwTotalCostRank(@Param("date") Long date);
+    List<Map<String, String>> xwDeptCostRank(@Param("date") Long date);
+    List<Map<String, String>> xwOptimizerCostRank(@Param("date") Long date);
 }

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

@@ -883,4 +883,157 @@
         group by  video_project_name, project_ad_date, movie_project_source
         order by project_ad_date desc
     </select>
+
+
+
+    <select id="xwTotalCostRank" resultType="java.util.Map">
+        select round(ifnull(sum(cost_amount),0), 2) AS cost_amount,
+               round(ifnull(sum(income),0), 2) AS income,
+               concat(round(ifnull(sum(income) / nullif(sum(real_cost_amount), 0), 0) * 100, 2), '%') as roi,
+               max(tb2.update_time) as update_time
+        from
+            (select cast(advertiser_id as char) advertiser_id, t1.user_id
+             from tidb.deepthink.advertiser_bind_info t1
+                      join tidb.deepthink.bytedance_auth_list t2 on t1.admin_advertiser_id = t2.admin_advertiser_id
+             where t1.user_id is not null and t2.corporation_name like '%信维%'
+            ) tb1
+                left join (select advertiser_id,
+                                  ifnull(sum(cost_amount), 0) as cost_amount,
+                                  ifnull(sum(real_cost_amount), 0) as real_cost_amount,
+                                max(t1.update_time) as update_time
+                           from application.media_ad_report_daily t1
+                                    join tidb.deepthink.mini_app_info t2 on t1.ma_appid = t2.app_id
+                                    join tidb.deepthink.all_moive t3 on t1.movie_id = t3.moive_id and find_in_set(t3.movie_source, t2.movie_sources) > 0
+                           where stat_date=#{date}
+                             and ma_appid in (select app_id from tidb.deepthink.mini_app_info where t2.business_line in (1,6,7,4,8,9,10))
+                           group by advertiser_id
+            ) tb2
+                          on tb1.advertiser_id = tb2.advertiser_id
+                left join (select advertiser_id,
+                                  ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as income
+                           from application.user_data_report_daily t1
+                                    join tidb.deepthink.mini_app_info t2 on t1.ma_appid = t2.app_id
+                                    join tidb.deepthink.all_moive t3 on t1.movie_id = t3.moive_id and find_in_set(t3.movie_source, t2.movie_sources) > 0
+                           where stat_date=#{date}
+                             and ma_appid in (select app_id from tidb.deepthink.mini_app_info where business_line in (1,6,7,4,8,9,10))
+                           group by advertiser_id) tb3
+                          on tb1.advertiser_id = tb3.advertiser_id
+                left join (select user_id, nick_name, phonenumber, dept_id from tidb.deepthink.sys_user) tb4
+                          on tb1.user_id=tb4.user_id
+                left join (
+                select dp.dept_id, dp.dept_name,
+                       ifnull(subdp.dept_id, dp.dept_id) as sub_dept_id,
+                       ifnull(subdp.dept_name, dp.dept_name) as sub_dept_name
+                from (select * from tidb.deepthink.sys_dept where parent_id in (300)) dp
+                         right join tidb.deepthink.sys_dept subdp on dp.dept_id=subdp.parent_id
+                where dp.dept_id is not null
+                union all
+                select dept_id, dept_name, dept_id as sub_dept_id, dept_name as sub_dept_name from tidb.deepthink.sys_dept where parent_id in (300)
+            ) tb5
+                          on tb4.dept_id = tb5.sub_dept_id
+    </select>
+
+    <select id="xwDeptCostRank" resultType="java.util.Map">
+        select tb5.sub_dept_id as dept_id,
+        concat(tb5.dept_name, '-', tb5.sub_dept_name) as dept_name,
+        rank() over (order by sum(cost_amount) desc) as rk,
+        round(ifnull(sum(cost_amount),0), 2) AS cost_amount,
+        round(ifnull(sum(income),0), 2) AS income,
+        concat(round(ifnull(sum(income) / nullif(sum(real_cost_amount), 0), 0) * 100, 2), '%') as roi
+        from
+        (select cast(advertiser_id as char) advertiser_id, t1.user_id
+        from tidb.deepthink.advertiser_bind_info t1
+        join tidb.deepthink.bytedance_auth_list t2 on t1.admin_advertiser_id = t2.admin_advertiser_id
+        where t1.user_id is not null and t2.corporation_name like '%信维%'
+        ) tb1
+        left join (select advertiser_id,
+        ifnull(sum(cost_amount), 0) as cost_amount,
+        ifnull(sum(real_cost_amount), 0) as real_cost_amount
+        from application.media_ad_report_daily t1
+        join tidb.deepthink.mini_app_info t2 on t1.ma_appid = t2.app_id
+        join tidb.deepthink.all_moive t3 on t1.movie_id = t3.moive_id and find_in_set(t3.movie_source, t2.movie_sources) > 0
+        where stat_date=#{date}
+        and ma_appid in (select app_id from tidb.deepthink.mini_app_info where t2.business_line in (1,6,7,4,8,9,10))
+        group by advertiser_id
+        ) tb2
+        on tb1.advertiser_id = tb2.advertiser_id
+        left join (select advertiser_id,
+        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as income
+        from application.user_data_report_daily t1
+        join tidb.deepthink.mini_app_info t2 on t1.ma_appid = t2.app_id
+        join tidb.deepthink.all_moive t3 on t1.movie_id = t3.moive_id and find_in_set(t3.movie_source, t2.movie_sources) > 0
+        where stat_date=#{date}
+        and ma_appid in (select app_id from tidb.deepthink.mini_app_info where business_line in (1,6,7,4,8,9,10))
+        group by advertiser_id) tb3
+        on tb1.advertiser_id = tb3.advertiser_id
+        left join (select user_id, nick_name, phonenumber, dept_id from tidb.deepthink.sys_user) tb4
+        on tb1.user_id=tb4.user_id
+        left join (
+        select dp.dept_id, dp.dept_name,
+            ifnull(subdp.dept_id, dp.dept_id) as sub_dept_id,
+            ifnull(subdp.dept_name, dp.dept_name) as sub_dept_name
+            from (select * from tidb.deepthink.sys_dept where parent_id in (300)) dp
+            right join tidb.deepthink.sys_dept subdp on dp.dept_id=subdp.parent_id
+            where dp.dept_id is not null
+            union all
+            select dept_id, dept_name, dept_id as sub_dept_id, dept_name as sub_dept_name from tidb.deepthink.sys_dept where parent_id in (300)
+        ) tb5
+        on tb4.dept_id = tb5.sub_dept_id
+        group by tb5.sub_dept_id, tb5.dept_name, tb5.sub_dept_name
+        having sum(cost_amount)>1
+        order by rk asc
+        limit 10
+    </select>
+
+    <select id="xwOptimizerCostRank" resultType="java.util.Map">
+        select tb1.user_id, tb4.phonenumber, tb4.nick_name, tb5.dept_id, tb5.dept_name,
+        rank() over (order by ifnull(sum(income) / nullif(sum(real_cost_amount), 0), 0) desc) as rk,
+        round(ifnull(sum(cost_amount),0), 2) AS cost_amount,
+        round(ifnull(sum(income),0), 2) AS income,
+        concat(round(ifnull(sum(income) / nullif(sum(real_cost_amount), 0), 0) * 100, 2), '%') as roi
+        from
+        (select cast(advertiser_id as char) advertiser_id, t1.user_id
+        from tidb.deepthink.advertiser_bind_info t1
+        join tidb.deepthink.bytedance_auth_list t2 on t1.admin_advertiser_id = t2.admin_advertiser_id
+        where t1.user_id is not null and t2.corporation_name like '%信维%'
+        ) tb1
+        left join (select advertiser_id,
+        ifnull(sum(cost_amount), 0) as cost_amount,
+        ifnull(sum(real_cost_amount), 0) as real_cost_amount
+        from application.media_ad_report_daily t1
+        join tidb.deepthink.mini_app_info t2 on t1.ma_appid = t2.app_id
+        join tidb.deepthink.all_moive t3 on t1.movie_id = t3.moive_id and find_in_set(t3.movie_source, t2.movie_sources) > 0
+        where stat_date=#{date}
+        and ma_appid in (select app_id from tidb.deepthink.mini_app_info where t2.business_line in (1,6,7,4,8,9,10))
+        group by advertiser_id
+        ) tb2
+        on tb1.advertiser_id = tb2.advertiser_id
+        left join (select advertiser_id,
+        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as income
+        from application.user_data_report_daily t1
+        join tidb.deepthink.mini_app_info t2 on t1.ma_appid = t2.app_id
+        join tidb.deepthink.all_moive t3 on t1.movie_id = t3.moive_id and find_in_set(t3.movie_source, t2.movie_sources) > 0
+        where stat_date=#{date}
+        and ma_appid in (select app_id from tidb.deepthink.mini_app_info where business_line in (1,6,7,4,8,9,10))
+        group by advertiser_id) tb3
+        on tb1.advertiser_id = tb3.advertiser_id
+        left join (select user_id, nick_name, phonenumber, dept_id, status, del_flag from tidb.deepthink.sys_user) tb4
+        on tb1.user_id=tb4.user_id
+        left join (
+        select dp.dept_id, dp.dept_name,
+        ifnull(subdp.dept_id, dp.dept_id) as sub_dept_id,
+        ifnull(subdp.dept_name, dp.dept_name) as sub_dept_name
+        from (select * from tidb.deepthink.sys_dept where parent_id in (300)) dp
+        right join tidb.deepthink.sys_dept subdp on dp.dept_id=subdp.parent_id
+        where dp.dept_id is not null
+        union all
+        select dept_id, dept_name, dept_id as sub_dept_id, dept_name as sub_dept_name from tidb.deepthink.sys_dept where parent_id in (300)
+        ) tb5
+        on tb4.dept_id = tb5.sub_dept_id
+        where tb1.user_id is not null and (tb4.nick_name not like '%IAP%' and tb4.nick_name not like '%IAA%')
+        and tb4.status = 0 and tb4.del_flag = 0
+        group by tb1.user_id, tb4.phonenumber, tb4.nick_name, tb5.dept_id, tb5.dept_name
+        having sum(cost_amount)>1
+        order by rk asc
+    </select>
 </mapper>

+ 14 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IXwPushService.java

@@ -0,0 +1,14 @@
+package cn.com.ctop.job.bytedance.data.service;
+
+import cn.com.ctop.job.bytedance.data.entity.FeishuSendObject;
+
+import java.util.List;
+
+public interface IXwPushService {
+
+    void send();
+
+    void send(List<FeishuSendObject> sendObjects);
+
+    void test();
+}

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

@@ -0,0 +1,194 @@
+package cn.com.ctop.job.bytedance.data.service.impl;
+
+import cn.com.ctop.job.bytedance.data.entity.FeishuSendObject;
+import cn.com.ctop.job.bytedance.data.mapper.ReportsAdPushMapper;
+import cn.com.ctop.job.bytedance.data.service.IXwPushService;
+import cn.com.ctop.job.bytedance.data.utils.Check;
+import cn.com.ctop.job.bytedance.data.utils.DateUtils;
+import cn.com.ctop.job.bytedance.data.utils.FeishuSendObjectUtils;
+import cn.com.ctop.job.bytedance.utils.FeishuRobotUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.ImmutableMap;
+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.*;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+public class XwPushServiceImpl implements IXwPushService {
+
+
+    public static final String APP_ID = "cli_a77cb66d87bfd00d";
+    public static final String APP_KEY = "nDAFiSBNbQ5gYilvKZflE6fWT47m0D1W";
+
+    public static final String TEMPLATE_ID = "AAqP0DPte4O3q";
+    public static final String TEMPLATE_VERSION = "1.0.1";
+    @Autowired
+    ReportsAdPushMapper reportsPushMapper;
+
+    @Override
+    public void send() {
+
+    }
+
+    @Override
+    public void test() {
+
+    }
+
+    @Override
+    public void send(List<FeishuSendObject> sendObjects) {
+        send(FeishuSendObjectUtils.userIds(sendObjects), FeishuSendObjectUtils.chatIds(sendObjects));
+    }
+
+
+
+    private void send(List<String> userIds, List<String> chatIds) {
+        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.getTenantToken(APP_ID, APP_KEY);
+            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("-", ""));
+
+        Map<String, String> total = reportsPushMapper.xwTotalCostRank(todayLong);
+        template_variable.put("todaydate", today);
+        template_variable.put("totalIncome", String.valueOf(total.get("income")));
+        template_variable.put("totalCostAmount", String.valueOf(total.get("cost_amount")));
+        template_variable.put("totalRoi", String.valueOf(total.get("roi")));
+        template_variable.put("updateTime", String.valueOf(total.get("update_time")));
+
+        JSONArray object_list_1 = new JSONArray();
+        List<Map<String, String>> deptCostRank = reportsPushMapper.xwDeptCostRank(todayLong);
+        for (int i = 0; i < deptCostRank.size(); i++) {
+            String deptId = String.valueOf(deptCostRank.get(i).getOrDefault("dept_id", "-"));
+            if ("300".equals(deptId)){
+                continue;
+            }
+            JSONObject deptData = JSON.parseObject(JSON.toJSONString(deptCostRank.get(i)));
+
+            JSONObject object_1 = new JSONObject();
+            object_1.put("dept_name", String.valueOf(deptData.get("dept_name")));
+            object_1.put("costAmount", String.valueOf(deptData.get("cost_amount")));
+            object_1.put("rk", String.valueOf(deptData.get("rk")));
+            object_1.put("income", String.valueOf(deptData.get("income")));
+            object_1.put("roi", String.valueOf(deptData.get("roi")));
+
+            object_list_1.add(object_1);
+        }
+        template_variable.put("object_list_1", object_list_1);
+
+        JSONArray object_list_4 = new JSONArray();
+        Map<String, String> mobile2FsUserId = Collections.EMPTY_MAP;
+        try {
+            FeishuRobotUtil.getFsUserIds(token, mobiles);
+        }catch (IOException e){
+            log.error("获取飞书用户ID失败", e);
+        }
+        List<Map<String, String>> optimizerCostRank = reportsPushMapper.xwOptimizerCostRank(todayLong);
+        for (int i = 0; i < optimizerCostRank.size(); i++) {
+            String phonenumber = mobileMap.get(optimizerCostRank.get(i).get("phonenumber"));
+            JSONObject optData = JSON.parseObject(JSON.toJSONString(optimizerCostRank.get(i)));
+
+            JSONObject obj = new JSONObject();
+//            obj.put("user_id", mobile2FsUserId.get(phonenumber));
+            obj.put("nick_name", String.valueOf(optData.get("nick_name")));
+            obj.put("costAmount", String.valueOf(optData.get("cost_amount")));
+            obj.put("rk", String.valueOf(optData.get("rk")));
+            obj.put("roi", String.valueOf(optData.get("roi")));
+
+            object_list_4.add(obj);
+        }
+        template_variable.put("object_list_4", object_list_4);
+
+
+
+        for (String userId : userIds){
+            sendToFeishu(token, template_variable, userId);
+        }
+        for (String chatId : chatIds){
+            sendToChatGroup(token, template_variable, chatId);
+        }
+
+    }
+
+    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("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);
+        }
+    }
+
+    private void sendToChatGroup(String token, JSONObject template_variable, String chatId) {
+        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", chatId);
+        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=chat_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);
+        }
+    }
+}

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

@@ -36,6 +36,8 @@ public class FeishuPushJob {
     IMovieRankPushService movieRankPushService;
     @Autowired
     IFeishuService feishuService;
+    @Autowired
+    IXwPushService xwPushService;
 
     private static ExecutorService sendExecutor = Executors.newFixedThreadPool(2);
     private static ExecutorService reportsAdPushExecutor = Executors.newFixedThreadPool(2);
@@ -120,6 +122,11 @@ public class FeishuPushJob {
         cutGroupRankPushService.send(getSendObjects("cutGroupRankPush"));
     }
 
+    @XxlJob("xwOptimizerRankPush")
+    public void xwOptimizerRankPush(){
+        xwPushService.send(getSendObjects("xwOptimizerRankPush"));
+    }
+
     @XxlJob("loginRecordPush")
     public void loginRecordPush() {
         userLoginRecordService.queryUserLoginRecord();