Przeglądaj źródła

巨量商品库上传

zhaotx 1 rok temu
rodzic
commit
cfb3222fdb

+ 7 - 3
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/controller/FeishuController.java

@@ -26,12 +26,16 @@ public class FeishuController {
     IUnbindWarnPushService unbindWarnPushService;
     @Autowired
     IBindAppErrorWarnService bindAppErrorWarnService;
+    @Autowired
+    IBytedanceProductUpdateService bytedanceProductUpdateService;
     @GetMapping("/test")
-    public void test(){
-        bindAppErrorWarnService.send();
-        System.out.println("---feishu test 收到---");
+    public void test() throws Exception {
+//        bytedanceProductUpdateService.initNew();
+        bytedanceProductUpdateService.run();
+
     }
 
+
     @PostMapping("/sendUnbind")
     public void sendUnbind(@RequestBody JSONObject obj){
         JSONArray array = obj.getJSONArray("advertiserIds");

+ 20 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/BytedanceProductUpdateMapper.java

@@ -0,0 +1,20 @@
+package cn.com.ctop.job.bytedance.data.mapper;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface BytedanceProductUpdateMapper {
+
+    List<JSONObject> getIAPPlatformList();
+    List<JSONObject> getIAPVideoList();
+    List<JSONObject> getIAPProductTypeEnums();
+
+    List<JSONObject> getIAAPlatformList();
+    List<JSONObject> getIAAVideoList();
+    List<JSONObject> getIAAProductTypeEnums();
+
+    int updateInstanceId(@Param("advertiserId") String advertiserId, @Param("instanceId") String instanceId);
+
+}

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

@@ -0,0 +1,63 @@
+<?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.BytedanceProductUpdateMapper">
+    <update id="updateInstanceId">
+        update deepthink.bytedance_product_main_advertiser
+        set mini_instance_id = #{instanceId}
+        where advertiser_id = #{advertiserId}
+    </update>
+
+
+    <select id="getIAPPlatformList" resultType="com.alibaba.fastjson.JSONObject">
+        select t1.*, t3.access_token
+        FROM deepthink.bytedance_product_main_advertiser t1
+        left join deepthink.bytedance_advertiser_list t2 on t1.advertiser_id=t2.advertiser_id
+        left join deepthink.bytedance_auth_list t3 on t2.admin_advertiser_id=t3.admin_advertiser_id
+        where t1.`type`=2 and t1.`_status`=0
+    </select>
+    <select id="getIAAPlatformList" resultType="com.alibaba.fastjson.JSONObject">
+        select t1.*, t3.access_token
+        FROM deepthink.bytedance_product_main_advertiser t1
+        left join deepthink.bytedance_advertiser_list t2 on t1.advertiser_id=t2.advertiser_id
+        left join deepthink.bytedance_auth_list t3 on t2.admin_advertiser_id=t3.admin_advertiser_id
+        where t1.`type`=1 and t1.`_status`=0
+    </select>
+    <select id="getIAPVideoList" resultType="com.alibaba.fastjson.JSONObject">
+        select third.album_id, detail.platform_config, video.id as video_id, detail.id as video_detail_id, video.name as name, video.cover_img as img,ifnull(category.name, '其他') as 'theme',
+        '天津星链视界信息技术有限公司' as copyright_owner, '其他' as playlet_gender, video.update_sort as playlet_num, '2' as playlet_duration,
+        payinfo.sort as start_pay_playlet, '5' as membership_types, '1' as has_paid_content, '2' as has_motivation_types,
+        payinfo.maxpay as max_charge, payinfo.minpay as min_charge, maxpay as recommended_charge, maxpay as price,
+        2 as has_paid_content, 1 as has_motivation_content
+        FROM (select * from thirtpart.ly_third_videos where submission_status=2
+        and video_id in (select video_id from thirtpart.ly_video_audit_records where audit_status=2)
+        ) third
+        LEFT JOIN (select * from thirtpart.ly_video_details where platform_config is not null group by video_id) detail ON third.video_id = detail.video_id
+        LEFT JOIN (select * from thirtpart.ly_videos where play_platform like '%2%') video ON third.video_id = video.id and third.platform=2
+        LEFT JOIN thirtpart.ly_video_category category ON video.category_id = category.id
+        LEFT JOIN (select video_id, min(sort) as sort, max(pay_amount/100.0) as maxpay, min(pay_amount/100.0) as minpay from thirtpart.ly_video_details where is_pay=1 group by video_id) payinfo ON video.id = payinfo.video_id
+        where video.is_free = 1
+    </select>
+    <select id="getIAAVideoList" resultType="com.alibaba.fastjson.JSONObject">
+        select third.album_id, detail.platform_config, video.id as video_id, detail.id as video_detail_id, video.name as name, video.cover_img as img,ifnull(category.name, '其他') as 'theme',
+        '天津星链视界信息技术有限公司' as copyright_owner, '其他' as playlet_gender, video.update_sort as playlet_num, '2' as playlet_duration,
+        '5' as membership_types, '1' as has_paid_content, '2' as has_motivation_types,payinfo.sort as starting_unlock_episode
+        FROM (select * from thirtpart.ly_third_videos where submission_status=2
+        and video_id in (select video_id from thirtpart.ly_video_audit_records where audit_status=2)
+        ) third
+        LEFT JOIN (select * from thirtpart.ly_video_details where platform_config is not null group by video_id) detail ON third.video_id = detail.video_id
+        LEFT JOIN (select * from thirtpart.ly_videos where play_platform like '%2%') video ON third.video_id = video.id and third.platform=2
+        LEFT JOIN thirtpart.ly_video_category category ON video.category_id = category.id
+        LEFT JOIN (select video_id, max(sort) as sort from thirtpart.ly_video_details where is_pay=0 group by video_id) payinfo ON video.id = payinfo.video_id
+        where video.is_free = 0
+    </select>
+    <select id="getIAPProductTypeEnums" resultType="com.alibaba.fastjson.JSONObject">
+        select first_type_name, first_type_id, second_type_name, second_type_id, third_type_name, third_type_id
+        from deepthink.bytedance_product_group_types
+        where `type`=2
+    </select>
+    <select id="getIAAProductTypeEnums" resultType="com.alibaba.fastjson.JSONObject">
+        select first_type_name, first_type_id, second_type_name, second_type_id, third_type_name, third_type_id
+        from deepthink.bytedance_product_group_types
+        where `type`=1
+    </select>
+</mapper>

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

@@ -41,15 +41,18 @@
         ) tb2
     </select>
     <select id="tmpTopMovie" resultType="java.util.Map">
-        select ot1.*, ot2.cost_amount, ot2.real_cost_amount,
+        select  case ot1.movie_source when 2 then movie_name else concat('欢瑞自制-', movie_name) end as movie_name,
+        ot1.rk, ot1.end_amount,  ot2.cost_amount, ot2.real_cost_amount,
         concat(round(if(ifnull(cost_amount,0)=0, 0, end_amount/cost_amount*100), 2), '%') AS water_roi,
-        concat(round(if(ifnull(real_cost_amount,0)=0, 0, end_amount/real_cost_amount*100), 2), '%') AS real_roi
+        concat(round(if(ifnull(real_cost_amount,0)=0, 0, share_end_amount/real_cost_amount*100), 2), '%') AS real_roi
         FROM
-        (select ifnull(COALESCE(COALESCE (tb2.movie_name, tb3.movie_name), tb4.name), tb1.movie_id) as movie_name,
-        rank() over (order by sum(end_amount) desc) as rk, sum(end_amount) as end_amount
+        (select ifnull(tb2.movie_name, tb1.movie_id) as movie_name, tb2.movie_source,
+        rank() over (order by sum(end_amount) desc) as rk, sum(end_amount) as end_amount, sum(share_end_amount) as share_end_amount
         from
         (select
-        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as end_amount, movie_id
+        ifnull(sum(iaa_amount), 0) + ifnull(sum(pay_amount), 0) as end_amount,
+        ifnull(sum(share_iaa_amount), 0) + ifnull(sum(share_pay_amount), 0) as share_end_amount,
+        movie_id
         from
         application.user_data_report_daily udrd
         where stat_date = ${date}
@@ -58,11 +61,7 @@
         order by end_amount desc
         ) 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
-        LEFT JOIN
-        (select id, concat('欢瑞自制-', name) as name from thirtpart.ly_videos group by id ) tb4 on tb1.movie_id = tb4.id
+        (select distinct moive_id, movie_name, movie_source from deepthink.all_moive) tb2 on tb1.movie_id=tb2.moive_id
         group by movie_name order by rk limit 10) ot1
         left join
         (select product_name, round(ifnull(sum(cost_amount),0),2) as cost_amount, sum(real_cost_amount) as real_cost_amount

+ 8 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IBytedanceProductUpdateService.java

@@ -0,0 +1,8 @@
+package cn.com.ctop.job.bytedance.data.service;
+
+public interface IBytedanceProductUpdateService {
+
+    void run() throws Exception;
+
+    void initNew();
+}

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

@@ -0,0 +1,407 @@
+package cn.com.ctop.job.bytedance.data.service.impl;
+
+import cn.com.ctop.job.bytedance.data.mapper.BytedanceProductUpdateMapper;
+import cn.com.ctop.job.bytedance.data.service.IBytedanceProductUpdateService;
+import cn.com.ctop.job.bytedance.data.utils.Check;
+import cn.com.ctop.job.bytedance.data.utils.HttpUtils;
+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.commons.lang.StringUtils;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+@Slf4j
+@Service
+public class BytedanceProductUpdateServiceImpl implements IBytedanceProductUpdateService {
+    @Resource
+    BytedanceProductUpdateMapper bytedanceProductUpdateMapper;
+
+    public static final String APP_ID = "ttba3acc87250c6f7901";
+    public static final String HOOK_URL = "https://open.feishu.cn/open-apis/bot/v2/hook/92e2445e-76df-44fb-8f61-f55ac94f49bd";
+
+    @Override
+    public void run() throws Exception {
+        List<String> errors = new ArrayList<>();
+        log.info("付费商品库更新中...");
+        iapProductUpdate(errors);
+        log.info("付费商品库更新完成");
+        log.info("免费商品库更新中...");
+        iaaProductUpdate(errors);
+        log.info("免费商品库更新完成");
+        if (!Check.isNull(errors)){
+//            List<String> users = Arrays.asList(
+//                    "44fbaed6" //zhaotx
+//                    ,"58e7a98a"  //xsq
+//            );
+            FeishuRobotUtil.sendTextToWebhook(StringUtils.join(errors, "\n"), HOOK_URL);
+        }
+
+    }
+
+
+    @Override
+    public void initNew(){
+        List<JSONObject> iapPlatformList = bytedanceProductUpdateMapper.getIAPPlatformList();
+        List<JSONObject> iaaPlatformList = bytedanceProductUpdateMapper.getIAAPlatformList();
+        initNew(iaaPlatformList);
+        initNew(iapPlatformList);
+    }
+
+    public void initNew(List<JSONObject> platformList)  {
+        //为广告主账户创建新的字节小程序
+        //step1 查询表里广告主是否都有自己的字节小程序、
+        //step2 如果没有,创建新的字节小程序
+        for (JSONObject obj : platformList){
+            if (obj.containsKey("mini_instance_id") && !Check.isNull(obj.get("mini_instance_id"))){
+                //已经有字节小程序了
+                continue;
+            }
+            String createLinkUrl = "https://api.oceanengine.com/open_api/v3.0/tools/micro_app/create/";
+            String advertiser_id = obj.getString("advertiser_id");
+            String access_token = obj.getString("access_token");
+            Map<String, String> headers = new HashMap<>();
+            headers.put("Access-Token", access_token);
+            JSONObject requestBody = new JSONObject();
+            requestBody.put("advertiser_id", Long.parseLong(advertiser_id));
+            requestBody.put("app_id", obj.getString("mi_appid"));
+            requestBody.put("remark", "商品库管理用");
+            requestBody.put("tag_info", "1050107001");
+            JSONObject app_page = new JSONObject();
+            app_page.put("link", "");
+            app_page.put("link_remark", "init");
+            app_page.put("start_page", "pages/index/index");
+            app_page.put("start_param", "link_id=574391");
+            requestBody.put("app_page", app_page);
+
+            String res = HttpUtils.httpPostRequest(createLinkUrl, requestBody, headers);
+            if (res == null || res.isEmpty()){
+                System.out.println("创建字节小程序失败");
+                return;
+            }else {
+                JSONObject jsonObject = JSONObject.parseObject(res);
+                int code = jsonObject.getIntValue("code");
+                if (code != 0) {
+                    System.out.println("创建字节小程序失败,code: " + code + ", msg: " + jsonObject.getString("message"));
+                    break;
+                }else {
+                    String instanceId = jsonObject.getJSONObject("data").getString("instance_id");
+                    //更新表
+                    bytedanceProductUpdateMapper.updateInstanceId(advertiser_id, instanceId);
+                }
+            }
+        }
+    }
+
+    private void iapProductUpdate(List<String> errorBlanket){
+        //IAP
+        List<JSONObject> iapPlatformList = bytedanceProductUpdateMapper.getIAPPlatformList();
+        List<JSONObject> iapVideoList = bytedanceProductUpdateMapper.getIAPVideoList();
+        List<JSONObject> iapProductTypeEnums = bytedanceProductUpdateMapper.getIAPProductTypeEnums();
+        Map<String, Map<String, String>> advertiserToLinkMap = new HashMap<>();
+
+        for (JSONObject obj : iapPlatformList){
+            String advertiserId = obj.getString("advertiser_id");
+            String accessToken = obj.getString("access_token");
+            String instanceId = obj.getString("mini_instance_id");
+            if (instanceId == null){
+                errorBlanket.add("没有找到字节小程序的实例ID: " + advertiserId);
+                System.out.println("没有找到字节小程序的实例ID: " + advertiserId);
+                continue;
+            }
+            Map<String, String> albumIdToLinkMap = getBytedancePlayletPromoteLink(advertiserId, accessToken, instanceId, iapVideoList, errorBlanket);
+            advertiserToLinkMap.put(advertiserId, albumIdToLinkMap);
+        }
+
+        Map<String, JSONObject> iapProductTypeMap = iapProductTypeEnums.stream().collect(Collectors.toMap(
+                obj -> obj.getString("third_type_name"),
+                obj -> obj,
+                (existing, replacement) -> replacement
+        ));
+        for (JSONObject obj : iapVideoList){
+            String theme = obj.getString("theme");
+            if (!iapProductTypeMap.containsKey(theme)){
+                errorBlanket.add("上传商品:没有找到对应的产品类型:" + theme);
+                System.out.println(">>>>没有找到对应的产品类型:" + theme);
+                System.out.println("\n\n\n");
+                continue;
+            }
+            for (JSONObject platform : iapPlatformList) {
+                String url = "https://ad.oceanengine.com/open_api/2/dpa/product/update/";
+                Map<String, String> headers = new HashMap<>();
+                headers.put("Access-Token", platform.getString("access_token"));
+                headers.put("Content-Type", "application/json");
+
+                JSONObject request = new JSONObject();
+                String album_id = obj.getString("album_id");
+                request.put("product_id", obj.getLongValue("album_id"));
+                JSONObject product_info = new JSONObject();
+                product_info.put("name", obj.getString("name"));
+                product_info.put("image_url", obj.getString("img"));
+                product_info.put("status", 1);
+                product_info.put("stock", 1);
+                JSONObject productType = iapProductTypeMap.get(theme);
+                product_info.put("first_category_id", productType.getString("first_type_id"));
+                product_info.put("sub_category_id", productType.getString("second_type_id"));
+                product_info.put("third_category_id", productType.getString("third_type_id"));
+                product_info.put("first_category", productType.getString("first_type_name"));
+                product_info.put("sub_category", productType.getString("second_type_name"));
+                product_info.put("third_category", productType.getString("third_type_name"));
+                request.put("advertiser_id", platform.getLongValue("advertiser_id"));
+                request.put("platform_id", platform.getLongValue("platform_id"));
+                JSONObject profession = new JSONObject();
+                profession.put("copyright_owner", obj.getString("copyright_owner"));
+                profession.put("playlet_gender", "3");
+                profession.put("playlet_num", obj.getString("playlet_num"));
+                profession.put("playlet_duration", obj.getString("playlet_duration"));
+                profession.put("start_pay_playlet", obj.getString("start_pay_playlet"));
+                if (platform.getDouble("max_charge") > 0.0){
+                    profession.put("max_charge", platform.getString("max_charge"));
+                }
+                if (platform.getDouble("min_charge") > 0.0){
+                    profession.put("min_charge", platform.getString("min_charge"));
+                }
+                if (platform.getDouble("recommended_charge") > 0.0){
+                    profession.put("recommended_charge", platform.getString("recommended_charge"));
+                }
+                profession.put("has_paid_content", "1");
+                profession.put("has_motivation_content", "2");
+                profession.put("membership_types", platform.getString("membership_types"));
+                profession.put("micro_app_link", advertiserToLinkMap.get(platform.getString("advertiser_id")).get(album_id));
+                //price_info
+                JSONObject price_info = new JSONObject();
+                price_info.put("price", platform.getDoubleValue("single_price"));
+                product_info.put("price_info", price_info);
+                product_info.put("profession", profession);
+                request.put("product_info", product_info);
+
+
+//                System.out.println("请求地址:" + url);
+//                System.out.println("请求头:" + headers);
+//                System.out.println("请求参数:" + request.toJSONString());
+                String res = HttpUtils.httpPostRequest(url, request, headers);
+//                System.out.println(res);
+                JSONObject btRes = JSONObject.parseObject(res);
+                int code = btRes.getIntValue("code");
+                if (code != 0){
+                    errorBlanket.add("上传付费商品失败,code: " + code + ", msg: " + btRes.getString("message")+", 关联剧目: " + obj.getString("name"));
+                }
+//                System.out.println("\n\n\n");
+            }
+        }
+    }
+
+    private void iaaProductUpdate(List<String> errorBlanket){
+        //IAA
+        List<JSONObject> iaaPlatformList = bytedanceProductUpdateMapper.getIAAPlatformList();
+        List<JSONObject> iaaVideoList = bytedanceProductUpdateMapper.getIAAVideoList();
+        List<JSONObject> iaaProductTypeEnums = bytedanceProductUpdateMapper.getIAAProductTypeEnums();
+
+        Map<String, Map<String, String>> advertiserToLinkMap = new HashMap<>();
+
+        for (JSONObject obj : iaaPlatformList){
+            String advertiserId = obj.getString("advertiser_id");
+            String accessToken = obj.getString("access_token");
+            String instanceId = obj.getString("mini_instance_id");
+            if (instanceId == null){
+                errorBlanket.add("没有找到字节小程序的实例ID: " + advertiserId);
+                System.out.println("没有找到字节小程序的实例ID: " + advertiserId);
+                continue; //todo
+            }
+            Map<String, String> albumIdToLinkMap = getBytedancePlayletPromoteLink(advertiserId, accessToken, instanceId, iaaVideoList, errorBlanket);
+            advertiserToLinkMap.put(advertiserId, albumIdToLinkMap);
+        }
+
+        Map<String, JSONObject> iaaProductTypeMap = iaaProductTypeEnums.stream().collect(Collectors.toMap(
+                obj -> obj.getString("third_type_name"),
+                obj -> obj,
+                (existing, replacement) -> replacement
+        ));
+        for (JSONObject obj : iaaVideoList){
+            String theme = obj.getString("theme");
+            if (!iaaProductTypeMap.containsKey(theme)){
+                errorBlanket.add("上传商品:没有找到对应的产品类型:" + theme);
+                System.out.println(">>>>没有找到对应的产品类型:" + theme);
+                System.out.println("\n\n\n");
+                continue;
+            }
+
+
+            for (JSONObject platform : iaaPlatformList) {
+                String url = "https://ad.oceanengine.com/open_api/2/dpa/product/update/";
+                Map<String, String> headers = new HashMap<>();
+                headers.put("Access-Token", platform.getString("access_token"));
+                headers.put("Content-Type", "application/json");
+                String album_id = obj.getString("album_id");
+                JSONObject request = new JSONObject();
+                request.put("product_id", obj.getLongValue("album_id"));
+                JSONObject product_info = new JSONObject();
+                product_info.put("name", obj.getString("name"));
+                product_info.put("image_url", obj.getString("img"));
+                product_info.put("status", 1);
+                product_info.put("stock", 1);
+                JSONObject productType = iaaProductTypeMap.get(theme);
+                product_info.put("first_category_id", productType.getString("first_type_id"));
+                product_info.put("sub_category_id", productType.getString("second_type_id"));
+                product_info.put("third_category_id", productType.getString("third_type_id"));
+                product_info.put("first_category", productType.getString("first_type_name"));
+                product_info.put("sub_category", productType.getString("second_type_name"));
+                product_info.put("third_category", productType.getString("third_type_name"));
+                JSONObject profession = new JSONObject();
+                profession.put("copyright_owner", obj.getString("copyright_owner"));
+                profession.put("playlet_gender", "3");
+                profession.put("playlet_num", obj.getString("playlet_num"));
+                profession.put("playlet_duration", obj.getString("playlet_duration"));
+                profession.put("has_paid_content", "2");
+                profession.put("has_motivation_content", "2");
+                profession.put("membership_types", "5");
+                profession.put("starting_unlock_episode", obj.getString("starting_unlock_episode"));
+                profession.put("micro_app_link", advertiserToLinkMap.get(platform.getString("advertiser_id")).get(album_id));
+                product_info.put("profession", profession);
+                request.put("product_info", product_info);
+                request.put("advertiser_id", platform.getLongValue("advertiser_id"));
+                request.put("platform_id", platform.getLongValue("platform_id"));
+
+//                System.out.println("请求地址:" + url);
+//                System.out.println("请求头:" + headers);
+//                System.out.println("请求参数:" + request.toJSONString());
+                String res = HttpUtils.httpPostRequest(url, request, headers);
+                JSONObject btRes = JSONObject.parseObject(res);
+                int code = btRes.getIntValue("code");
+                if (code != 0){
+                    errorBlanket.add("上传免费商品失败,code: " + code + ", msg: " + btRes.getString("message")+", 关联剧目: " + obj.getString("name"));
+                }
+//                System.out.println(res);
+//                System.out.println("\n\n\n");
+            }
+        }
+    }
+
+    /**
+     *
+     * @param advertiserId 商品库主账户ID,也是创建字节小程序的ID
+     * @param accessToken 广告账号的access_token
+     * @param instanceId 字节小程序的实例ID
+     * @return map key: album_id, value: 对应的字节小程序推广链接,sslocal开头
+     */
+    private Map<String, String> getBytedancePlayletPromoteLink(String advertiserId, String accessToken, String instanceId, List<JSONObject> requests, List<String> errorBlanket){
+        JSONArray playletDetailInfo = getPlayletDetail(advertiserId, accessToken, instanceId, errorBlanket);
+        if (playletDetailInfo == null || playletDetailInfo.isEmpty()){
+            return null;
+        }
+        List<String> reqAlbumIds = requests.stream()
+                .map(obj -> obj.getString("album_id"))
+                .collect(Collectors.toList());
+        Map<String, String> res = new HashMap<>(reqAlbumIds.size());
+
+        for (int i = 0; i < playletDetailInfo.size(); i++) {
+            JSONObject obj = playletDetailInfo.getJSONObject(i);
+            String startParam = obj.getString("start_param");
+            for (String album_id : reqAlbumIds){
+                if (startParam.contains("tt_album_id="+album_id)){
+                    res.put(album_id, obj.getString("link"));
+                }
+            }
+        }
+        List<JSONObject> unfound = requests.stream()
+                .filter(obj -> !res.containsKey(obj.getString("album_id")))
+                .collect(Collectors.toList());
+        if (unfound.isEmpty()){
+            return res;
+        }
+        //没有找到对应的album_id,需要新建链接
+        for (JSONObject obj : unfound){
+            String editLinkUrl = "https://api.oceanengine.com/open_api/v3.0/tools/micro_app/update/";
+            Map<String, String> headers = new HashMap<>();
+            headers.put("Access-Token", accessToken);
+            JSONObject requestBody = new JSONObject();
+            requestBody.put("advertiser_id", Long.parseLong(advertiserId));
+            requestBody.put("instance_id", Long.parseLong(instanceId));
+            JSONObject app_page = new JSONObject();
+            String name = obj.getString("name");
+            app_page.put("link", "");
+            app_page.put("remark", "生成商品用-"+name);
+            app_page.put("link_remark", "生成商品用-"+name);
+            app_page.put("operate_type", "NEW");
+            app_page.put("start_page", "pages/index/index");
+            String video_id = obj.getString("video_id");
+            String album_id = obj.getString("album_id");
+            String video_detail_id = obj.getString("video_detail_id");
+            String platform_config = obj.getString("platform_config");
+            JSONObject pconfig = JSONObject.parseObject(platform_config);
+            String tt_episode_id = pconfig.getJSONObject("douyinyun").getString("episode_id");
+            String start_param = "tt_album_id=" + album_id + "&video_id=" + video_id + "&video_detail_id=" + video_detail_id + "&tt_episode_id=" + tt_episode_id + "&source=link&type=1";
+            app_page.put("start_param", start_param);
+            JSONArray app_page_list = new JSONArray();
+            app_page_list.add(app_page);
+            requestBody.put("app_page", app_page_list);
+            requestBody.put("tag_info", "1050107001");
+//            System.out.println("\n\n请求地址:" + editLinkUrl);
+//            System.out.println("请求头:" + headers);
+//            System.out.println("请求参数:" + requestBody.toJSONString());
+
+            String rr = HttpUtils.httpPostRequest(editLinkUrl, requestBody, headers);
+            if (rr == null || rr.isEmpty()){
+                errorBlanket.add("创建字节小程序推广链接失败, 广告账号ID:" + advertiserId + ", 小程序资产ID:" + instanceId);
+                System.out.println("创建字节小程序推广链接失败");
+                return res;
+            }else {
+                JSONObject jsonObject = JSONObject.parseObject(rr);
+                int code = jsonObject.getIntValue("code");
+                if (code != 0) {
+                    errorBlanket.add("创建字节小程序推广链接失败,code: " + code + ", msg: " + jsonObject.getString("message") + ", 广告账号ID:" + advertiserId + ", 小程序资产ID:" + instanceId);
+                    System.out.println("创建字节小程序推广链接失败,code: " + code + ", msg: " + jsonObject.getString("message"));
+                    continue;
+                }else {
+                    String instanceid = jsonObject.getJSONObject("data").getString("instance_id");
+                    JSONArray newPlayletDetailInfo = getPlayletDetail(advertiserId, accessToken, instanceid, errorBlanket);
+                    if (newPlayletDetailInfo == null || newPlayletDetailInfo.isEmpty()){
+                        continue;
+                    }
+                    for (int i = 0; i < newPlayletDetailInfo.size(); i++) {
+                        JSONObject link = newPlayletDetailInfo.getJSONObject(i);
+                        String startParam = link.getString("start_param");
+                        if (startParam.contains("tt_album_id="+album_id)){
+                            res.put(album_id, link.getString("link"));
+                        }
+                    }
+                }
+            }
+        }
+        return res;
+
+    }
+
+    private JSONArray getPlayletDetail(String advertiserId, String accessToken, String instanceId, List<String> errorBlanket) {
+        String getPlayletDetailUrl = "https://api.oceanengine.com/open_api/v3.0/tools/asset_link/list/";
+        getPlayletDetailUrl = getPlayletDetailUrl.replace("${ADVERTISER_ID}", advertiserId);
+        getPlayletDetailUrl = getPlayletDetailUrl.replace("${INSTANCE_ID}", instanceId);
+        Map<String, String> headers = new HashMap<>();
+        headers.put("Access-Token", accessToken);
+        Map<String, Object> params = new HashMap<>();
+        params.put("advertiser_id", advertiserId);
+        params.put("page", "1");
+        params.put("page_size", "100");
+        params.put("filtering", "{\"instance_id\":" + instanceId + "}");
+        String res = HttpUtils.httpGet(getPlayletDetailUrl, params, headers);
+        if (res == null || res.isEmpty()) {
+            errorBlanket.add("获取字节小程序推广链接失败, 广告账号ID:" + advertiserId + ", 小程序资产ID:" + instanceId);
+            return null;
+        }else {
+            JSONObject jsonObject = JSONObject.parseObject(res);
+            int code = jsonObject.getIntValue("code");
+            if (code != 0) {
+                errorBlanket.add("获取字节小程序推广链接失败,code: " + code + ", msg: " + jsonObject.getString("message") + ", 广告账号ID:" + advertiserId + ", 小程序资产ID:" + instanceId);
+                System.out.println("获取字节小程序推广链接失败,code: " + code + ", msg: " + jsonObject.getString("message"));
+                return null;
+            }else {
+                return jsonObject.getJSONObject("data").getJSONArray("list");
+            }
+        }
+
+    }
+
+}

+ 31 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/handler/BytedanceProductUploadJob.java

@@ -0,0 +1,31 @@
+package cn.com.ctop.job.bytedance.handler;
+
+import cn.com.ctop.job.bytedance.data.service.IBytedanceProductUpdateService;
+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 BytedanceProductUploadJob {
+    @Autowired
+    IBytedanceProductUpdateService bytedanceProductUpdateService;
+
+    /**
+     * 创建和更新推广链
+     * @throws Exception
+     */
+    @XxlJob("productUploadJob")
+    public void productUploadJob() throws Exception {
+        bytedanceProductUpdateService.run();
+    }
+
+    /**
+     * 初始化推广链接用的字节小程序
+     */
+    @XxlJob("initProductLinkApp")
+    public void initProductLinkApp(){
+        bytedanceProductUpdateService.initNew();
+    }
+}

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

@@ -114,4 +114,49 @@ public class FeishuRobotUtil {
             return null;
         }
     }
+
+    public static void sendTextToDt(String text, List<String> userIds) throws IOException {
+        JSONObject content = new JSONObject();
+        content.put("text", text);
+        String token = getFsZtTenantToken();
+
+        for (String userId : userIds){
+            JSONObject request = new JSONObject();
+            request.put("receive_id", userId);
+            request.put("msg_type", "text");
+            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){
+                System.out.println("发送飞书消息失败");
+            }
+        }
+    }
+
+    public static void sendTextToWebhook(String text, String url) throws IOException {
+        JSONObject content = new JSONObject();
+        content.put("text", text);
+        JSONObject request = new JSONObject();
+        request.put("msg_type", "text");
+        request.put("content", content.toJSONString());
+        //发送
+        try (CloseableHttpClient client = HttpClients.createDefault()){
+            HttpPost httpPost = new HttpPost(url);
+            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){
+            System.out.println("发送飞书消息失败");
+        }
+
+    }
 }