yumeng 2 周之前
父节点
当前提交
dda38510df

+ 202 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/common/module/entity/BytedanceCdpProject.java

@@ -0,0 +1,202 @@
+package cn.com.ctop.common.module.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecgframework.poi.excel.annotation.Excel;
+
+/**
+ * 项目信息
+ * @date 2023-04-19
+ */
+@Data
+@TableName("bytedance_cdp_project")
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value = "bytedance_cdp_project对象", description = "项目信息")
+public class BytedanceCdpProject {
+
+    /**
+     * 项目id
+     */
+    @Excel(name = "项目id", width = 15)
+    @ApiModelProperty(value = "项目id")
+    private Long projectId;
+    /**
+     * 项目名称
+     */
+    @Excel(name = "项目名称", width = 15)
+    @ApiModelProperty(value = "项目名称")
+    private String name;
+    /**
+     * 账户id
+     */
+    @Excel(name = "账户id", width = 15)
+    @ApiModelProperty(value = "账户id")
+    private Long advertiserId;
+    /**
+     * 项目状态
+     */
+    @Excel(name = "项目状态", width = 15)
+    @ApiModelProperty(value = "项目状态")
+    private String status;
+    /**
+     * 应用包名
+     */
+    @Excel(name = "应用包名", width = 15)
+    @ApiModelProperty(value = "应用包名")
+    private String packageName;
+    /**
+     * 项目创建时间,格式yyyy-MM-dd HH:mm:ss
+     */
+    @Excel(name = "项目创建时间,格式yyyy-MM-dd HH:mm:ss", width = 15)
+    @ApiModelProperty(value = "项目创建时间,格式yyyy-MM-dd HH:mm:ss")
+    private String projectCreateTime;
+    /**
+     * 项目更新时间,格式yyyy-MM-dd HH:mm:ss
+     */
+    @Excel(name = "项目更新时间,格式yyyy-MM-dd HH:mm:ss", width = 15)
+    @ApiModelProperty(value = "项目更新时间,格式yyyy-MM-dd HH:mm:ss")
+    private String projectModifyTime;
+    /**
+     * 出价方式
+     */
+    @Excel(name = "出价方式", width = 15)
+    @ApiModelProperty(value = "出价方式")
+    private String pricing;
+    /**
+     * 推广目的,枚举值:APP 应用推广、LINK 销售线索推广、MICRO_GAME小程序、QUICK_APP快应用
+     */
+    @Excel(name = "推广目的,枚举值:APP 应用推广、LINK 销售线索推广、MICRO_GAME小程序、QUICK_APP快应用", width = 15)
+    @ApiModelProperty(value = "推广目的,枚举值:APP 应用推广、LINK 销售线索推广、MICRO_GAME小程序、QUICK_APP快应用")
+    private String landingType;
+    /**
+     * 广告类型,枚举值:ALL所有广告,SEARCH 搜索广告
+     */
+    @Excel(name = "广告类型,枚举值:ALL所有广告,SEARCH 搜索广告", width = 15)
+    @ApiModelProperty(value = "广告类型,枚举值:ALL所有广告,SEARCH 搜索广告")
+    private String adType;
+    /**
+     * 定向拓展
+     */
+    @Excel(name = "定向拓展", width = 15)
+    @ApiModelProperty(value = "定向拓展")
+    private String audienceExtend;
+    /**
+     * 目标操作,枚举值:ENABLE 启用项目、DISABLE暂停项目
+     */
+    @Excel(name = "目标操作,枚举值:ENABLE 启用项目、DISABLE暂停项目", width = 15)
+    @ApiModelProperty(value = "目标操作,枚举值:ENABLE 启用项目、DISABLE暂停项目")
+    private String optStatus;
+    /**
+     * 子目标,枚举值:DOWNLOAD 应用下载、LAUNCH 应用调用、RESERVE 预约下载
+     */
+    @Excel(name = "子目标,枚举值:DOWNLOAD 应用下载、LAUNCH 应用调用、RESERVE 预约下载", width = 15)
+    @ApiModelProperty(value = "子目标,枚举值:DOWNLOAD 应用下载、LAUNCH 应用调用、RESERVE 预约下载")
+    private String appPromotionType;
+    /**
+     * 投放模式,允许值:MANUAL手动投放、PROCEDURAL自动投放
+     */
+    @Excel(name = "投放模式,允许值:MANUAL手动投放、PROCEDURAL自动投放", width = 15)
+    @ApiModelProperty(value = "投放模式,允许值:MANUAL手动投放、PROCEDURAL自动投放")
+    private String deliveryMode;
+    /**
+     * 出价系数
+     */
+    @Excel(name = " 出价系数", width = 15)
+    @ApiModelProperty(value = " 出价系数")
+    private java.math.BigDecimal searchBidRatio;
+    /**
+     * 下载链接
+     */
+    @Excel(name = " 下载链接", width = 15)
+    @ApiModelProperty(value = " 下载链接")
+    private String downloadUrl;
+    /**
+     * 优先从系统应用商店下载(下载模式),枚举值:APP_STORE_DELIVERY 优先商店下载、DEFAULT 默认下载
+     */
+    @Excel(name = "优先从系统应用商店下载(下载模式),枚举值:APP_STORE_DELIVERY 优先商店下载、DEFAULT 默认下载", width = 15)
+    @ApiModelProperty(value = "优先从系统应用商店下载(下载模式),枚举值:APP_STORE_DELIVERY 优先商店下载、DEFAULT 默认下载")
+    private String downloadMode;
+    /**
+     * 下载方式,枚举值:DOWNLOAD_URL 直接下载、EXTERNAL_URL 落地页下载
+     */
+    @Excel(name = "下载方式,枚举值:DOWNLOAD_URL 直接下载、EXTERNAL_URL 落地页下载", width = 15)
+    @ApiModelProperty(value = "下载方式,枚举值:DOWNLOAD_URL 直接下载、EXTERNAL_URL 落地页下载")
+    private String downloadType;
+    /**
+     * 应用名
+     */
+    @Excel(name = "应用名", width = 15)
+    @ApiModelProperty(value = "应用名")
+    private String appName;
+    /**
+     * 搜索快投关键词功能,HAS_OPEN:启用,DISABLED:未启用
+     */
+    @Excel(name = "搜索快投关键词功能,HAS_OPEN:启用,DISABLED:未启用", width = 15)
+    @ApiModelProperty(value = "搜索快投关键词功能,HAS_OPEN:启用,DISABLED:未启用")
+    private String feedDeliverySearch;
+    /**
+     * 营销场景,枚举值:VIDEO_AND_IMAGE 短视频/图片、LIVE直播
+     */
+    @Excel(name = "营销场景,枚举值:VIDEO_AND_IMAGE 短视频/图片、LIVE直播", width = 15)
+    @ApiModelProperty(value = "营销场景,枚举值:VIDEO_AND_IMAGE 短视频/图片、LIVE直播")
+    private String marketingGoal;
+    /**
+     * 搜索关键词列表
+     */
+    @Excel(name = "搜索关键词列表", width = 15)
+    @ApiModelProperty(value = "搜索关键词列表")
+    private String keywords;
+    /**
+     * 投放设置
+     */
+    @Excel(name = " 投放设置", width = 15)
+    @ApiModelProperty(value = " 投放设置")
+    private String deliverySetting;
+    /**
+     * 自定义筛选条件(商品投放条件)
+     */
+    @Excel(name = "自定义筛选条件(商品投放条件)", width = 15)
+    @ApiModelProperty(value = "自定义筛选条件(商品投放条件)")
+    private String dpaProductTarget;
+    /**
+     * 优化目标
+     */
+    @Excel(name = "优化目标", width = 15)
+    @ApiModelProperty(value = "优化目标")
+    private String optimizeGoal;
+    /**
+     * 定向设置
+     */
+    @Excel(name = "定向设置", width = 15)
+    @ApiModelProperty(value = "定向设置")
+    private String audience;
+    /**
+     * 关联产品投放相关
+     */
+    @Excel(name = "关联产品投放相关", width = 15)
+    @ApiModelProperty(value = "关联产品投放相关")
+    private String relatedProduct;
+    /**
+     * 广告版位
+     */
+    @Excel(name = "广告版位", width = 15)
+    @ApiModelProperty(value = "广告版位")
+    private String deliveryRange;
+    /**
+     * 监测链接设置
+     */
+    @Excel(name = "监测链接设置", width = 15)
+    @ApiModelProperty(value = "监测链接设置")
+    private String trackUrlSetting;
+    /**
+     * internalAdvertiserInfo
+     */
+    @Excel(name = "internalAdvertiserInfo", width = 15)
+    @ApiModelProperty(value = "internalAdvertiserInfo")
+    private String internalAdvertiserInfo;
+}

