|
@@ -0,0 +1,296 @@
|
|
|
+package cn.com.ctop.kuaishou.modules.report.service.impl;
|
|
|
+
|
|
|
+import cn.com.ctop.common.module.entity.CtopOauthToken;
|
|
|
+import cn.com.ctop.common.module.mapper.UserAllocationMapper;
|
|
|
+import cn.com.ctop.common.module.service.ICtopOauthTokenService;
|
|
|
+import cn.com.ctop.common.module.service.IMessageTemplate;
|
|
|
+import cn.com.ctop.common.module.service.ISendMessageService;
|
|
|
+import cn.com.ctop.common.module.utils.Check;
|
|
|
+import cn.com.ctop.common.module.utils.HttpUtils;
|
|
|
+import cn.com.ctop.common.module.utils.KuaishouInterfaceConstant;
|
|
|
+import cn.com.ctop.common.module.utils.PropertiesUtils;
|
|
|
+import cn.com.ctop.kuaishou.modules.batch.entity.KuaiShouCreative;
|
|
|
+import cn.com.ctop.kuaishou.modules.batch.mapper.KuaiShouCreativeMapper;
|
|
|
+import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouUpdateService;
|
|
|
+import cn.com.ctop.kuaishou.modules.report.entity.KuaishouZombieCreative;
|
|
|
+import cn.com.ctop.kuaishou.modules.report.mapper.KuaishouReportDailyCreativeMapper;
|
|
|
+import cn.com.ctop.kuaishou.modules.report.mapper.KuaishouZombieCreativeMapper;
|
|
|
+import cn.com.ctop.kuaishou.modules.report.service.IKuaishouZombieCreativeService;
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.xxl.job.core.enums.NoEn;
|
|
|
+import org.jeecg.common.util.DateUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 僵尸计划记录
|
|
|
+ *
|
|
|
+ * @author jeecg-boot
|
|
|
+ * @version V1.0
|
|
|
+ * @date 2020-10-14
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class KuaishouZombieCreativeServiceImpl extends ServiceImpl<KuaishouZombieCreativeMapper, KuaishouZombieCreative> implements IKuaishouZombieCreativeService {
|
|
|
+ @Autowired
|
|
|
+ private KuaishouReportDailyCreativeMapper kuaishouReportDailyCreativeMapper;
|
|
|
+ @Autowired
|
|
|
+ private KuaishouZombieCreativeMapper kuaishouZombieCreativeMapper;
|
|
|
+ @Autowired
|
|
|
+ private KuaiShouCreativeMapper kuaiShouCreativeMapper;
|
|
|
+ @Autowired
|
|
|
+ private ISendMessageService sendMessageService;
|
|
|
+ @Autowired
|
|
|
+ private IMessageTemplate messageTemplate;
|
|
|
+ @Autowired
|
|
|
+ private UserAllocationMapper userAllocationMapper;
|
|
|
+ @Autowired
|
|
|
+ private ICtopOauthTokenService oauthTokenService;
|
|
|
+ @Autowired
|
|
|
+ private IKuaiShouUpdateService kuaiShouUpdateService;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 筛选僵尸计划
|
|
|
+ * 逻辑简述:
|
|
|
+ * 1、以当天时间为节点,往前取五天的数据,若创意消耗(花费)都为0,则获取其数据,并insert为待确认状态的僵尸创意。
|
|
|
+ * 2、获取前一天存储的待确认僵尸计划
|
|
|
+ * 3、步骤二和步骤一的数据作比对筛选,若两组数据都含有该创意时,并且为了保证数据的准确性,还需调用API接口获取当天实时消耗明细,
|
|
|
+ * 若消耗不为0,则表明为真正的僵尸创意,3.1关停创意;3.2然后发送“关停通知”消息;3.3最后更新其状态。
|
|
|
+ * 4、步骤二存在,步骤一不存在说明创意已经活跃,更新其状态为“2”已活跃、反之则是新的待确认僵尸创意,作insert操作
|
|
|
+ * 5、insert操作时,考虑到数据重复的问题,需先查询全部僵尸计划,作筛选比对,已有则更新,否则就新增。
|
|
|
+ * **凡insert数据皆为状态0,待确认僵尸创意,且要发送“提醒通知”。
|
|
|
+ *
|
|
|
+ * @param
|
|
|
+ * @return void
|
|
|
+ * @throws
|
|
|
+ * @author ZHAOXA
|
|
|
+ */
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void checkZombieProject() {
|
|
|
+ //获取五天前的日期 yyyy-MM-dd
|
|
|
+ String date = DateUtils.getLastDay(DateUtils.formatDate(new Date()), NoEn.NO5.valueInt());
|
|
|
+ //查询五天内花费都为0的数据集
|
|
|
+ List<JSONObject> list = kuaishouReportDailyCreativeMapper.checkZombieProject(date);
|
|
|
+ //查询审核未通过且超过5天的创意集
|
|
|
+ List<JSONObject> olist = kuaiShouCreativeMapper.checkZombieProject(date);
|
|
|
+ if (Check.isNull(list) && Check.isNull(olist)) {
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ list.addAll(olist);
|
|
|
+ }
|
|
|
+ //查询数据库中前一天存储的待确认僵尸创意集
|
|
|
+ Map<String, Object> map = new HashMap<>();
|
|
|
+ map.put("record_time", DateUtils.getLastDay(DateUtils.formatDate(new Date()), NoEn.NO1.valueInt()));
|
|
|
+ map.put("status", NoEn.NO0.valueStr());
|
|
|
+ List<KuaishouZombieCreative> zombieCreatives = kuaishouZombieCreativeMapper.selectByMap(map);
|
|
|
+ if (!Check.isNull(zombieCreatives)) {
|
|
|
+ List<KuaishouZombieCreative> zombiePlans = new ArrayList<>();
|
|
|
+ List<JSONObject> zombieList = new ArrayList<>();
|
|
|
+ //通过账户和创意ID,筛选出确认的僵尸计划账户
|
|
|
+ zombieCreatives.forEach(zombie -> {
|
|
|
+ list.forEach(obj -> {
|
|
|
+ if (zombie.getAccountId() - obj.getInteger("accountId") == 0 && zombie.getCreativeId() - obj.getInteger("creativeId") == 0) {
|
|
|
+ QueryWrapper<KuaishouZombieCreative> wrapper = new QueryWrapper<>();
|
|
|
+ wrapper.eq("id", zombie.getId());
|
|
|
+ CtopOauthToken token = oauthTokenService.getTokenByAccountId(zombie.getAccountId());
|
|
|
+ //校验当天是不是依旧不活跃,是则关停,否则更新激活状态
|
|
|
+ if (isActivation(token, zombie.getCreativeId())) {
|
|
|
+ //3.1关停创意
|
|
|
+ Map<String, Object> updateMap = kuaiShouUpdateService.updateCreativeStatus(token.getAccessToken(), zombie.getAccountId(), zombie.getCreativeId(), NoEn.NO2.valueInt(), obj.getString("userId"));
|
|
|
+ if (!Check.isNull(updateMap) && (boolean) updateMap.get("success")) {
|
|
|
+ //3.2发送创意关停通知
|
|
|
+ sendMsg(NoEn.NO2.valueStr(), obj);
|
|
|
+ }
|
|
|
+ zombie.setStatus(NoEn.NO1.valueStr());
|
|
|
+ //记录已确认的僵尸计划集
|
|
|
+ zombiePlans.add(zombie);
|
|
|
+ zombieList.add(obj);
|
|
|
+ } else {
|
|
|
+ zombie.setStatus(NoEn.NO2.valueStr());
|
|
|
+ }
|
|
|
+ //3.3更新状态
|
|
|
+ kuaishouZombieCreativeMapper.update(zombie, wrapper);
|
|
|
+ //校验数据为审核未通过数据,并将投放状态设为“暂停”
|
|
|
+ if (Check.isNull(obj.getInteger("dates"))) {
|
|
|
+ QueryWrapper<KuaiShouCreative> creativeWrapper = new QueryWrapper<>();
|
|
|
+ creativeWrapper.eq("id", obj.getString("id"));
|
|
|
+ KuaiShouCreative creative = new KuaiShouCreative();
|
|
|
+ //投放状态 1:投放中;2:暂停
|
|
|
+ creative.setPutStatus(NoEn.NO2.valueInt());
|
|
|
+ kuaiShouCreativeMapper.update(creative, creativeWrapper);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ //过滤掉已确认的僵尸数据,剩余活跃的计划
|
|
|
+ zombieCreatives.removeAll(zombiePlans);
|
|
|
+ //更新计划状态为已活跃
|
|
|
+ zombieCreatives.forEach(zom -> {
|
|
|
+ QueryWrapper<KuaishouZombieCreative> wrapper = new QueryWrapper<>();
|
|
|
+ wrapper.eq("id", zom.getId());
|
|
|
+ //状态 0-待确认,1-是僵尸计划,2-已活跃,不是僵尸计划
|
|
|
+ zom.setStatus(NoEn.NO2.valueStr());
|
|
|
+ kuaishouZombieCreativeMapper.update(zom, wrapper);
|
|
|
+ });
|
|
|
+ //过滤掉已确认的数据,新增新一批的僵尸计划
|
|
|
+ list.removeAll(zombieList);
|
|
|
+ insertorUpdate(list);
|
|
|
+ } else {
|
|
|
+ insertorUpdate(list);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 筛选僵尸计划数据,新增或更新操作
|
|
|
+ *
|
|
|
+ * @param
|
|
|
+ * @return void
|
|
|
+ * @throws
|
|
|
+ * @author ZHAOXA
|
|
|
+ */
|
|
|
+ private void insertorUpdate(List<JSONObject> list) {
|
|
|
+ //查询库中已有的全部数据
|
|
|
+ List<KuaishouZombieCreative> kuaishouZombieCreatives = kuaishouZombieCreativeMapper.selectList(null);
|
|
|
+ if (!Check.isNull(kuaishouZombieCreatives)) {
|
|
|
+ List<KuaishouZombieCreative> updateList = new ArrayList<>();
|
|
|
+ List<JSONObject> toBeDeleted = new ArrayList<>();
|
|
|
+ //待新增的和库中数据对比筛选,选出相同账户和相同创意的数据
|
|
|
+ list.forEach(obj -> {
|
|
|
+ kuaishouZombieCreatives.forEach(zom -> {
|
|
|
+ if (zom.getAccountId() - obj.getInteger("accountId") == 0 && zom.getCreativeId() - obj.getInteger("creativeId") == 0) {
|
|
|
+ updateList.add(zom);
|
|
|
+ toBeDeleted.add(obj);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ //已有的数据作更新操作
|
|
|
+ if (!Check.isNull(updateList)) {
|
|
|
+ updateList.forEach(zom -> {
|
|
|
+ QueryWrapper<KuaishouZombieCreative> wrapper = new QueryWrapper<>();
|
|
|
+ wrapper.eq("id", zom.getId());
|
|
|
+ zom.setStatus(NoEn.NO0.valueStr());
|
|
|
+ zom.setRecordTime(DateUtils.getNowDate(DateUtils.WEB_FORMAT));
|
|
|
+ kuaishouZombieCreativeMapper.update(zom, wrapper);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ //去除已有的数据,新的数据作insert
|
|
|
+ list.removeAll(toBeDeleted);
|
|
|
+ insert(list);
|
|
|
+ } else {
|
|
|
+ insert(list);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新增待确认僵尸数据,并发送消息通知
|
|
|
+ * userAllocation
|
|
|
+ *
|
|
|
+ * @param
|
|
|
+ * @return void
|
|
|
+ * @throws
|
|
|
+ * @author ZHAOXA
|
|
|
+ */
|
|
|
+ private void insert(List<JSONObject> list) {
|
|
|
+ list.forEach(obj -> {
|
|
|
+ KuaishouZombieCreative create = new KuaishouZombieCreative();
|
|
|
+ create.setAccountId(obj.getLong("accountId"));
|
|
|
+ create.setCreativeId(obj.getLong("creativeId"));
|
|
|
+ create.setCreativeName(obj.getString("creativeName"));
|
|
|
+ create.setRecordTime(DateUtils.getNowDate(DateUtils.WEB_FORMAT));
|
|
|
+ //状态 0-待确认,1-是僵尸计划,2-已活跃,不是僵尸计划
|
|
|
+ create.setStatus(NoEn.NO0.valueStr());
|
|
|
+ kuaishouZombieCreativeMapper.insert(create);
|
|
|
+ sendMsg(NoEn.NO1.valueStr(), obj);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送消息通知
|
|
|
+ *
|
|
|
+ * @param
|
|
|
+ * @return void
|
|
|
+ * @throws
|
|
|
+ * @author ZHAOXA
|
|
|
+ */
|
|
|
+ private void sendMsg(String type, JSONObject obj) {
|
|
|
+ String userId = userAllocationMapper.queryUserIdByAccountId(obj.getLong("accountId"));
|
|
|
+ String message = "";
|
|
|
+ if (NoEn.NO1.valueStr().equals(type)) {
|
|
|
+ //提醒通知
|
|
|
+ message = messageTemplate.getZombieCreativeRemindTemplate(obj);
|
|
|
+ } else {
|
|
|
+ //关停通知
|
|
|
+ message = messageTemplate.getZombieCreativeShutDownTemplate(obj);
|
|
|
+ }
|
|
|
+ sendMessageService.sendMessage(userId, message);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查创意是否激活
|
|
|
+ *
|
|
|
+ * @param
|
|
|
+ * @return boolean
|
|
|
+ * @throws
|
|
|
+ * @author ZHAOXA
|
|
|
+ */
|
|
|
+ private boolean isActivation(CtopOauthToken oauthToken, long creativeId) {
|
|
|
+ boolean flag = true;
|
|
|
+ try {
|
|
|
+ JSONArray creativeIds = new JSONArray();
|
|
|
+ creativeIds.add(creativeId);
|
|
|
+ JSONObject info = getAdvertiserCreativeReportDailyInfo(oauthToken, new Date(), new Date(), creativeIds);
|
|
|
+ if (!Check.isNull(info) && !Check.isNull(info.getBigDecimal("charge"))) {
|
|
|
+ flag = false;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("校验创意是否激活,异常", e);
|
|
|
+ //异常时,默认为激活状态
|
|
|
+ flag = false;
|
|
|
+ }
|
|
|
+ return flag;
|
|
|
+ }
|
|
|
+
|
|
|
+ private JSONObject getAdvertiserCreativeReportDailyInfo(CtopOauthToken token, Date startDate, Date endDate, JSONArray creativeIds) throws Exception {
|
|
|
+ String url = PropertiesUtils.getConfig("kuaishou_api_url") + KuaishouInterfaceConstant.CREATIVE_REPORT;
|
|
|
+ Map<String, String> headers = new HashMap<String, String>();
|
|
|
+ headers.put("Content-Type", "application/json");
|
|
|
+ headers.put("Access-Token", token.getAccessToken());
|
|
|
+ Map<String, Object> param = new HashMap<String, Object>();
|
|
|
+ if (!Check.isNull(startDate)) {
|
|
|
+ param.put("start_date", DateUtils.formatDate(startDate));
|
|
|
+ }
|
|
|
+ if (!Check.isNull(endDate)) {
|
|
|
+ param.put("end_date", DateUtils.formatDate(endDate));
|
|
|
+ }
|
|
|
+ if (!Check.isNull(creativeIds) && creativeIds.size() > 0) {
|
|
|
+ param.put("creative_ids", creativeIds);
|
|
|
+
|
|
|
+ }
|
|
|
+ param.put("advertiser_id", token.getAccountId());
|
|
|
+ param.put("temporal_granularity", "DAILY");
|
|
|
+
|
|
|
+ String result = HttpUtils.httpPostRequest(url, param, headers);
|
|
|
+ JSONObject resultJson = JSONObject.parseObject(result);
|
|
|
+ Integer code = resultJson.getInteger("code");
|
|
|
+ String message = resultJson.getString("message");
|
|
|
+ if (null == code || code != 0) {
|
|
|
+ throw new Exception("获取快手广告创意分天报表异常:" + message + ",accountId:" + token.getAccountId());
|
|
|
+ }
|
|
|
+ JSONArray details = resultJson.getJSONObject("data").getJSONArray("details");
|
|
|
+ if (null == details || details.size() <= 0) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return (JSONObject) details.get(0);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|