zhaotx пре 8 месеци
родитељ
комит
9ea20fefd3
17 измењених фајлова са 1396 додато и 5 уклоњено
  1. 11 4
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/controller/FeishuController.java
  2. 48 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/constants/ReportsConstants.java
  3. 20 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtActionLog.java
  4. 48 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicy.java
  5. 16 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyAction.java
  6. 18 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyCondition.java
  7. 26 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyConfig.java
  8. 22 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyRunInfo.java
  9. 20 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyTargetFilter.java
  10. 16 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyTargetIds.java
  11. 28 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtTargetRunInfo.java
  12. 71 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/ZtPolicyMapper.java
  13. 354 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/xml/ZtPolicyMapper.xml
  14. 10 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IZtPolicyRunService.java
  15. 644 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/impl/ZtPolicyRunServiceImpl.java
  16. 1 1
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/utils/FeishuRobotUtil.java
  17. 43 0
      job-bytedance/src/main/java/cn/com/ctop/job/bytedance/utils/FsTimeBitmapChecker.java

+ 11 - 4
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/controller/FeishuController.java

@@ -55,19 +55,27 @@ public class FeishuController {
     private PromoteLinkRefreshJob promoteLinkRefreshJob;
     @Autowired
     private IBytedancePromoteRefreshService bytedancePromoteRefreshService;
+    @Autowired
+    private IZtPolicyRunService ztPolicyRunService;
 
     @Autowired
     IUserLoginRecordService userLoginRecordService;
     @PostMapping("/test")
     public void test(@RequestBody String str) throws Exception {
-//        bytedanceProductUpdateService.initNew();
+        ztPolicyRunService.test();
+
+
+    }
+
+    private void sendFs(){
+        //        bytedanceProductUpdateService.initNew();
 //        bytedanceProductUpdateService.runMp();
 //        bytedanceProductUpdateService.runPlatform("307146835248942");
 //        bytedanceProductUpdateService.refreshAlbumStatus();
 //        reportsAdPushService.test();
-        totalRankPushService.test();
+//        totalRankPushService.test();
 //        dateReportsPushService.preSend();
-        optimizerRankPushService.send();
+//        optimizerRankPushService.send();
 //        unbindWarnPushService.send();
 //        cutGroupRankPushService.send();
 //        reportsByBusinessPushService.test();
@@ -103,7 +111,6 @@ public class FeishuController {
 
 //        promoteLinkRefreshJob.runAdvertiserTaskStatusJob();
 //        promoteLinkRefreshJob.runHistoryAdvertiserTaskStatus();
-
     }
 
 

Разлика између датотеке није приказан због своје велике величине
+ 48 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/constants/ReportsConstants.java


+ 20 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtActionLog.java

@@ -0,0 +1,20 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import lombok.Data;
+
+@Data
+public class ZtActionLog {
+    String id;
+    String runId;
+    Long policyId;
+    Integer targetType;//触发目标类型:1=账号 2=项目 3=计划
+    String targetId;
+    Long actionId;
+    Integer actionType;//操作类型:1=调整巨量账号预算 2=调整巨量项目预算 3=调整巨量计划预算 10=发送飞书
+    Integer executed;//是否实际执行:1=是 0=否
+    Integer skipReason;//未执行原因(executed=0时);1:不在发送时段 2:触发频控 3:动作未启用 4:参数校验失败 5:当前版本不支持 9:其他
+    Integer resultStatus = 1;
+    String errorMessage;
+
+
+}

+ 48 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicy.java

@@ -0,0 +1,48 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class ZtPolicy {
+
+    private Integer id;
+    private String name;
+    private Integer status;//1开启 0停用
+    private Integer mediaId;
+    private String mediaName;
+    private Integer scheduleType;//1半小时 2小时 3天
+    private Integer managedDimension;//托管目标维度 1账号 2项目 3计划
+    private Integer targetScope;//1所有广告账号 2选定广告账号 3选定广告项目 4选定广告计划
+    private Integer conditionLogic;//判断条件组合 1且 2或
+    private String createdBy;
+    private String createdAt;
+    private String updatedBy;
+    private String updatedAt;
+    private Long createUserId;//归属人,用这个判断权限和飞书推送发给谁
+    private Integer isDeleted;
+
+    //托管目标过滤条件
+    private Integer businessLine;
+    private String businessLineName;
+    private List<String> appId;
+    private List<String> appName;
+    private List<String> businessMethod;
+    private List<Long> optimizerId;
+    private List<Long> deptId;
+    private List<String> movieId;
+
+    //目标范围ID(维度为targetScope定义的,不一定是托管目标的维度)
+    private List<String> targetScopeIds;
+
+    //最终托管目标ID,运行任务目标以这个+managedDimension为准
+    private List<String> targetResIds;
+
+    private List<ZtPolicyCondition> conditions;
+    private List<ZtPolicyAction>  actions;
+    private boolean saveAsTemplate = false;
+
+    private Map<String, String> targetFromAdvertiserId;//目标ID所属的广告账号ID
+}

+ 16 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyAction.java

@@ -0,0 +1,16 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.Data;
+
+@Data
+public class ZtPolicyAction {
+
+    private Long id;
+    private Integer ownerType;
+    private Long ownerId;
+    private Integer actionType;
+    private String actionRuleJson;
+    private JSONObject actionRuleJsonObject;
+    private String descriptionZh;
+}

+ 18 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyCondition.java

@@ -0,0 +1,18 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.Data;
+
+@Data
+public class ZtPolicyCondition {
+
+    private Long id;
+    private Integer ownerType;
+    private Long ownerId;
+    private Long indicatorId;
+    private String operator;
+    private Integer timeType;
+    private String compareValueJson;
+    private JSONObject compareValueJsonObject;
+    private String descriptionZh;
+}

+ 26 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyConfig.java

@@ -0,0 +1,26 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ZtPolicyConfig {
+    private Long id;
+    private Integer mediaId;
+    private String name;
+    private Integer status;
+    private Integer scheduleType;
+    private Integer managedDimension;
+    private Integer targetScope;
+    private Integer conditionLogic;
+    private String createdBy;
+    private String updatedBy;
+    private Long createUserId;
+
+    private String createdAt;
+    private String updatedAt;
+    private Integer isDeleted;
+}

+ 22 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyRunInfo.java

@@ -0,0 +1,22 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 策略一次运行的整体信息
+ */
+@Data
+public class ZtPolicyRunInfo {
+
+    private String runId;
+    private Long policyConfigId;
+    private Date scheduledTime;
+    private Date startTime;
+    private Date endTime;
+    private Integer triggered;
+    private Integer resultStatus;
+    private String errorMessage;
+    private String configSnapshotJson;
+}

+ 20 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyTargetFilter.java

@@ -0,0 +1,20 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import lombok.Data;
+
+@Data
+public class ZtPolicyTargetFilter {
+
+    private Long policyConfigId;
+    //目标维度:1=广告账号(当前仅支持账号)
+    private Integer targetDimension;
+    //筛选字段:1=app 2=businessLine 3=businessMethod 4=optimizerId 5=deptId 6=movieId
+    private Integer filterKey;
+    private String filterValue;
+
+    public ZtPolicyTargetFilter(Integer targetDimension, Integer filterKey, String filterValue) {
+        this.targetDimension = targetDimension;
+        this.filterKey = filterKey;
+        this.filterValue = filterValue;
+    }
+}

+ 16 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtPolicyTargetIds.java

@@ -0,0 +1,16 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import lombok.Data;
+
+@Data
+public class ZtPolicyTargetIds {
+
+    private Long policyConfigId;
+    private Integer targetType;//目标类型:1=广告账号 2=广告项目 3=广告计划
+    private String targetId;//目标ID(账号/项目/计划ID)
+
+    public ZtPolicyTargetIds(Integer targetType, String targetId) {
+        this.targetType = targetType;
+        this.targetId = targetId;
+    }
+}

+ 28 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/entity/ZtTargetRunInfo.java

@@ -0,0 +1,28 @@
+package cn.com.ctop.job.bytedance.data.entity;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class ZtTargetRunInfo {
+
+    String uuid;
+    String runId;
+    Long advertiserId;
+    Long policyId;
+    Integer managedDimension;//托管目标维度 1账号 2项目 3计划
+    String targetId;//目标ID
+    ZtPolicyCondition condition;
+    JSONObject indicatorInfo;
+    String currentValue;//实际值
+    Long conditionId;
+
+    Boolean isExecute;//是否触发操作
+    Date triggerTime;
+    JSONObject matchedConditionJson;
+    String matchedConditionJsonStr;
+    String matchedConditionDesc;
+
+}

+ 71 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/mapper/ZtPolicyMapper.java

@@ -0,0 +1,71 @@
+package cn.com.ctop.job.bytedance.data.mapper;
+
+import cn.com.ctop.job.bytedance.data.entity.*;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.dynamic.datasource.annotation.DS;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface ZtPolicyMapper {
+
+    ZtPolicy getPolicyDetail(@Param("policyId") Long policyId);
+    List<ZtPolicyTargetFilter> getTargetFilters(@Param("policyId") Long policyId);
+    List<ZtPolicyTargetIds> getTargetIds(@Param("policyId") Long policyId);
+
+    @DS("selectdb")
+    List<String> advFilterList(
+                               @Param("appId") List<String> appId,
+                               @Param("businessLine") Integer businessLine,
+                               @Param("businessMethod") List<String> businessMethod,
+                               @Param("optimizerId") List<Long> optimizerId,
+                               @Param("deptId") List<Long> deptId,
+                               @Param("movieId") List<String> movieId
+    );
+
+    @DS("selectdb")
+    List<String> advListFromAdvIds(@Param("policyId") Long policyId);
+    @DS("selectdb")
+    List<JSONObject> projectListFromAdvIds(@Param("policyId") Long policyId);
+    @DS("selectdb")
+    List<JSONObject> projectListFromProjIds(@Param("policyId") Long policyId);
+    @DS("selectdb")
+    List<JSONObject> adListFromAdvIds(@Param("policyId") Long policyId);
+    @DS("selectdb")
+    List<JSONObject> adListFromAdIds(@Param("policyId") Long policyId);
+
+    /**
+     * @Param("indicatorSql") String indicatorSql,
+     * @Param("mediaAdSql") String mediaAdSql,
+     * @Param("userDataSql") String userDataSql,
+     * @Param("managedDimension") Integer managedDimension,
+     * @Param("idList") List<String> idList,
+     * @Param("timeType") Integer timeType,//1按天 2按小时
+     * 按天: startDate, endDate
+     * 按小时: 今日累计, 今日分小时
+     * @return
+     */
+    @DS("selectdb")
+    List<JSONObject> adData(JSONObject requestParam);
+    @DS("selectdb")
+    List<JSONObject> promotionData(@Param("idList") List<String> idList);
+    @DS("selectdb")
+    List<JSONObject> promotionGroupToProjectData(@Param("idList") List<String> idList);
+    @DS("selectdb")
+    List<JSONObject> projectData(@Param("idList") List<String> idList);
+    List<JSONObject> balanceData(@Param("idList") List<String> idList);
+
+    List<JSONObject> indicatorList(@Param("indicatorIds") List<Long> indicatorIds);
+
+    int insertRunRecords(List<ZtTargetRunInfo> list);
+
+    int updatePolicyRunRecord(ZtPolicyRunInfo ztPolicyRunInfo);
+    int insertPolicyRunRecord(ZtPolicyRunInfo ztPolicyRunInfo);
+
+    int insertActionLog(List<ZtActionLog> list);
+
+
+
+
+}

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

@@ -0,0 +1,354 @@
+<?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.ZtPolicyMapper">
+
+    <resultMap id="ZtPolicyWithCondActionResultMap" type="cn.com.ctop.job.bytedance.data.entity.ZtPolicy">
+
+        <!-- config -->
+        <id     property="id"               column="id"/>
+        <result property="name"             column="name"/>
+        <result property="status"           column="status"/>
+        <result property="mediaId"          column="media_id"/>
+        <result property="scheduleType"     column="schedule_type"/>
+        <result property="managedDimension" column="managed_dimension"/>
+        <result property="targetScope"      column="target_scope"/>
+        <result property="conditionLogic"      column="condition_logic"/>
+        <result property="createdAt"        column="created_at"/>
+        <result property="createdBy"        column="created_by"/>
+        <result property="updatedAt"        column="updated_at"/>
+        <result property="updatedBy"        column="updated_by"/>
+        <result property="createUserId"     column="create_user_id"/>
+
+        <!-- conditions: config.id -> cd.owner_id (1:N) -->
+        <collection property="conditions" ofType="cn.com.ctop.job.bytedance.data.entity.ZtPolicyCondition">
+            <id     property="id"               column="cd_id"/>
+            <result property="indicatorId"      column="cd_indicator_id"/>
+            <result property="operator"         column="cd_operator"/>
+            <result property="timeType"         column="cd_time_type"/>
+            <result property="compareValueJson" column="cd_compare_value_json"/>
+            <result property="descriptionZh"    column="cd_description_zh"/>
+        </collection>
+
+        <!-- actions: config.id -> ac.owner_id (1:N) -->
+        <collection property="actions" ofType="cn.com.ctop.job.bytedance.data.entity.ZtPolicyAction">
+            <id     property="id"            column="ac_id"/>
+            <result property="actionType"    column="ac_action_type"/>
+            <result property="actionRuleJson" column="ac_action_rule_json"/>
+            <result property="descriptionZh" column="ac_description_zh"/>
+        </collection>
+
+    </resultMap>
+    <insert id="insertRunRecords">
+        insert into deepthink.zt_rule_run_log
+        (id, run_id, advertiser_id, target_type, target_id, condition_id,
+         operation_executed, trigger_time, matched_conditions_json, matched_condition_desc, policy_config_id)
+        values
+        <foreach collection="list" item="item" open="" close="" separator=",">
+            (#{item.uuid}, #{item.runId}, #{item.advertiserId}, #{item.managedDimension}, #{item.targetId}, #{item.conditionId},
+             #{item.isExecute}, #{item.triggerTime}, #{item.matchedConditionJsonStr}, #{item.matchedConditionDesc}, #{item.policyId})
+        </foreach>
+    </insert>
+    <insert id="insertPolicyRunRecord">
+        insert into deepthink.zt_policy_run
+            (run_id, policy_config_id, scheduled_time, result_status, config_snapshot_json)
+        values
+            (#{runId}, #{policyConfigId}, #{scheduledTime}, 0, #{configSnapshotJson})
+    </insert>
+    <insert id="insertActionLog">
+            insert into deepthink.zt_policy_action_log
+            (
+            id,
+            run_id,
+            policy_config_id,
+            target_type,
+            target_id,
+            action_id,
+            action_type,
+            executed,
+            skip_reason,
+            result_status,
+            error_message
+            )
+            values
+            <foreach collection="list" item="item" separator=",">
+                (
+                #{item.id},
+                #{item.runId},
+                #{item.policyId},
+                #{item.targetType},
+                #{item.targetId},
+                #{item.actionId},
+                #{item.actionType},
+                #{item.executed},
+                #{item.skipReason},
+                #{item.resultStatus},
+                #{item.errorMessage}
+                )
+            </foreach>
+    </insert>
+    <update id="updatePolicyRunRecord">
+        update deepthink.zt_policy_run
+        <set>
+            <if test="startTime != null">start_time = #{startTime},</if>
+            <if test="endTime != null">end_time = #{endTime},</if>
+            <if test="triggered != null">triggered = #{triggered},</if>
+            <if test="resultStatus != null">result_status = #{resultStatus},</if>
+            <if test="errorMessage != null">error_message = #{errorMessage},</if>
+        </set>
+        where run_id = #{runId}
+    </update>
+    <select id="getPolicyDetail" resultMap="ZtPolicyWithCondActionResultMap">
+        select config.id, config.name, config.status,
+               config.media_id,
+               config.schedule_type, config.managed_dimension, config.target_scope, config.condition_logic,
+               config.created_at, config.created_by, config.updated_at, config.updated_by, config.create_user_id,
+               cd.id              as cd_id,
+               cd.indicator_id    as cd_indicator_id,
+               cd.operator        as cd_operator,
+               cd.time_type       as cd_time_type,
+               cd.compare_value_json as cd_compare_value_json,
+               cd.description_zh  as cd_description_zh,
+
+               ac.id              as ac_id,
+               ac.action_type     as ac_action_type,
+               ac.action_rule_json as ac_action_rule_json,
+               ac.description_zh  as ac_description_zh
+        from (select * from deepthink.zt_policy_config where is_deleted=0) config
+                 left join deepthink.zt_policy_condition cd on config.id = cd.owner_id and cd.owner_type=1
+                 left join deepthink.zt_policy_action ac on config.id = ac.owner_id and ac.owner_type=1
+        where config.id = #{policyId}
+    </select>
+    <select id="getTargetFilters" resultType="cn.com.ctop.job.bytedance.data.entity.ZtPolicyTargetFilter">
+        select policy_config_id, target_dimension, filter_key, filter_value
+        from deepthink.zt_policy_target_filter
+        where policy_config_id = #{policyId}
+    </select>
+    <select id="getTargetIds" resultType="cn.com.ctop.job.bytedance.data.entity.ZtPolicyTargetIds">
+        select policy_config_id, target_type, target_id
+        from deepthink.zt_policy_target_ids
+        where policy_config_id = #{policyId}
+    </select>
+    <select id="advFilterList" resultType="java.lang.String">
+        select distinct tb1.advertiser_id from
+        (select ma_appid, advertiser_id
+        from application.media_ad_report_daily where stat_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 9 DAY)
+        group by ma_appid, advertiser_id
+        having sum(show_num)>0
+        ) tb1
+        join (select advertiser_id, advertiser_name, user_id, ma_appid from tidb.deepthink.advertiser_bind_info) tb2
+        on tb1.advertiser_id = tb2.advertiser_id
+        left join tidb.deepthink.mini_app_info tb3 on tb1.ma_appid = tb3.app_id
+        left join tidb.deepthink.sys_user tb4 on tb2.user_id = tb4.user_id
+        left join (select advertiser_id, movie_id
+        from application.media_ad_report_daily
+        where stat_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 9 DAY)
+        group by advertiser_id, movie_id
+        ) tb5 on tb1.advertiser_id = tb5.advertiser_id
+        left join (select distinct moive_id, standard_movie_id
+        from tidb.deepthink.all_moive) tb6 on tb5.movie_id = tb6.moive_id
+        <where>
+            <if test="appId != null and appId.size > 0">
+                and tb3.app_id in
+                <foreach collection="appId" item="item" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="businessMethod != null and businessMethod.size > 0">
+                and tb3.business_method in
+                <foreach collection="businessMethod" item="item" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="businessLine != null and businessLine > 0">
+                and tb3.business_line = #{businessLine}
+            </if>
+            <if test="optimizerId != null and optimizerId.size > 0">
+                and tb4.user_id in
+                <foreach collection="optimizerId" item="item" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="deptId != null and deptId.size > 0">
+                and tb4.dept_id in
+                <foreach collection="deptId" item="item" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="movieId != null and movieId.size > 0">
+                and tb6.standard_movie_id in
+                <foreach collection="movieId" item="item" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if>
+        </where>
+    </select>
+    <select id="advListFromAdvIds" resultType="java.lang.String">
+        select tb2.advertiser_id
+        from tidb.deepthink.zt_policy_target_ids tb1
+                 join (select advertiser_id
+                       from application.media_ad_report_daily
+                       where stat_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 9 DAY)
+                       group by advertiser_id
+                       having sum(show_num)>0
+                       ) tb2
+                      on tb1.target_id = tb2.advertiser_id
+    </select>
+    <select id="projectListFromAdvIds" resultType="com.alibaba.fastjson.JSONObject">
+        select advertiser_id, a_id
+        from tidb.deepthink.zt_policy_target_ids tb1
+                 join (select advertiser_id, a_id
+                       from application.media_ad_report_daily
+                       where stat_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 9 DAY)
+                       group by advertiser_id , a_id
+                       having sum(show_num)>0
+                       ) tb2
+                      on tb1.target_id = tb2.advertiser_id
+    </select>
+    <select id="projectListFromProjIds" resultType="com.alibaba.fastjson.JSONObject">
+        select advertiser_id, a_id
+        from tidb.deepthink.zt_policy_target_ids tb1
+                 join (select advertiser_id, a_id
+                       from application.media_ad_report_daily
+                       where stat_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 9 DAY)
+                       group by advertiser_id , a_id
+                       having sum(show_num)>0
+                       ) tb2
+                      on tb1.target_id = tb2.a_id
+    </select>
+    <select id="adListFromAdvIds" resultType="com.alibaba.fastjson.JSONObject">
+        select advertiser_id, ad_id
+        from tidb.deepthink.zt_policy_target_ids tb1
+                 join (select advertiser_id, a_id, ad_id
+                       from application.media_ad_report_daily
+                       where stat_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 9 DAY)
+                       group by advertiser_id , a_id, ad_id
+                       having sum(show_num)>0
+                       ) tb2
+                      on tb1.target_id = tb2.advertiser_id
+    </select>
+    <select id="adListFromAdIds" resultType="com.alibaba.fastjson.JSONObject">
+        select advertiser_id, ad_id
+        from tidb.deepthink.zt_policy_target_ids tb1
+                 join (select advertiser_id, a_id, ad_id
+                       from application.media_ad_report_daily
+                       where stat_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 9 DAY)
+                       group by advertiser_id , a_id, ad_id
+                       having sum(show_num)>0
+                       ) tb2
+                      on tb1.target_id = tb2.ad_id
+    </select>
+    <select id="adData" resultType="com.alibaba.fastjson.JSONObject">
+        select
+        <choose>
+            <when test="managedDimension == 1">
+                coalesce(tb1.advertiser_id, tb2.advertiser_id) as target_id,
+            </when>
+            <when test="managedDimension == 2">
+                coalesce(tb1.a_id, tb2.a_id) as target_id,
+            </when>
+            <when test="managedDimension == 3">
+                coalesce(tb1.ad_id, tb2.ad_id) as target_id,
+            </when>
+        </choose>
+            ${selectIndicator}
+        from
+            (select advertiser_id, a_id, ad_id, ${mediaAdColumns}
+             <if test="timeType == 1">
+                 from application.media_ad_report_daily
+                 where stat_date &gt;= #{startDate} and stat_date &lt;= #{endDate}
+             </if>
+             <if test="timeType == 2">
+                 from application.media_ad_report_hourly
+                 where stat_date = #{date}
+                 <if test="hour != null and hour > 0">
+                     and `hour``=#{hour}
+                 </if>
+             </if>
+             group by advertiser_id, a_id, ad_id
+            ) tb1
+            full join
+            (select advertiser_id, a_id, ad_id, ${userDataColumns}
+            <if test="timeType == 1">
+                from application.user_data_report_daily
+                where stat_date &gt;= #{startDate} and stat_date &lt;= #{endDate}
+            </if>
+            <if test="timeType == 2">
+                from application.user_data_report_hourly
+                where stat_date = #{date}
+                <if test="hour != null and hour > 0">
+                    and `hour``=#{hour}
+                </if>
+            </if>
+            group by advertiser_id, a_id, ad_id
+            ) tb2
+        on tb1.advertiser_id = tb2.advertiser_id
+            and tb1.a_id = tb2.a_id
+            and tb1.ad_id = tb2.ad_id
+        <where>
+            <choose>
+                <when test="managedDimension == 1">
+                    coalesce(tb1.advertiser_id, tb2.advertiser_id) in
+                    <foreach collection="idList" item="item" open="(" close=")" separator=",">
+                        #{item}
+                    </foreach>
+                </when>
+                <when test="managedDimension == 2">
+                    coalesce(tb1.a_id, tb2.a_id) in
+                    <foreach collection="idList" item="item" open="(" close=")" separator=",">
+                        #{item}
+                    </foreach>
+                </when>
+                <when test="managedDimension == 3">
+                    coalesce(tb1.ad_id, tb2.ad_id) in
+                    <foreach collection="idList" item="item" open="(" close=")" separator=",">
+                        #{item}
+                    </foreach>
+                </when>
+            </choose>
+        </where>
+        group by 1
+    </select>
+    <select id="promotionData" resultType="com.alibaba.fastjson.JSONObject">
+        select promotion_id as target_id, status_first as bytedance_promote_status
+        from media_api.bytedance_promotion_list
+        where promotion_id in
+        <foreach collection="idList" item="item" open="(" close=")" separator=",">
+            #{item}
+        </foreach>
+    </select>
+    <select id="promotionGroupToProjectData" resultType="com.alibaba.fastjson.JSONObject">
+        select project_id as target_id, group_concat(distinct status_first) as bytedance_project_status
+        from media_api.bytedance_promotion_list
+        where project_id in
+        <foreach collection="idList" item="item" open="(" close=")" separator=",">
+            #{item}
+        </foreach>
+        group by project_id
+    </select>
+    <select id="projectData" resultType="com.alibaba.fastjson.JSONObject">
+        select project_id as target_id, bid_type as bytedance_project_bid_type,
+               roi_goal as bytedance_project_roi_goal,
+               cpa_bid as bytedance_project_cpa_bid
+        from media_api.bytedance_cdp_project_list
+        where project_id in
+        <foreach collection="idList" item="item" open="(" close=")" separator=",">
+            #{item}
+        </foreach>
+    </select>
+    <select id="balanceData" resultType="com.alibaba.fastjson.JSONObject">
+        select advertiser_id as target_id, (wallet_total_balance_valid+balance) as bytedance_advertiser_balance
+        from media_api.bytedance_advertiser_fund
+        where advertiser_id in
+        <foreach collection="idList" item="item" open="(" close=")" separator=",">
+            #{item}
+        </foreach>
+    </select>
+    <select id="indicatorList" resultType="com.alibaba.fastjson.JSONObject">
+        select indicator_id, indicator_name, indicator_code, data_source, time_type, formula, from_page
+        from deepthink.indicator_info
+        where indicator_id in 
+        <foreach collection="indicatorIds" item="item" open="(" close=")" separator=",">
+            #{item}
+        </foreach>
+    </select>
+</mapper>