+ 46 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/common/module/entity/MediaSendAlarmRecord.java

@@ -0,0 +1,46 @@
+package cn.com.ctop.common.module.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * @Description: 媒体预警发送表
+ */
+@Data
+@TableName("ctop_media_send_alarm_record")
+@Accessors(chain = true)
+@ApiModel(value = "ctop_media_send_alarm_record对象", description = "媒体预警发送表")
+public class MediaSendAlarmRecord {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private Long accountId;
+    private Long planId;
+    private Long projectId;
+    private String userId;
+    private String alarmDetail;
+    private String mediaType;
+    /**
+     * createTime
+     */
+    private Date createTime;
+    /**
+     * updateTime
+     */
+    private Date updateTime;
+
+    public MediaSendAlarmRecord(Long accountId, String mediaType, String alarmDetail) {
+        this.accountId = accountId;
+        this.mediaType = mediaType;
+        this.alarmDetail = alarmDetail;
+    }
+
+    public MediaSendAlarmRecord() {
+    }
+}

+ 35 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/common/module/mapper/BytedanceProjectMapper.java

@@ -0,0 +1,35 @@
+package cn.com.ctop.common.module.mapper;
+
+
+
+import cn.com.ctop.common.module.entity.BytedanceCdpProject;
+import cn.com.ctop.common.module.entity.MediaSendAlarmRecord;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface BytedanceProjectMapper {
+
+    Long getProjectId(@Param("accountId") Long accountId);
+
+    int replaceProjectList(@Param("list") List<BytedanceCdpProject> list);
+
+    List<JSONObject> getWChatIdsByAccountId(@Param("accountId") Long accountId);
+
+    void insertSendRecord(MediaSendAlarmRecord record);
+
+    List<Long> getBytedanceRefusalSend(@Param("accountId") Long accountId);
+
+    List<Long> getSpecialProject();
+
+    void insertSendAccountId(@Param("accountId") Long accountId, @Param("adId") Long adId, @Param("counts") Integer counts);
+
+    Integer getrefusalAccountCounts(@Param("accountId") Long accountId, @Param("adId") Long adId);
+
+    JSONObject getProjectByAccountId(@Param("accountId") Long accountId);
+
+    List<JSONObject> getBytedanceOcpxTypeList();
+
+    String getTokenByAccountId(@Param("advertiserId") Long advertiserId);
+}