+ 10 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/data/service/IZtPolicyRunService.java

@@ -0,0 +1,10 @@
+package cn.com.ctop.job.bytedance.data.service;
+
+public interface IZtPolicyRunService {
+
+
+    void test();
+    void run(Long policyId);
+
+
+}

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

@@ -0,0 +1,644 @@
+package cn.com.ctop.job.bytedance.data.service.impl;
+
+import cn.com.ctop.job.bytedance.data.constants.ReportsConstants;
+import cn.com.ctop.job.bytedance.data.entity.*;
+import cn.com.ctop.job.bytedance.data.mapper.ReportsAdPushMapper;
+import cn.com.ctop.job.bytedance.data.mapper.ZtPolicyMapper;
+import cn.com.ctop.job.bytedance.data.service.IBytedanceBudgetStatusService;
+import cn.com.ctop.job.bytedance.data.service.IZtPolicyRunService;
+import cn.com.ctop.job.bytedance.data.utils.DateUtils;
+import cn.com.ctop.job.bytedance.utils.FeishuRobotUtil;
+import cn.com.ctop.job.bytedance.utils.FsTimeBitmapChecker;
+import cn.com.ctop.job.bytedance.utils.RedisUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import org.redisson.Redisson;
+import org.redisson.api.RAtomicLong;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Service
+public class ZtPolicyRunServiceImpl implements IZtPolicyRunService {
+
+    @Resource
+    ZtPolicyMapper ztPolicyConfigMapper;
+    @Autowired
+    RedissonClient redissonClient;
+    @Autowired
+    IBytedanceBudgetStatusService bytedanceBudgetStatusService;
+    @Resource
+    ReportsAdPushMapper reportsAdPushMapper;
+    public static final String ERROR_HOOK_URL="https://open.feishu.cn/open-apis/bot/v2/hook/a0fcfb57-c053-47bd-b1e7-b826f44841e4";
+
+    @Override
+    public void test() {
+        run(15L);
+    };
+
+    @Override
+    public void run(Long policyId) {
+        final String runId = UUID.randomUUID().toString().replace("-", "").substring(0,16);
+        ZtPolicy policy = getConfig(policyId);
+        /**已完成扫描
+        //指标选择- 获取indicator信息
+        //指标分类
+        //ID按500个一组,分别获取普通指标/项目信息/余额的数据
+         //在程序中处理判断
+        */
+        if (policy == null){
+            return;
+        }
+        if (policy.getTargetResIds() == null || policy.getTargetResIds().isEmpty()){
+            return;
+        }
+        ZtPolicyRunInfo ztPolicyRunInfo = new ZtPolicyRunInfo();
+        ztPolicyRunInfo.setRunId(runId);
+        ztPolicyRunInfo.setPolicyConfigId(policyId);
+        ztPolicyRunInfo.setScheduledTime(new Date());
+        ztPolicyRunInfo.setStartTime(new Date());
+        ztPolicyRunInfo.setConfigSnapshotJson(JSONObject.toJSONString(policy));
+        ztPolicyConfigMapper.insertPolicyRunRecord(ztPolicyRunInfo);
+
+        List<ZtPolicyCondition> conditions = policy.getConditions();
+        List<Long> indicatorIds = conditions.stream().map(ZtPolicyCondition::getIndicatorId).collect(Collectors.toList());
+
+
+        List<JSONObject> indicatorList = ztPolicyConfigMapper.indicatorList(indicatorIds);
+        Map<Long, JSONObject> indicatorMap = indicatorList.stream().collect(Collectors.toMap(o -> o.getLong("indicator_id"), o -> o));
+
+
+        /**---------------------------------------获取指标数据----------------------------------------------------*/
+
+        Map<String, ZtTargetRunInfo> runRecordMap = new HashMap<>();
+        for (ZtPolicyCondition condition : conditions) {
+            Long indicatorId = condition.getIndicatorId();
+            JSONObject indicator = indicatorMap.get(indicatorId);
+            String indicatorName = indicator.getString("indicator_name");
+            String indicatorCode = indicator.getString("indicator_code");
+            String dataSource =  indicator.getString("data_source");
+            String formula = indicator.getString("formula");
+
+            //批量查询过程
+            Map<String, JSONObject> matchConditionExtraRecordInfo = new HashMap<>();//记录的额外信息
+            Map<String, Object> dataResMap = new HashMap<>();//id -> 查询结果
+            if ("bytedance".equals(dataSource)){//几个特殊字段获取
+                for (int index = 0; index < policy.getTargetResIds().size(); ) {
+                    List<String> subResIds = policy.getTargetResIds().subList(index, Math.min(index+500, policy.getTargetResIds().size()));
+                    List<JSONObject> data = null;
+                    if ("bytedance_project_status".equals(indicatorCode)){
+                        data = ztPolicyConfigMapper.promotionGroupToProjectData(subResIds);
+                    }else if ("bytedance_promote_status".equals(indicatorCode)){
+                        data = ztPolicyConfigMapper.promotionData(subResIds);
+                        data.forEach(o -> dataResMap.put(o.getString("project_id"), o.get(indicatorCode)));
+                    }else if ("bytedance_advertiser_balance".equals(indicatorCode)){
+                        data = ztPolicyConfigMapper.balanceData(subResIds);
+                    }else {
+                        data = ztPolicyConfigMapper.projectData(subResIds);
+                    }
+                    data.forEach(o -> dataResMap.put(o.getString("project_id"), o.get(indicatorCode)));
+                    index += 500;
+                }
+            }else {//自定义指标通用字段
+                JSONObject requestParams = new JSONObject();
+                Integer timeType = policy.getScheduleType() == 3 ? 1: 2;
+                requestParams.put("timeType", timeType);
+                requestParams.put("mediaAdColumns", ReportsConstants.mediaSelectStr);
+                requestParams.put("userDataColumns", ReportsConstants.userSelectStr);
+                requestParams.put("selectIndicator", formula);
+                requestParams.put("managedDimension", policy.getManagedDimension());
+                Integer taskTimeType = condition.getTimeType();
+                String today = DateUtils.getNowDate("yyyy-MM-dd");
+                Long todayLong = Long.parseLong(today.replaceAll("-", ""));
+                if (timeType == 1){
+                    if (taskTimeType == 1){
+                        requestParams.put("startDate", todayLong);
+                        requestParams.put("endDate", todayLong);
+                    }else if (taskTimeType == 2){
+                        String startDate = DateUtils.getNowAddDate("yyyy-MM-dd", -1);
+                        Long startDateLong = Long.parseLong(startDate.replaceAll("-", ""));
+                        requestParams.put("startDate", startDateLong);
+                        requestParams.put("endDate", startDateLong);
+                    }else if (taskTimeType == 3){
+                        String startDate = DateUtils.getNowAddDate("yyyy-MM-dd", -2);
+                        Long startDateLong = Long.parseLong(startDate.replaceAll("-", ""));
+                        requestParams.put("startDate", startDateLong);
+                        requestParams.put("endDate", todayLong);
+                    }else if (taskTimeType == 4){
+                        String startDate = DateUtils.getNowAddDate("yyyy-MM-dd", -6);
+                        Long startDateLong = Long.parseLong(startDate.replaceAll("-", ""));
+                        requestParams.put("startDate", startDateLong);
+                        requestParams.put("endDate", startDateLong);
+                    }
+                }else {
+                    if (taskTimeType == 11){
+                        requestParams.put("date", todayLong);
+                    }else if (taskTimeType == 12){
+                        ZoneId zone = ZoneId.systemDefault();
+                        LocalDateTime lastHour = LocalDateTime.now(zone).minusHours(1);
+                        String yyyyMMdd = lastHour.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+                        Long date = Long.parseLong(yyyyMMdd);
+                        int H = lastHour.getHour(); // 0-23,不补零
+                        requestParams.put("date", date);
+                        requestParams.put("hour", H);
+                    }
+                }
+                for (int index = 0; index < policy.getTargetResIds().size(); ) {
+                    List<String> subResIds = policy.getTargetResIds().subList(index, Math.min(index+500, policy.getTargetResIds().size()));
+                    requestParams.put("idList", subResIds);
+                    List<JSONObject> data = ztPolicyConfigMapper.adData(requestParams);
+                    data.forEach(o -> dataResMap.put(o.getString("target_id"), o.get(indicatorCode)));
+                    index += 500;
+                }
+            }
+
+            /**--------------------------------------指标数据判断阈值-----------------------------------------------------*/
+            //处理dataResMap,和condition条件做  比较,顺便做记录
+            for (String targetId : policy.getTargetResIds()){
+                ZtTargetRunInfo runInfo = new ZtTargetRunInfo();
+                final String recordId = UUID.randomUUID().toString().replace("-", "").substring(0,16); //记录,runId
+                runInfo.setPolicyId(policyId);
+                runInfo.setRunId(runId);
+                runInfo.setUuid(recordId);
+                runInfo.setManagedDimension(policy.getManagedDimension());
+                runInfo.setTargetId(targetId);
+                runInfo.setIndicatorInfo(indicator);
+                runInfo.setCondition(condition);
+                Object indValueObj = dataResMap.get(targetId);
+                String indValue = indValueObj == null ? "0" : String.valueOf(indValueObj);
+                runInfo.setCurrentValue(indValue);
+                //判断是否触发操作
+                boolean isExecute = false;
+                String operator = condition.getOperator();
+                if (operator.equals("BETWEEN")){
+                    Double value = Double.parseDouble(indValue);
+                    Double compareValueMin = condition.getCompareValueJsonObject().getDoubleValue("min");
+                    Double compareValueMax = condition.getCompareValueJsonObject().getDoubleValue("max");
+                    if (value >= compareValueMin && value <= compareValueMax){
+                        isExecute = true;
+                    }
+                    String matchedConditionDesc = indicatorName + ": "+value + "介于"+compareValueMin+", "+compareValueMax;
+                    runInfo.setMatchedConditionDesc(matchedConditionDesc);
+                }else if (operator.equals("EQ")){
+                    String value = indValue;
+                    String compareValue = condition.getCompareValueJsonObject().getString("value");
+                    if (Objects.equals(value, compareValue)){
+                        isExecute = true;
+                    }
+                    String matchedConditionDesc = indicatorName + ": "+ value+" 等于 "+value;
+                    runInfo.setMatchedConditionDesc(matchedConditionDesc);
+                }else if (operator.equals("NEQ")){
+                    String value = indValue;
+                    String compareValue = condition.getCompareValueJsonObject().getString("value");
+                    if (!Objects.equals(value, compareValue)){
+                        isExecute = true;
+                    }
+                    String matchedConditionDesc = indicatorName + ": "+ value+" 不等于 "+value;
+                    runInfo.setMatchedConditionDesc(matchedConditionDesc);
+                }else {
+                    Double value = Double.parseDouble(indValue);
+                    Double compareValue = condition.getCompareValueJsonObject().getDoubleValue("value");
+                    if (operator.equals("GT")){
+                        isExecute = value > compareValue;
+                        String matchedConditionDesc = indicatorName + ": "+ value + " > "+compareValue;
+                        runInfo.setMatchedConditionDesc(matchedConditionDesc);
+                    }else if (operator.equals("LT")){
+                        isExecute = value < compareValue;
+                        String matchedConditionDesc = indicatorName + ": "+ value + " < "+compareValue;
+                        runInfo.setMatchedConditionDesc(matchedConditionDesc);
+                    }else if (operator.equals("GE")){
+                        isExecute = value >= compareValue;
+                        String matchedConditionDesc = indicatorName + ": "+ value + " >= "+compareValue;
+                        runInfo.setMatchedConditionDesc(matchedConditionDesc);
+                    }else {
+                        FeishuRobotUtil.sendTextToWebhook("智投助手任务报错-判断符号无效,任务中止。任务ID:"+policyId+"条件ID:"+condition.getId(), ERROR_HOOK_URL);
+                    }
+
+                }
+                runInfo.setIsExecute(isExecute);
+                runInfo.setTriggerTime(new Date());
+
+                JSONObject matchedConditionJson = new JSONObject();
+                matchedConditionJson.put("indicatorCode", indicatorCode);
+                matchedConditionJson.put("operator",  operator);
+                matchedConditionJson.put("compareValue", condition.getCompareValueJson());
+                matchedConditionJson.put("currentValue", indValue);
+                runInfo.setMatchedConditionJson(matchedConditionJson);
+                runInfo.setMatchedConditionJsonStr(matchedConditionJson.toJSONString());
+                runInfo.setConditionId(runInfo.getCondition().getId());
+                if (policy.getManagedDimension() == 1){
+                    runInfo.setAdvertiserId(Long.valueOf(targetId));
+                }else {
+                    String advId = policy.getTargetFromAdvertiserId().get(targetId);
+                    runInfo.setAdvertiserId(Long.valueOf(advId));
+                }
+
+                runRecordMap.put(targetId+"_"+condition.getId(), runInfo);
+            }
+        }
+        //condition结束
+        // 存储condition的记录
+        List<ZtTargetRunInfo> runInfoList = new ArrayList<>(runRecordMap.values());
+        ztPolicyConfigMapper.insertRunRecords(runInfoList);
+
+
+        /**---------------------------------------所有指标做且/或判断----------------------------------------------------*/
+        //挑拣出达到阈值的,走执行
+        Integer conditionLogic = policy.getConditionLogic();//1且2或
+        List<String> executeTargetList = new ArrayList<>();//执行列表
+        for (String targetId : policy.getTargetResIds()){
+            if (conditionLogic == 1){//且
+                boolean notMatched = false;
+                for (ZtPolicyCondition c : policy.getConditions()){
+                    ZtTargetRunInfo runInfo = runRecordMap.get(targetId+"_"+c.getId());
+                    if (!runInfo.getIsExecute()) {
+                        notMatched = true;
+                        break;
+                    }
+                }
+                if (!notMatched){
+                    executeTargetList.add(targetId);
+                }
+            }else {//或
+                boolean isExecute = false;
+                for (ZtPolicyCondition c : policy.getConditions()){
+                    ZtTargetRunInfo runInfo = runRecordMap.get(targetId+"_"+c.getId());
+                    if (runInfo.getIsExecute()){
+                        isExecute = true;
+                        break;
+                    }
+                }
+                if (isExecute){
+                    executeTargetList.add(targetId);
+                }
+            }
+        }
+
+
+        System.out.println(">>>>>执行列表");
+        System.out.println(JSON.toJSONString(executeTargetList));
+        //没有要执行的,记录一下然后结束
+        if (executeTargetList.isEmpty()){
+            ztPolicyRunInfo.setEndTime(new Date());
+            ztPolicyRunInfo.setTriggered(0);
+            ztPolicyRunInfo.setResultStatus(1);
+            ztPolicyRunInfo.setErrorMessage("成功");
+            ztPolicyConfigMapper.updatePolicyRunRecord(ztPolicyRunInfo);
+            return;
+        }else {
+            ztPolicyRunInfo.setTriggered(0);
+            ztPolicyConfigMapper.updatePolicyRunRecord(ztPolicyRunInfo);
+        }
+
+        /**-------------------------------------执行------------------------------------------------------*/
+        //根据executeTargetList来执行
+
+        List<ZtPolicyAction> actions = policy.getActions();
+        for (ZtPolicyAction action : actions){
+            ZtActionLog actionLog = new ZtActionLog();
+            actionLog.setActionId(action.getId());
+            actionLog.setId(UUID.randomUUID().toString().replace("-","").substring(0,16));
+            actionLog.setRunId(runId);
+            actionLog.setPolicyId(policyId);
+            actionLog.setTargetType(policy.getManagedDimension());
+            actionLog.setActionType(action.getActionType());
+
+            String operateFreqCircle = action.getActionRuleJsonObject().getString("operateFreqCircle");
+            Integer operateFreqLimit = action.getActionRuleJsonObject().getInteger("operateFreqLimit");
+
+            if (action.getActionType() == 10){//feishu
+                actionLog.setTargetId("0");
+                Integer fsTimeType = action.getActionRuleJsonObject().getInteger("fsTimeType");
+                if (fsTimeType == 2){
+                    List<String> fsTimeBitmap = action.getActionRuleJsonObject().getJSONArray("fsTimeBitmap").toJavaList(String.class);
+                    if (!isInFeishuTime(fsTimeBitmap)){//不在时间段,留一条记录直接退出
+                        recordActionSkip(actionLog, 1);
+                        continue;
+                    }
+                }
+                //因为所有的广告账号一起发飞书,就不单独记录targetId了
+                boolean isOperation = isOperation(policyId, "0", action.getId(), operateFreqLimit);
+                if (!isOperation){
+                    recordActionSkip(actionLog, 2);
+                    continue;
+                }
+                //飞书通知所有的一起发送,但是记录可以分开记录
+                StringBuffer feishuMessageTextSb = new StringBuffer();
+                feishuMessageTextSb.append("智投助手触发预警\n");
+                feishuMessageTextSb.append("触发策略名称:").append(policy.getName()).append("\n");
+                feishuMessageTextSb.append("触发预警广告账号:\n");
+                for (String executeTarget : executeTargetList){
+                    feishuMessageTextSb.append(executeTarget+"\n");
+                }
+                String fsMessage = feishuMessageTextSb.toString();
+                Integer fsSendType = action.getActionRuleJsonObject().getInteger("fsSendType");//1飞书应用单发 2群机器人
+
+                recordOperation(policyId, "0", action.getId(), operateFreqCircle);//频控记录
+                if (fsSendType == 1){//单发,发给策略的创建人
+                    Long userId = policy.getCreateUserId();
+                    List<JSONObject> users = reportsAdPushMapper.selectUserList();
+                    List<String> phonenumbers = new ArrayList<>();
+                    for (JSONObject u : users){
+                        Long us = u.getLong("user_id");
+                        if (Objects.equals(us, userId)){
+                            String phone = u.getString("phonenumber");
+                            phonenumbers.add(phone);
+                            break;
+                        }
+                    }
+                    try {
+                        if (!phonenumbers.isEmpty()){
+                            Map<String, String> phoneUseridMap = FeishuRobotUtil.getFsUserIds(FeishuRobotUtil.getFsZtTenantToken(), phonenumbers);
+                            if (phoneUseridMap != null) {
+                                FeishuRobotUtil.sendTextToDt(fsMessage, new ArrayList<>(phoneUseridMap.values()));
+                                recordActionSuccess(actionLog);
+                            }else {
+                                recordActionError(actionLog, "未找到飞书用户ID信息");
+                            }
+                        }else {
+                            recordActionError(actionLog, "未找到用户电话号码信息");
+                        }
+                    } catch (IOException e) {
+                        String message = e.getMessage();
+                        if (message.length()>500){
+                            message=message.substring(0,500);
+                        }
+                        recordActionError(actionLog, message);
+                    }
+                    // 整理这里可能遇到的错误点,写入action_log
+
+                }else {
+                    // 频次、时间段拦截, 频次拦截key: policyId_targetId
+                    List<String> fsHookUrls = action.getActionRuleJsonObject().getJSONArray("fsHookUrls").toJavaList(String.class);
+                    for (String hookUrl : fsHookUrls){
+                        try {
+                            FeishuRobotUtil.sendTextToWebhook(fsMessage, hookUrl);
+                            recordActionSuccess(actionLog);
+                        }catch (Exception e){
+                            String message = e.getMessage();
+                            if (message.length()>500){
+                                message=message.substring(0,500);
+                            }
+                            recordActionError(actionLog, message);
+                        }
+                    }
+                }
+
+            }else if (action.getActionType() == 1 || action.getActionType() == 2 || action.getActionType() == 3){//调账号预算
+
+                for (String executeTarget : executeTargetList){
+                    actionLog.setTargetId(executeTarget);
+                    if (!isOperation(policyId, executeTarget, action.getId(), operateFreqLimit)){
+                        //达到频控,不执行
+                        recordActionSkip(actionLog, 2);
+                        continue;
+                    }
+                    Integer changeForward =  action.getActionRuleJsonObject().getInteger("changeForward");
+                    BigDecimal changeRate = action.getActionRuleJsonObject().getBigDecimal("changeRate");
+                    BigDecimal changeAmountLimit =  action.getActionRuleJsonObject().getBigDecimal("changeAmountLimit");
+                    JSONObject budgetInfo;
+                    if (action.getActionType() == 1){
+                        budgetInfo = bytedanceBudgetStatusService.getAdvertiserBudget(Long.valueOf(executeTarget));
+                    }else if (action.getActionType() == 2){
+                        String advertiserId = policy.getTargetFromAdvertiserId().get(executeTarget);
+                        budgetInfo = bytedanceBudgetStatusService.getProjectBudget(Long.valueOf(advertiserId), Long.valueOf(executeTarget));
+                    }else{
+                        String advertiserId = policy.getTargetFromAdvertiserId().get(executeTarget);
+                        budgetInfo = bytedanceBudgetStatusService.getPromotionBudget(Long.valueOf(advertiserId), Long.valueOf(executeTarget));
+                    }
+                    if (budgetInfo != null){
+                        String budget_mode =  budgetInfo.getString("budget_mode");
+                        BigDecimal budget = budgetInfo.getBigDecimal("budget");
+                        //计算调整之后的预算
+                        BigDecimal change;
+                        change = budget.multiply(changeRate).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
+                        if (changeAmountLimit != null && change.compareTo(changeAmountLimit) > 0){
+                            change = changeAmountLimit;
+                        }
+                        BigDecimal budgetChange;
+                        if (changeForward == 1){
+                            budgetChange = budget.add(change);
+                        }else {
+                            budgetChange = budget.subtract(change);
+                        }
+
+                        try {
+                            JSONObject operationResult = null;
+                            if (action.getActionType() == 1){
+                                operationResult = bytedanceBudgetStatusService.updateAdvertiserBudget(Long.valueOf(executeTarget), budget_mode, budgetChange);
+                            }else if (action.getActionType() == 2){
+                                String advertiserId = policy.getTargetFromAdvertiserId().get(executeTarget);
+                                operationResult = bytedanceBudgetStatusService.updateProjectBudget(Long.valueOf(advertiserId), Long.valueOf(executeTarget), budget_mode, budgetChange);
+                            }else{
+                                String advertiserId = policy.getTargetFromAdvertiserId().get(executeTarget);
+                                operationResult = bytedanceBudgetStatusService.updatePromotionBudget(Long.valueOf(advertiserId), Long.valueOf(executeTarget), budget_mode, budgetChange);
+                            }
+                            recordOperation(policyId, executeTarget, action.getId(), operateFreqCircle);
+                            if (operationResult == null){
+                                recordActionError(actionLog, "调整预算失败,调整值:"+budgetChange);
+                            }else {
+                                Integer code =  operationResult.getInteger("code");
+                                if (code == null){
+                                    recordActionError(actionLog, "获取调整预算接口结果失败,调整值:"+budgetChange);
+                                }else if (code != 0){
+                                    recordActionError(actionLog, "调整预算接口错误, message:"+operationResult.getString("message")+", 调整值:"+budgetChange);
+                                }else {
+                                    recordActionSuccess(actionLog);
+                                }
+                            }
+
+                            recordActionSuccess(actionLog);
+                        }catch (Exception e){
+                            String message = e.getMessage();
+                            if (message.length()>500){
+                                message=message.substring(0,500);
+                            }
+                            recordActionError(actionLog, "调整预算出现错误:"+message);
+                        }
+                    }else {
+                        recordActionError(actionLog, "未找到目标的预算信息");
+                    }
+                }
+                //end for
+
+            }
+        }
+
+    }
+
+    private void recordActionSkip(ZtActionLog log, Integer skipReason){
+        recordActionInfo(log, 0, skipReason, 1, null);
+    }
+
+    private void recordActionError(ZtActionLog log, String errorMessage){
+        recordActionInfo(log, 1, null, 2, errorMessage);
+    }
+
+    private void recordActionSuccess(ZtActionLog log){
+        recordActionInfo(log, 1, null, 1, null);
+    }
+
+    private void recordActionInfo(ZtActionLog log, Integer executed, Integer skipReason, Integer resultStatus, String errorMessage){
+        log.setExecuted(executed);
+        log.setSkipReason(skipReason);
+        log.setResultStatus(resultStatus);
+        log.setErrorMessage(errorMessage);
+        ztPolicyConfigMapper.insertActionLog(Collections.singletonList(log));
+    }
+
+    private boolean isInFeishuTime(List<String> feishuBitMap){
+        if (feishuBitMap != null){//判断是否在发送时段内
+            return FsTimeBitmapChecker.hit(feishuBitMap, LocalDateTime.now());
+        }
+        return true;
+    }
+
+    private boolean isOperation(Long policyId, String targetId, Long actionId, Integer operateFreqLimit){
+        if (operateFreqLimit == null || operateFreqLimit == 0){
+            return true;
+        }
+        String rkey = "zt:"+policyId+":"+actionId+":"+targetId;
+        RAtomicLong rAtomicLong = redissonClient.getAtomicLong(rkey);
+        if (!rAtomicLong.isExists()){
+            return true;
+        }
+        long value = rAtomicLong.get();
+        return value < operateFreqLimit;
+    }
+
+    private void recordOperation(Long policyId, String targetId, Long actionId, String operateFreqCircle){
+        String rkey = "zt:"+policyId+":"+actionId+":"+targetId;
+        RAtomicLong rAtomicLong = redissonClient.getAtomicLong(rkey);
+        if (!rAtomicLong.isExists()){
+            if ("HOUR".equals(operateFreqCircle)){
+                rAtomicLong.set(0);
+                ZoneId zone = ZoneId.systemDefault();
+                Instant hourEnd = ZonedDateTime.now(zone)
+                        .truncatedTo(ChronoUnit.HOURS)
+                        .plusHours(1)
+                        .toInstant();
+                rAtomicLong.expire(hourEnd);
+            }else {//DAY
+                rAtomicLong.set(0);
+                ZoneId zone = ZoneId.systemDefault();
+                Instant dayEnd = LocalDate.now(zone)
+                        .plusDays(1)
+                        .atStartOfDay(zone)
+                        .toInstant();
+                rAtomicLong.expire(dayEnd);
+            }
+        }
+        long nowCount = rAtomicLong.incrementAndGet();
+    }
+
+
+
+    private ZtPolicy getConfig(Long policyId){
+        ZtPolicy policy = null;
+        if (policyId != null){
+            policy = ztPolicyConfigMapper.getPolicyDetail(policyId);
+            if (policy.getManagedDimension()==1 && policy.getTargetScope()==1){
+                List<ZtPolicyTargetFilter> filterList = ztPolicyConfigMapper.getTargetFilters(policyId);
+                Map<Integer, List<ZtPolicyTargetFilter>> scopeFilterMap =
+                        filterList.stream().collect(Collectors.groupingBy(ZtPolicyTargetFilter::getFilterKey));
+                for (Map.Entry<Integer, List<ZtPolicyTargetFilter>> entry : scopeFilterMap.entrySet()) {
+                    Integer key = entry.getKey();
+                    List<ZtPolicyTargetFilter> value = entry.getValue();
+                    if (key == 1){
+                        policy.setAppId(value.stream().map(ZtPolicyTargetFilter::getFilterValue).collect(Collectors.toList()));
+                    }else if (key == 2){
+                        Integer businessLine = Integer.parseInt(value.get(0).getFilterValue());
+                        policy.setBusinessLine(businessLine);
+                    }else if (key == 3){
+                        policy.setBusinessMethod(value.stream().map(ZtPolicyTargetFilter::getFilterValue).collect(Collectors.toList()));
+                    }else if (key == 4){
+                        List<String> optimizerIdStringList = value.stream().map(ZtPolicyTargetFilter::getFilterValue).collect(Collectors.toList());
+                        List<Long>  optimizerIdList = optimizerIdStringList.stream().map(Long::parseLong).collect(Collectors.toList());
+                        policy.setOptimizerId(optimizerIdList);
+                    }else if (key == 5){
+                        List<String> deptIdStringList = value.stream().map(ZtPolicyTargetFilter::getFilterValue).collect(Collectors.toList());
+                        List<Long> deptIdList = deptIdStringList.stream().map(Long::parseLong).collect(Collectors.toList());
+                        policy.setDeptId(deptIdList);
+                    }else if (key == 6){
+                        policy.setMovieId(value.stream().map(ZtPolicyTargetFilter::getFilterValue).collect(Collectors.toList()));
+                    }
+                }
+
+                List<String> resIds = ztPolicyConfigMapper.advFilterList(policy.getAppId(), policy.getBusinessLine(),
+                        policy.getBusinessMethod(), policy.getOptimizerId(), policy.getDeptId(), policy.getMovieId());
+                policy.setTargetResIds(resIds);
+            }else {
+                List<ZtPolicyTargetIds> ids =  ztPolicyConfigMapper.getTargetIds(policyId);
+                policy.setTargetScopeIds(ids.stream().map(ZtPolicyTargetIds::getTargetId).collect(Collectors.toList()));
+
+                if (policy.getManagedDimension() == 1 && policy.getTargetScope() == 2){
+                    List<String> resIds = ztPolicyConfigMapper.advListFromAdvIds(policyId);
+                    policy.setTargetResIds(resIds);
+                }else if (policy.getManagedDimension() == 2 && policy.getTargetScope() == 2){
+                    List<JSONObject> resIds = ztPolicyConfigMapper.projectListFromAdvIds(policyId);
+                    List<String> rList = new ArrayList<>();
+                    Map<String, String> targetFromAdvertiserId = new HashMap<>();
+                    for (JSONObject resId : resIds) {
+                        rList.add(resId.getString("a_id"));
+                        targetFromAdvertiserId.put(resId.getString("a_id"), resId.getString("advertiser_id"));
+                    }
+                    policy.setTargetResIds(rList);
+                    policy.setTargetFromAdvertiserId(targetFromAdvertiserId);
+                }else if (policy.getManagedDimension() == 2 && policy.getTargetScope() == 3){
+                    List<JSONObject> resIds = ztPolicyConfigMapper.projectListFromProjIds(policyId);
+                    List<String> rList = new ArrayList<>();
+                    Map<String, String> targetFromAdvertiserId = new HashMap<>();
+                    for (JSONObject resId : resIds) {
+                        rList.add(resId.getString("a_id"));
+                        targetFromAdvertiserId.put(resId.getString("a_id"), resId.getString("advertiser_id"));
+                    }
+                    policy.setTargetResIds(rList);
+                    policy.setTargetFromAdvertiserId(targetFromAdvertiserId);
+                }else if (policy.getManagedDimension() == 3 && policy.getTargetScope() == 2){
+                    List<JSONObject> resIds = ztPolicyConfigMapper.adListFromAdvIds(policyId);
+                    List<String> rList = new ArrayList<>();
+                    Map<String, String> targetFromAdvertiserId = new HashMap<>();
+                    for (JSONObject resId : resIds) {
+                        rList.add(resId.getString("ad_id"));
+                        targetFromAdvertiserId.put(resId.getString("ad_id"), resId.getString("advertiser_id"));
+                    }
+                    policy.setTargetResIds(rList);
+                    policy.setTargetFromAdvertiserId(targetFromAdvertiserId);
+                }else if (policy.getManagedDimension() == 3 && policy.getTargetScope() == 4){
+                    List<JSONObject> resIds = ztPolicyConfigMapper.adListFromAdIds(policyId);
+                    List<String> rList = new ArrayList<>();
+                    Map<String, String> targetFromAdvertiserId = new HashMap<>();
+                    for (JSONObject resId : resIds) {
+                        rList.add(resId.getString("ad_id"));
+                        targetFromAdvertiserId.put(resId.getString("ad_id"), resId.getString("advertiser_id"));
+                    }
+                    policy.setTargetResIds(rList);
+                    policy.setTargetFromAdvertiserId(targetFromAdvertiserId);
+                }
+
+            }
+
+        }
+
+        for (ZtPolicyCondition condition : policy.getConditions()){
+            condition.setCompareValueJsonObject(JSON.parseObject(condition.getCompareValueJson()));
+        }
+        for (ZtPolicyAction action : policy.getActions()){
+            action.setActionRuleJsonObject(JSON.parseObject(action.getActionRuleJson()));
+        }
+        return policy;
+    }
+
+
+}

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

@@ -232,7 +232,7 @@ public class FeishuRobotUtil {
 
     }
 
-    public static void sendTextToWebhook(String text, String url) throws IOException {
+    public static void sendTextToWebhook(String text, String url) {
         JSONObject content = new JSONObject();
         content.put("text", text);
         JSONObject request = new JSONObject();

+ 43 - 0
job-bytedance/src/main/java/cn/com/ctop/job/bytedance/utils/FsTimeBitmapChecker.java

@@ -0,0 +1,43 @@
+package cn.com.ctop.job.bytedance.utils;
+
+import java.time.DayOfWeek;
+import java.time.LocalDateTime;
+import java.util.List;
+
+public final class FsTimeBitmapChecker {
+
+    /**
+     * 判断指定时间是否命中 fsTimeBitmap
+     *
+     * @param fsTimeBitmap 7 x 48 的 bitmap
+     * @param time 要判断的时间
+     */
+    public static boolean hit(List<String> fsTimeBitmap, LocalDateTime time) {
+        if (fsTimeBitmap == null || fsTimeBitmap.size() != 7) {
+            throw new IllegalArgumentException("fsTimeBitmap must have 7 elements");
+        }
+
+        // 周一 = 1 ... 周日 = 7  → 转成 Mon=0
+        DayOfWeek dow = time.getDayOfWeek();
+        int dayIndex = dow.getValue() - 1; // 0=Mon, 6=Sun
+
+        String dayBits = fsTimeBitmap.get(dayIndex);
+        if (dayBits == null || dayBits.length() != 48) {
+            throw new IllegalArgumentException("bitmap day length must be 48");
+        }
+
+        int hour = time.getHour();      // 0-23
+        int minute = time.getMinute();  // 0-59
+
+        int slot = hour * 2 + (minute >= 30 ? 1 : 0);
+
+        char bit = dayBits.charAt(slot);
+        if (bit == '1') {
+            return true;
+        }
+        if (bit == '0') {
+            return false;
+        }
+        throw new IllegalArgumentException("invalid char '" + bit + "' in bitmap");
+    }
+}