+ 183 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/common/module/mapper/xml/BytedanceProjectMapper.xml

@@ -0,0 +1,183 @@
+<?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.common.module.mapper.BytedanceProjectMapper">
+
+
+    <insert id="replaceProjectList">
+        REPLACE INTO bytedance_cdp_project (
+        project_id,
+        `name`,
+        advertiser_id,
+        status,
+        package_name,
+        project_create_time,
+        project_modify_time,
+        pricing,
+        landing_type,
+        ad_type,
+        audience_extend,
+        opt_status,
+        app_promotion_type,
+        delivery_mode,
+        search_bid_ratio,
+        download_url,
+        download_mode,
+        download_type,
+        app_name,
+        feed_delivery_search,
+        marketing_goal,
+        keywords,
+        delivery_setting,
+        dpa_product_target,
+        optimize_goal,
+        audience,
+        related_product,
+        delivery_range,
+        track_url_setting,
+        internal_advertiser_info
+        )
+        VALUES
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.projectId},
+            #{item.name},
+            #{item.advertiserId},
+            #{item.status},
+            #{item.packageName},
+            #{item.projectCreateTime},
+            #{item.projectModifyTime},
+            #{item.pricing},
+            #{item.landingType},
+            #{item.adType},
+            #{item.audienceExtend},
+            #{item.optStatus},
+            #{item.appPromotionType},
+            #{item.deliveryMode},
+            #{item.searchBidRatio},
+            #{item.downloadUrl},
+            #{item.downloadMode},
+            #{item.downloadType},
+            #{item.appName},
+            #{item.feedDeliverySearch},
+            #{item.marketingGoal},
+            #{item.keywords},
+            #{item.deliverySetting},
+            #{item.dpaProductTarget},
+            #{item.optimizeGoal},
+            #{item.audience},
+            #{item.relatedProduct},
+            #{item.deliveryRange},
+            #{item.trackUrlSetting},
+            #{item.internalAdvertiserInfo}
+            )
+        </foreach>
+    </insert>
+
+    <select id="getProjectId" resultType="Long">
+        SELECT project_id
+        FROM `jeecg-boot`.ctop_user_allocation
+        WHERE account_id = #{accountId}
+    </select>
+
+    <!-- 获取用户微信id -->
+    <select id="getWChatIdsByAccountId" resultType="com.alibaba.fastjson.JSONObject">
+        select wexin_id, corp_id, wexin_name
+        from `jeecg-boot`.ctop_corp_wexin_user
+        where user_id = (select ctop_user_allocation.user_id
+                         from `jeecg-boot`.ctop_user_allocation
+                         where account_id = #{accountId} limit 1)
+        union all
+        select wexin_id, corp_id, wexin_name
+        from `jeecg-boot`.ctop_corp_wexin_user
+        where user_id = (select leader_id
+                         from `jeecg-boot`.sys_user
+                         where id = (SELECT ctop_user_allocation.user_id
+                                     FROM `jeecg-boot`.ctop_user_allocation
+                                     WHERE account_id = #{accountId}
+            LIMIT 1))
+        union all
+        select wexin_id, corp_id, wexin_name
+        from `jeecg-boot`.ctop_corp_wexin_user
+        where user_id = 'b07f2bdd0f914f1dab2b4e841764911f';
+    </select>
+
+    <insert id="insertSendRecord">
+        insert
+        into `jeecg-boot`.ctop_media_send_alarm_record
+        (account_id,
+         plan_id,
+         project_id,
+         user_id,
+         alarm_detail,
+         media_type)
+        values (#{accountId},
+                #{planId},
+                #{projectId},
+                #{userId},
+                #{alarmDetail},
+                #{mediaType})
+    </insert>
+
+    <insert id="insertSendAccountId">
+        replace
+        into `jeecg-boot`.bytedance_refusal_send
+        (account_id,ad_id,
+         counts,
+         stat_date)
+        values (
+        #{accountId},
+        #{adId},
+        #{counts},
+        (DATE_FORMAT(now(),'%Y%m%d-%H'))
+        )
+    </insert>
+
+
+    <select id="getBytedanceRefusalSend" resultType="Long">
+        SELECT ad_id
+        FROM `jeecg-boot`.bytedance_refusal_send
+        WHERE stat_date = DATE_FORMAT(now(), '%Y%m%d-%H')
+          AND account_id =#{accountId}
+          AND counts >= 2
+    </select>
+
+    <select id="getSpecialProject" resultType="Long">
+        SELECT IFNULL(project_id,-1)
+        FROM `jeecg-boot`.bytedance_refusal_send
+        WHERE flag = 1
+        UNION
+        SELECT IFNULL(account_id,-1)
+        FROM `jeecg-boot`.bytedance_refusal_send
+        WHERE flag = 1
+    </select>
+
+    <select id="getrefusalAccountCounts" resultType="Integer">
+        SELECT ifNUll(counts, 0)
+        FROM `jeecg-boot`.bytedance_refusal_send
+        WHERE account_id = #{accountId}
+          AND ad_id = #{adId}
+          AND stat_date = DATE_FORMAT(now(), '%Y%m%d-%H')
+    </select>
+    <select id="getProjectByAccountId" resultType="com.alibaba.fastjson.JSONObject">
+        select id 'projectId', project_name 'projectName', max_bid 'maxBid', bid_type 'bidType', ocpx_action_type 'ocpxActionType', deep_conversion_type 'deepConversionType'
+        from `jeecg-boot`.ctop_project
+        where id = (
+            select project_id
+            from `jeecg-boot`.ctop_user_allocation
+            where account_id = #{accountId}
+            limit 1
+            )
+    </select>
+    <select id="getBytedanceOcpxTypeList" resultType="com.alibaba.fastjson.JSONObject">
+        select ocpx_action_type as 'ocpxActionType', ocpx_action_name as 'ocpxActionName'
+        from `jeecg-boot`.ctop_bytedance_ocpx_action_type_config
+    </select>
+    <select id="getTokenByAccountId" resultType="java.lang.String">
+        select access_token
+        from `jeecg-boot`.ctop_oauth_token
+        where account_id = #{advertiserId} limit 1
+
+    </select>
+
+
+</mapper>

+ 16 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/common/module/service/IBytedanceProjectService.java

@@ -0,0 +1,16 @@
+package cn.com.ctop.common.module.service;
+
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.List;
+
+/**
+ * 获取项目列表
+ */
+public interface IBytedanceProjectService {
+    void updProjectStatus(String token, Long accountId, Long projectId, String disable);
+
+    void checkAdvertiserProject(Long accountId, String accessToken, List<Long> ids, int i);
+
+    String getTokenByAccountId(Long advertiserId);
+}

+ 384 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/common/module/service/impl/BytedanceProjectServiceImpl.java

@@ -0,0 +1,384 @@
+package cn.com.ctop.common.module.service.impl;
+
+
+import cn.com.ctop.common.module.entity.BytedanceCdpProject;
+import cn.com.ctop.common.module.entity.MediaSendAlarmRecord;
+import cn.com.ctop.common.module.mapper.BytedanceProjectMapper;
+import cn.com.ctop.common.module.service.IBytedanceProjectService;
+import cn.com.ctop.common.module.utils.Check;
+import cn.com.ctop.common.module.utils.CorpWexinUtils;
+import cn.com.ctop.common.module.utils.HttpUtils;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.util.RedisUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 获取项目列表
+ */
+@Service
+@Slf4j
+public class BytedanceProjectServiceImpl implements IBytedanceProjectService {
+
+
+    private static String bytedanceApiUrl = "https://ad.oceanengine.com/open_api";
+    @Resource
+    BytedanceProjectMapper projectListMapper;
+
+
+    @Autowired
+    private RedisUtil redisUtil;
+
+    @Resource
+    private CorpWexinUtils corpWexinUtils;
+
+
+    @Override
+    public void updProjectStatus(String token, Long accountId, Long projectId, String disable) {
+        JSONObject params = new JSONObject();
+        params.put("advertiser_id", accountId);
+        JSONObject data = new JSONObject();
+        data.put("project_id", projectId);
+        data.put("opt_status", disable);//ENABLE 启用项目、 DISABLE 暂停项目
+        List<JSONObject> list = new ArrayList<>();
+        list.add(data);
+        params.put("data", list);
+        String url = bytedanceApiUrl + "/v3.0/project/status/update/";
+        JSONObject object = HttpUtils.bytedancePostRequest(token, url, params);
+    }
+
+
+    @Override
+    public void checkAdvertiserProject(Long accountId, String accessToken, List<Long> ids, int page) {
+        try {
+            // 请求地址
+            String url = bytedanceApiUrl + "/v3.0/project/list/";
+            JSONObject param = new JSONObject();
+            param.put("advertiser_id", accountId);
+            param.put("page", page);
+            param.put("page_size", 100);
+            JSONObject filtering = new JSONObject();
+            if (!Check.isNull(ids)) {
+                filtering.put("ids", new ArrayList<>(ids));
+            }
+            param.put("filtering", filtering);
+            JSONObject resultObject = HttpUtils.bytedanceGetRequest(accessToken, url, param);
+            if (Check.isNull(resultObject)) {
+                log.error("checkAdvertiserProject异常==》accountId:{},结果为null", accountId);
+                return;
+            }
+            Integer code = resultObject.getInteger("code");
+            if (null == code || !code.equals(0)) {
+                log.error("checkAdvertiserProject接口==》accountId:{},message:{},code{}", accountId, resultObject.getString("message"), code);
+                return;
+            }
+            JSONArray data = resultObject.getJSONObject("data").getJSONArray("list");
+            if (null == data || data.isEmpty()) {
+                log.info("checkAdvertiserProject数据为空==》accountId:{},message:{},code:{}", accountId, resultObject.getString("message"), code);
+                return;
+            }
+            for (int i = 0; i < data.size(); i++) {
+                JSONObject dataObject = data.getJSONObject(i);
+                checkProjectBidType(accessToken, dataObject);
+
+            }
+            int totalPage = resultObject.getJSONObject("data").getJSONObject("page_info").getInteger("total_page");
+            if (page <= totalPage) {
+                checkAdvertiserProject(accountId, accessToken, ids, page + 1);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public String getTokenByAccountId(Long advertiserId) {
+        return projectListMapper.getTokenByAccountId(advertiserId);
+    }
+
+
+    //检查过滤账户和项目 flag为1 过滤掉
+    private boolean checkThreeRejections(Long accountId) {
+        Long projectId = projectListMapper.getProjectId(accountId);
+        List<Long> ids = projectListMapper.getSpecialProject();
+        if (ids.contains(accountId) || ids.contains(projectId)) {
+            //过滤标记 都不发送
+            return false;
+        }
+        return true;
+    }
+
+    //检查广告ID 发送2次以后拒绝发送
+    private boolean checkAdSendCounts(Long accountId, Long adId) {
+        List<Long> adIds = projectListMapper.getBytedanceRefusalSend(accountId);
+        if (!Check.isNull(adIds)) {
+            return !adIds.contains(adId);
+        }
+        return true;
+    }
+
+
+    /**
+     * 校验转化出价,并预警
+     */
+    public void checkProjectBidType(String accessToken, JSONObject detail) {
+        Long accountId = detail.getLong("advertiser_id");
+        //项目ID
+        JSONObject json = projectListMapper.getProjectByAccountId(accountId);
+        if (Check.isNull(json) || Check.isNull(detail)) {
+            return;
+        }
+
+        Map<String, String> textMap = new HashMap<>();
+        List<JSONObject> lists = projectListMapper.getBytedanceOcpxTypeList();
+        for (JSONObject da : lists) {
+            textMap.put(da.getString("ocpxActionType"), da.getString("ocpxActionName"));
+        }
+
+        //转化目标map
+        Map<String, String> ocpxMap = new HashMap<>();
+        String ocpxTypeStr = json.getString("ocpxActionType");
+        if (!Check.isNull(ocpxTypeStr)) {
+            JSONArray ocpxArr = JSONArray.parseArray(ocpxTypeStr);
+            StringBuffer nameStr = new StringBuffer();
+            for (int i = 0; i < ocpxArr.size(); i++) {
+                JSONObject obj = ocpxArr.getJSONObject(i);
+                ocpxMap.put(obj.getString("ocpxCode"), obj.getString("maxValue"));
+                nameStr.append(textMap.get(obj.getString("ocpxCode"))).append(" ");
+            }
+            if (ocpxArr.size() > 0) {
+                ocpxMap.put("nameStr", nameStr.toString());
+            }
+        }
+
+        //深度转化目标map
+        Map<String, String> deepMap = new HashMap<>();
+        String deepTypeStr = json.getString("deepConversionType");
+        if (!Check.isNull(deepTypeStr)) {
+            JSONArray deepArr = JSONArray.parseArray(deepTypeStr);
+            StringBuffer nameStr = new StringBuffer();
+            for (int i = 0; i < deepArr.size(); i++) {
+                JSONObject obj = deepArr.getJSONObject(i);
+                deepMap.put(obj.getString("deepCode"), obj.getString("deepValue"));
+                nameStr.append(textMap.get(obj.getString("deepCode"))).append(" ");
+            }
+            if (deepArr.size() > 0) {
+                deepMap.put("nameStr", nameStr.toString());
+            }
+        }
+
+        //操作状态
+        String optStatus = detail.getString("opt_status");// ENABLE 启用项目、 DISABLE 暂停项目
+        if ("ENABLE".equals(optStatus)) {
+            //转化目标出价
+            String cpaBid = detail.getJSONObject("delivery_setting").getString("cpa_bid");
+            //深度转化目标出价
+            String deepConversionBid = detail.getJSONObject("delivery_setting").getString("deep_cpabid");
+
+            String pricing = detail.getString("pricing");
+            List<JSONObject> advertiserList = new ArrayList<>();
+            if (Check.isNull(cpaBid) && Check.isNull(deepConversionBid)) {
+                if (advertiserList.size() > 0) {
+                    for (JSONObject adObj : advertiserList) {
+                        String statusFirst = adObj.getString("status_first");
+                        //一级状态:PROMOTION_STATUS_ENABLE 投放中
+                        if ("PROMOTION_STATUS_ENABLE".equals(statusFirst)) {
+                            if (!checkAdSendCounts(detail.getLong("advertiser_id"), adObj.getLong("promotion_id"))) {
+                                continue;
+                            }
+                            String bid = adObj.getString("cpa_bid");
+                            if ("PRICING_CPM".equals(pricing) || "PRICING_CPC".equals(pricing)) {
+                                bid = adObj.getString("bid");
+                            }
+                            checkLogic(accessToken, "ad", detail, ocpxMap, textMap, deepMap, bid,
+                                    adObj.getString("deep_cpabid"), json.getString("projectName"), adObj);
+                        } else if ("PROMOTION_STATUS_DISABLE".equals(statusFirst)) {
+                            String statusSecond = adObj.getString("status_second");
+                            //除了广告二级状态 已暂停的状态,其他状态都需要预警
+                            if (!"DISABLE_BY_QUOTA".equals(statusSecond)) {
+                                if (!checkAdSendCounts(detail.getLong("advertiser_id"), adObj.getLong("promotion_id"))) {
+                                    continue;
+                                }
+                                String bid = adObj.getString("cpa_bid");
+                                if ("PRICING_CPM".equals(pricing)) {
+                                    bid = adObj.getString("bid");
+                                }
+                                checkLogic(accessToken, "ad", detail, ocpxMap, textMap, deepMap, bid,
+                                        adObj.getString("deep_cpabid"), json.getString("projectName"), adObj);
+                            }
+                        }
+                    }
+                }
+            } else {
+                checkLogic(accessToken, "project", detail, ocpxMap, textMap, deepMap, cpaBid,
+                        deepConversionBid, json.getString("projectName"), new JSONObject());
+            }
+        }
+    }
+
+    //判断逻辑
+    private void checkLogic(String token, String type, JSONObject detail, Map<String, String> ocpxMap, Map<String, String> textMap, Map<String, String> deepMap,
+                            String cpaBid, String deepConversionBid, String localProject, JSONObject adObj) {
+        Long accountId = detail.getLong("advertiser_id");
+        //广告项目ID
+        Long projectId = detail.getLong("project_id");
+        //广告项目名称
+        String projectName = detail.getString("name");
+        //广告ID
+        Long adId = adObj.getLong("promotion_id");
+
+        JSONObject data = new JSONObject();
+        //转化目标
+        String ocpxActionType = detail.getJSONObject("optimize_goal").getString("external_action");
+        //深度转化目标
+        String deepConversionType = detail.getJSONObject("optimize_goal").getString("deep_external_action");
+
+        //为NO_BID时,不校验出价
+        String bidType = detail.getJSONObject("delivery_setting").getString("bid_type");
+
+        StringBuffer msgType = new StringBuffer();
+        StringBuffer msgBid = new StringBuffer();
+        //转化目标标识
+        Boolean typeFalg = true;
+        //出价标识
+        Boolean bidFalg = true;
+        //转化目标
+        if (!Check.isNull(ocpxActionType)) {
+            String octypeName = Check.isNull(textMap.get(ocpxActionType)) ? ocpxActionType : textMap.get(ocpxActionType);
+            if (ocpxMap.isEmpty()) {
+                typeFalg = false;
+                msgType.append("未设转化,目标转化:").append(octypeName);
+            } else {
+                //转化出价
+                String ocpxBid = ocpxMap.get(ocpxActionType);
+                //通过转化目标没有查询到转化出价,判断为转化目标不一致,触发关停
+                if (Check.isNull(ocpxBid)) {
+                    typeFalg = false;
+                    msgType.append("已设转化:").append(ocpxMap.get("nameStr")).append("<br/>").append("目标转化:").append(octypeName);
+                } else {
+                    //未查询到转化出价,说明出价不一致,触发出价关停。
+                    if (!Check.isNull(cpaBid)) {
+//                        if (!"NO_BID".equals(bidType)) {
+//                            bidFalg = false;
+//                            msgBid.append("[").append(octypeName).append("]目标出价为空");
+//                        }
+//                    } else {
+                        BigDecimal cpa = new BigDecimal(cpaBid).multiply(new BigDecimal("1000"));
+                        BigDecimal ocpx = new BigDecimal(ocpxBid);//本地设置
+                        if (cpa.compareTo(ocpx) == 1) {
+                            //计划转化出价cpa 大于 项目设置最大转化出价,则关停
+                            bidFalg = false;
+                            msgBid.append("[").append(octypeName).append("]最高出价:").append(ocpx.divide(new BigDecimal("1000"))).append("<br/>");
+                            msgBid.append("目标出价:").append(cpaBid);
+                        }
+                    }
+                }
+            }
+        }
+        //深度转化目标
+        if (typeFalg && bidFalg) {
+            String depTypeName = Check.isNull(textMap.get(deepConversionType)) ? deepConversionType : textMap.get(deepConversionType);
+            if (!Check.isNull(deepConversionType) && deepMap.isEmpty()) {
+                typeFalg = false;
+                msgType.append("未设深度转化,目标深度转化:").append(depTypeName);
+            } else if (!Check.isNull(deepConversionType)) {
+                String deepBid = deepMap.get(deepConversionType);
+                if (Check.isNull(deepBid)) {
+                    //通过key未获取值,说明不包含该目标,进行关停
+                    typeFalg = false;
+                    msgType.append("已设深度:").append(deepMap.get("nameStr")).append("<br/>").append("目标深度:").append(depTypeName);
+                } else {
+                    //未查询到转化出价,说明出价不一致,触发出价关停。
+                    if (!Check.isNull(deepConversionBid)) {
+                       /* bidFalg = false;
+                        msgBid.append("[").append(depTypeName).append("]目标深度出价为空");
+                    } else {*/
+                        BigDecimal cpa = new BigDecimal(deepConversionBid).multiply(new BigDecimal("1000"));
+                        BigDecimal ocpx = new BigDecimal(deepBid);
+                        if (cpa.compareTo(ocpx) == 1) {
+                            //计划转化出价cpa 大于 项目设置最大转化出价,则关停
+                            bidFalg = false;
+                            msgBid.append("[").append(depTypeName).append("]最高深度出价:").append(ocpx.divide(new BigDecimal("1000"))).append("<br/>");
+                            msgBid.append("目标深度出价:").append(deepConversionBid);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!typeFalg) { // 出价方式设置非法
+            String text = getProjectBidTypeText(localProject, accountId, projectId, projectName, adId, adObj.getString("promotion_name"), msgType.toString());
+            sendMsg(accountId, token, projectId, adId, text);
+        }
+        if (!bidFalg) {
+            String text = getProjectOverBidMessage(localProject, accountId, projectId, projectName, adId, adObj.getString("promotion_name"), msgBid.toString());
+            sendMsg(accountId, token, projectId, adId, text);
+        }
+    }
+
+    private void sendMsg(Long accountId, String token, Long projectId, Long adId, String text) {
+        try {
+            MediaSendAlarmRecord record = new MediaSendAlarmRecord(accountId, "1", text);
+            record.setProjectId(projectId);
+            //关停计划
+            this.updProjectStatus(token, accountId, projectId, "DISABLE");
+            log.info(">预警<,账户:{}=>计划:{}现已【关停】---> {}", accountId, projectId, text);
+            List<JSONObject> list = projectListMapper.getWChatIdsByAccountId(accountId);
+            for (JSONObject object : list) {
+                corpWexinUtils.sendMessageByWeChatId(object, text);
+            }
+            //添加记录 ctop_media_send_alarm_record
+            projectListMapper.insertSendRecord(record);
+            //当前已发送次数
+
+            Integer i = projectListMapper.getrefusalAccountCounts(accountId, adId);
+            //更新计数
+            projectListMapper.insertSendAccountId(accountId, adId, (Check.isNull(i) ? 0 : i) + 1);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    //转化方式错误消息生成
+    private String getProjectBidTypeText(String projectName, Long advertiserId, Long unitId, String unit_name, Long adId, String adName, String msgType) {
+        StringBuilder text = new StringBuilder();
+        text.append("出价方式错误预警").append("<br/>")
+                .append("您本地的项目:").append(projectName).append("<br/>")
+                .append("下的账户:").append(advertiserId).append("<br/>")
+                .append("项目id:").append(unitId).append("<br/>")
+                .append("项目名称:").append(unit_name).append("<br/>");
+        if (!Check.isNull(adId)) {
+            text.append("广告id:").append(adId).append("<br/>").append("广告名称:").append(adName).append("<br/>");
+        }
+        text.append("错误说明:").append("<br/>").append(msgType).append("<br/>").append("请您及时查看并处理!");
+        return text.toString();
+    }
+
+    //出价过高预警消息生成
+    private String getProjectOverBidMessage(String projectName, Long accountId, Long planId, String planName, Long adId, String adName, String msgBid) {
+        StringBuilder text = new StringBuilder();
+        text.append("出价过高预警").append("<br/>")
+                .append("您本地的项目:").append(projectName).append("<br/>")
+                .append("下的账户:").append(accountId).append("<br/>")
+                .append("项目id:").append(planId).append("<br/>")
+                .append("项目名称:").append(planName).append("<br/>");
+        if (!Check.isNull(adId)) {
+            text.append("广告id:").append(adId).append("<br/>").append("广告名称:").append(adName).append("<br/>");
+        }
+        if (!msgBid.contains("出价为空")) {
+            text.append("出价过高说明:").append("<br/>");
+        }
+        text.append(msgBid).append("<br/>").append("请您及时查看并处理!");
+        return text.toString();
+    }
+}

+ 85 - 0
jeecg-boot-module-system/src/main/java/cn/com/ctop/toutiao/modules/report/controller/BytedancePushDataController.java

@@ -0,0 +1,85 @@
+package cn.com.ctop.toutiao.modules.report.controller;
+
+
+import cn.com.ctop.common.module.service.IBytedanceProjectService;
+import cn.com.ctop.common.module.utils.Check;
+import cn.com.ctop.toutiao.modules.tool.callback.AuthTokenUtil;
+import cn.com.ctop.toutiao.modules.tool.callback.BaseResponse;
+import cn.com.ctop.toutiao.modules.tool.callback.CallbackResponse;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+
+@Slf4j
+@RestController
+@RequestMapping("/bytedance/push")
+public class BytedancePushDataController {
+    private static String secretKey = "156a5d76ad354b43865ef57e5e416ca7";
+    private static ExecutorService projectExecutorService = Executors.newFixedThreadPool(5);
+
+    @Autowired
+    private IBytedanceProjectService bytedanceProjectService;
+
+
+    @RequestMapping(value = "/bytedanceProjectData", method = {RequestMethod.GET, RequestMethod.POST})
+    @ResponseBody
+    public CallbackResponse bytedanceProjectData(HttpServletRequest request, @RequestParam(value = "challenge", defaultValue = "0") int challenge, @RequestParam(value = "event", defaultValue = "") String event, @RequestParam(value = "appId", defaultValue = "") String appId) throws IOException {
+
+        if (null != event && event.equals("verify_webhook")) {
+            return new CallbackResponse(BaseResponse.ok(), challenge);
+        }
+        // 数据接收,验证消息
+        AuthTokenUtil.InputStreamCacher cacher = new AuthTokenUtil.InputStreamCacher(request.getInputStream());
+        boolean isValidToken = AuthTokenUtil.isValidToken(secretKey, cacher, request.getHeader("X-Open-Signature"));
+        if (!isValidToken) {
+            return new CallbackResponse(new BaseResponse(400, "invalid token"), 0);
+        }
+        JSONObject result = (JSONObject) JSONObject.parse(new String(AuthTokenUtil.readAsBytes(cacher)));
+        log.info("---项目物料推送  task_id:{}\n转译结果:{}", result.getString("subscribe_task_id"), result);
+        if (!Check.isNull(result)) {
+            JSONArray advertiserIds = result.getJSONArray("advertiser_ids");
+            Long advertiserId;
+            if (!Check.isNull(advertiserIds)) {
+                advertiserId = advertiserIds.getLong(0);
+            } else {
+                advertiserId = null;
+            }
+            JSONObject data = result.getJSONObject("data");
+            if (!Check.isNull(data)) {
+                JSONObject content = data.getJSONObject("content");
+                if (!Check.isNull(content)) {
+                    JSONArray idArr = content.getJSONArray("project_ids");
+                    List<Long> idList = JSONArray.parseArray(idArr.toJSONString(), Long.class);
+                    List<List<Long>> moreList = Lists.partition(idList, 100);
+                    for (List<Long> ids : moreList) {
+                        projectExecutorService.submit(new Runnable() {
+                            @Override
+                            public void run() {
+                                String token = bytedanceProjectService.getTokenByAccountId(advertiserId);
+                                if (!Check.isNull(token)) {
+                                    bytedanceProjectService.checkAdvertiserProject(advertiserId, token, ids, 1);
+                                }
+                            }
+                        });
+
+
+                    }
+                }
+            }
+        }
+        return new CallbackResponse(BaseResponse.ok(), 0);
+    }
+
+}

+ 26 - 81
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/controller/TestController.java

@@ -44,6 +44,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.ibatis.annotations.Param;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.util.DateUtils;
+import org.jeecg.modules.ctop.entity.TransferAccount;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.*;
@@ -165,90 +166,34 @@ public class TestController {
         }
     }
 
+    @Autowired
+    private IMailLogService mailLogService;
+
     @GetMapping(value = "/getProgramCreative")
     public void getProgramCreative() {
-        List<Long> list = new ArrayList();
-        list.add(10344460L);
-        list.add(10344415L);
-        list.add(10344390L);
-        list.add(10344030L);
-        list.add(10343624L);
-        list.add(10343590L);
-        list.add(10343529L);
-        list.add(10343451L);
-        list.add(10343394L);
-        list.add(10327332L);
-        list.add(10327322L);
-        list.add(10327303L);
-        list.add(10327276L);
-        list.add(10327257L);
-        list.add(10327214L);
-        list.add(10327078L);
-        list.add(10327042L);
-        list.add(10327022L);
-        list.add(10326908L);
-        list.add(10299258L);
-        list.add(10299241L);
-        list.add(10299221L);
-        list.add(10299210L);
-        list.add(10298910L);
-        list.add(10298871L);
-        list.add(10298859L);
-        list.add(10298840L);
-        list.add(10258924L);
-        list.add(10258921L);
-        list.add(10258286L);
-        list.add(10258278L);
-        list.add(10258257L);
-        list.add(10258249L);
-        list.add(10258216L);
-        list.add(10257909L);
-        list.add(10257825L);
-        list.add(10257792L);
-        list.add(10257253L);
-        list.add(10257209L);
-        list.add(10257193L);
-        list.add(10257189L);
-        list.add(10257172L);
-        list.add(10257141L);
-        list.add(10257048L);
-        list.add(10252339L);
-        list.add(10252321L);
-        list.add(10252233L);
-        list.add(10252231L);
-        list.add(10173389L);
-        list.add(10173385L);
-        list.add(10173375L);
-        list.add(10173366L);
-        list.add(10173200L);
-        list.add(10108671L);
-        list.add(10108435L);
-        list.add(10093939L);
-        list.add(10093926L);
-        list.add(10093914L);
-        list.add(10093909L);
-        list.add(10093479L);
-        list.add(9767717L);
-        list.add(9767697L);
-        list.add(9706671L);
-        list.add(9635821L);
-        list.add(9635234L);
-        list.add(9549313L);
-        list.add(9549277L);
-
-        for (int i = 0; i < list.size(); i++) {
-            Long accountId = list.get(i);
-            CtopOauthToken oauthToken = oauthTokenService.getTokenByAccountId(accountId);
-            videoService.submit(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        kuaishouProgramCreativeService.getProgramCreative(oauthToken.getAccountId(), null, oauthToken.getAccessToken(), null, null, 1);
-                    } catch (Exception e) {
-                        e.printStackTrace();
+        try {
+            TransferAccount transferAccountEntity = new TransferAccount();
+            transferAccountEntity.setAccountId(30862452L);
+            //根据账户ID获取总监
+            JSONObject accountJson = mailLogMapper.getAccountInfo(transferAccountEntity.getAccountId());
+            BigDecimal cost = new BigDecimal(0);
+            if (!Check.isNull(accountJson)) {
+                Integer mediaId = accountJson.getInteger("mediaId");
+                if (!Check.isNull(mediaId)) {
+                    if (mediaId == 1) {
+                        cost = mailLogMapper.getBytedanceCost(transferAccountEntity.getAccountId());
+                    }
+                    if (mediaId == 2) {
+                        cost = mailLogMapper.getKuaishouCost(transferAccountEntity.getAccountId());
                     }
                 }
-            });
+                if (cost.compareTo(BigDecimal.ZERO) > 0) {
+                    String leaderId = accountJson.getString("leaderId");
+                    mailLogService.sendLeaderWeChat("1111", 1, "113dee46c7df464da78c07a985e92cd1", transferAccountEntity.getAccountId(), "b07f2bdd0f914f1dab2b4e841764911f", "f6a6843879c949618e4a859140c8a127", cost);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
         }
     }
 
@@ -1298,7 +1243,7 @@ public class TestController {
     }
 
 
-/**
+    /**
      * 勿动勿删,煜一人群报表数据拉取接口。
      *
      * @param