diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java index bff2234..a7dc290 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.BoundSetOperations; import org.springframework.data.redis.core.HashOperations; @@ -18,63 +19,58 @@ import org.springframework.stereotype.Component; * * @author ruoyi **/ -@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@SuppressWarnings(value = {"unchecked", "rawtypes"}) @Component -public class RedisCache -{ +public class RedisCache { @Autowired public RedisTemplate redisTemplate; /** * 缓存基本的对象,Integer、String、实体类等 * - * @param key 缓存的键值 + * @param key 缓存的键值 * @param value 缓存的值 */ - public void setCacheObject(final String key, final T value) - { + public void setCacheObject(final String key, final T value) { redisTemplate.opsForValue().set(key, value); } /** * 缓存基本的对象,Integer、String、实体类等 * - * @param key 缓存的键值 - * @param value 缓存的值 - * @param timeout 时间 + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 * @param timeUnit 时间颗粒度 */ - public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) - { + public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { redisTemplate.opsForValue().set(key, value, timeout, timeUnit); } - public Long getExpireTime(String key){ + public Long getExpireTime(String key) { return redisTemplate.getExpire(key); } /** * 设置有效时间 * - * @param key Redis键 + * @param key Redis键 * @param timeout 超时时间 * @return true=设置成功;false=设置失败 */ - public boolean expire(final String key, final long timeout) - { + public boolean expire(final String key, final long timeout) { return expire(key, timeout, TimeUnit.SECONDS); } /** * 设置有效时间 * - * @param key Redis键 + * @param key Redis键 * @param timeout 超时时间 - * @param unit 时间单位 + * @param unit 时间单位 * @return true=设置成功;false=设置失败 */ - public boolean expire(final String key, final long timeout, final TimeUnit unit) - { + public boolean expire(final String key, final long timeout, final TimeUnit unit) { return redisTemplate.expire(key, timeout, unit); } @@ -84,8 +80,7 @@ public class RedisCache * @param key 缓存键值 * @return 缓存键值对应的数据 */ - public T getCacheObject(final String key) - { + public T getCacheObject(final String key) { ValueOperations operation = redisTemplate.opsForValue(); return operation.get(key); } @@ -95,8 +90,7 @@ public class RedisCache * * @param key */ - public boolean deleteObject(final String key) - { + public boolean deleteObject(final String key) { return redisTemplate.delete(key); } @@ -106,20 +100,18 @@ public class RedisCache * @param collection 多个对象 * @return */ - public long deleteObject(final Collection collection) - { + public long deleteObject(final Collection collection) { return redisTemplate.delete(collection); } /** * 缓存List数据 * - * @param key 缓存的键值 + * @param key 缓存的键值 * @param dataList 待缓存的List数据 * @return 缓存的对象 */ - public long setCacheList(final String key, final List dataList) - { + public long setCacheList(final String key, final List dataList) { Long count = redisTemplate.opsForList().rightPushAll(key, dataList); return count == null ? 0 : count; } @@ -130,24 +122,21 @@ public class RedisCache * @param key 缓存的键值 * @return 缓存键值对应的数据 */ - public List getCacheList(final String key) - { + public List getCacheList(final String key) { return redisTemplate.opsForList().range(key, 0, -1); } /** * 缓存Set * - * @param key 缓存键值 + * @param key 缓存键值 * @param dataSet 缓存的数据 * @return 缓存数据的对象 */ - public BoundSetOperations setCacheSet(final String key, final Set dataSet) - { + public BoundSetOperations setCacheSet(final String key, final Set dataSet) { BoundSetOperations setOperation = redisTemplate.boundSetOps(key); Iterator it = dataSet.iterator(); - while (it.hasNext()) - { + while (it.hasNext()) { setOperation.add(it.next()); } return setOperation; @@ -159,8 +148,7 @@ public class RedisCache * @param key * @return */ - public Set getCacheSet(final String key) - { + public Set getCacheSet(final String key) { return redisTemplate.opsForSet().members(key); } @@ -170,8 +158,7 @@ public class RedisCache * @param key * @param dataMap */ - public void setCacheMap(final String key, final Map dataMap) - { + public void setCacheMap(final String key, final Map dataMap) { if (dataMap != null) { redisTemplate.opsForHash().putAll(key, dataMap); } @@ -183,32 +170,29 @@ public class RedisCache * @param key * @return */ - public Map getCacheMap(final String key) - { + public Map getCacheMap(final String key) { return redisTemplate.opsForHash().entries(key); } /** * 往Hash中存入数据 * - * @param key Redis键 - * @param hKey Hash键 + * @param key Redis键 + * @param hKey Hash键 * @param value 值 */ - public void setCacheMapValue(final String key, final String hKey, final T value) - { + public void setCacheMapValue(final String key, final String hKey, final T value) { redisTemplate.opsForHash().put(key, hKey, value); } /** * 获取Hash中的数据 * - * @param key Redis键 + * @param key Redis键 * @param hKey Hash键 * @return Hash中的对象 */ - public T getCacheMapValue(final String key, final String hKey) - { + public T getCacheMapValue(final String key, final String hKey) { HashOperations opsForHash = redisTemplate.opsForHash(); return opsForHash.get(key, hKey); } @@ -216,12 +200,11 @@ public class RedisCache /** * 获取多个Hash中的数据 * - * @param key Redis键 + * @param key Redis键 * @param hKeys Hash键集合 * @return Hash对象集合 */ - public List getMultiCacheMapValue(final String key, final Collection hKeys) - { + public List getMultiCacheMapValue(final String key, final Collection hKeys) { return redisTemplate.opsForHash().multiGet(key, hKeys); } @@ -231,8 +214,16 @@ public class RedisCache * @param pattern 字符串前缀 * @return 对象列表 */ - public Collection keys(final String pattern) - { + public Collection keys(final String pattern) { return redisTemplate.keys(pattern); } + + + public void rightPush(String key, Object obj) { + redisTemplate.opsForList().rightPush(key, obj); + } + + public Object leftPop(String key) { + return redisTemplate.opsForList().leftPop(key); + } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/AppType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/AppType.java index 63dfe05..6c79e1f 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/AppType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/AppType.java @@ -37,4 +37,13 @@ public enum AppType { public String getInfo() { return info; } + + public static AppType getByCode(String code){ + for (AppType value : AppType.values()) { + if (value.getCode().equals(code)){ + return value; + } + } + return null; + } } diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/MailInfoController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/MailInfoController.java index de50cfb..5fd6330 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/MailInfoController.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/MailInfoController.java @@ -23,6 +23,8 @@ public class MailInfoController extends BaseController { @Autowired private MonitorJobTask monitorJobTask; @Autowired + private RetryErrorSyncJob retryErrorSyncJob; + @Autowired private ManufactureOrderSyncJob manufactureOrderSyncJob; @Autowired private PurchaseArrivalOpenSyncJob purchaseArrivalOpenSyncJob; diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/CJTRetryRequest.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/CJTRetryRequest.java new file mode 100644 index 0000000..34f80cf --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/CJTRetryRequest.java @@ -0,0 +1,37 @@ +package com.ruoyi.quartz.domain; + +import com.ruoyi.common.utils.DateUtils; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * @author yuxiangyong + * @create 2023-07-17 20:31 + */ +@Data +@NoArgsConstructor +public class CJTRetryRequest implements Serializable { + + private LocalDateTime executeTime; + private Integer count; + + public CJTRetryRequest(LocalDateTime executeTime, Integer count) { + this.executeTime = executeTime; + this.count = count; + } + + public void addCount(){ + count++; + } + + public String getQueryFrom(Long reduceMinutes){ + return DateUtils.ldt2str(executeTime.minusMinutes(reduceMinutes),DateUtils.YYYY_MM_DD_HH_MM_SS); + } + + public String getQueryTo(){ + return DateUtils.ldt2str(executeTime,DateUtils.YYYY_MM_DD_HH_MM_SS); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CJT/RetryErrorSyncJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CJT/RetryErrorSyncJob.java new file mode 100644 index 0000000..73ed84c --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CJT/RetryErrorSyncJob.java @@ -0,0 +1,110 @@ +package com.ruoyi.quartz.task.CJT; + +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.enums.AppType; +import com.ruoyi.quartz.domain.CJTRetryRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; + +/** + * @author yuxiangyong + * @create 2023-08-02 20:14 + */ +@Slf4j +@Component("retryErrorSyncJob") +public class RetryErrorSyncJob { + @Autowired + private RedisCache redisCache; + @Autowired + private ManufactureOrderSyncJob manufactureOrderSyncJob; + @Autowired + private PurchaseArrivalOpenSyncJob purchaseArrivalOpenSyncJob; + @Autowired + private PurchaseOrderSyncJob purchaseOrderSyncJob; + @Autowired + private SaleDeliverySyncJob saleDeliverySyncJob; + @Autowired + private SaleDispatchSyncJob saleDispatchSyncJob; + @Autowired + private SaleOrderSyncJob saleOrderSyncJob; + + public void execute(String appTypeCode) { + AppType appType = AppType.getByCode(appTypeCode); + if (appType != null) { + executeDetail(appType); + } + } + + public void execute() { + List retryJobList = retryJobList(); + for (AppType retryJobType : retryJobList) { + executeDetail(retryJobType); + } + } + + private void executeDetail(AppType retryJobType) { + CJTRetryRequest request = null; + String retryKey = null; + switch (retryJobType) { + case SYNC_CJT_MANUFACTURE_ORDER_DATA_TO_MULTI_TABLE: + retryKey = manufactureOrderSyncJob.getRetryKey(); + do { + request = (CJTRetryRequest) redisCache.leftPop(retryKey); + manufactureOrderSyncJob.execute(request); + } while (request != null); + break; + case SYNC_CJT_PURCHASE_ARRIVAL_ORDER_DATA_TO_MULTI_TABLE: + retryKey = purchaseArrivalOpenSyncJob.getRetryKey(); + do { + request = (CJTRetryRequest) redisCache.leftPop(retryKey); + purchaseArrivalOpenSyncJob.execute(request); + } while (request != null); + break; + case SYNC_CJT_PURCHASE_ORDER_DATA_TO_MULTI_TABLE: + retryKey = purchaseOrderSyncJob.getRetryKey(); + do { + request = (CJTRetryRequest) redisCache.leftPop(retryKey); + purchaseOrderSyncJob.execute(request); + } while (request != null); + break; + case SYNC_CJT_SALE_DELIVERY_DATA_TO_MULTI_TABLE: + retryKey = saleDeliverySyncJob.getRetryKey(); + do { + request = (CJTRetryRequest) redisCache.leftPop(retryKey); + saleDeliverySyncJob.execute(request); + } while (request != null); + break; + case SYNC_CJT_SALE_ORDER_DATA_TO_MULTI_TABLE: + retryKey = saleOrderSyncJob.getRetryKey(); + do { + request = (CJTRetryRequest) redisCache.leftPop(retryKey); + saleOrderSyncJob.execute(request); + } while (request != null); + break; + case SYNC_CJT_SALE_DISPATCH_DATA_TO_MULTI_TABLE: + retryKey = saleDispatchSyncJob.getRetryKey(); + do { + request = (CJTRetryRequest) redisCache.leftPop(retryKey); + saleDispatchSyncJob.execute(request); + } while (request != null); + break; + default: + break; + } + } + + public List retryJobList() { + return Arrays.asList( + AppType.SYNC_CJT_MANUFACTURE_ORDER_DATA_TO_MULTI_TABLE, + AppType.SYNC_CJT_PURCHASE_ARRIVAL_ORDER_DATA_TO_MULTI_TABLE, + AppType.SYNC_CJT_PURCHASE_ORDER_DATA_TO_MULTI_TABLE, + AppType.SYNC_CJT_SALE_DELIVERY_DATA_TO_MULTI_TABLE, + AppType.SYNC_CJT_SALE_ORDER_DATA_TO_MULTI_TABLE, + AppType.SYNC_CJT_SALE_DISPATCH_DATA_TO_MULTI_TABLE + ); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CJT/SyncAccountsJobAbstract.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CJT/SyncAccountsJobAbstract.java index 4515b5b..ef6cf34 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CJT/SyncAccountsJobAbstract.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CJT/SyncAccountsJobAbstract.java @@ -65,6 +65,10 @@ public abstract class SyncAccountsJobAbstract { private static final String CJT_APP_KEY = "wwjSb5Vl"; private static final String CJT_APP_SECRET = "C661F71361CC4C5636396480FF08BBA4"; + + public static final String RETRY_KEY = "RETRY_CJT:%s"; + + private static final Long REDUCE_MINUTES = 4L; /** * 畅捷通域名 */ @@ -83,10 +87,50 @@ public abstract class SyncAccountsJobAbstract { */ private static final String CERTIFICATE = "OXYwHSWAc22UPHxfIUM0SSFZwziCLmBfOzBaN+PCNp0SNfV3ewYIaWLJCCrYToCU46x3PJO8t4TXV57bGpbiqClld5DiAkQ3EX1qqxoyaE9J0HAsfnp/PkPurKMQewBHICM2oEPRyLU5GUQjTCucfLvO4xT3DKlelbjBsIkKLqs="; - public void executeSync(String queryFromTime,String queryToTime) { + private CJTJobContext initContext(String queryFromTime,String queryToTime,LocalDateTime now){ CJTJobContext context = new CJTJobContext(); + if (!StringUtils.isEmpty(queryFromTime)){ + queryFromTime = DateUtils.ldt2str(now.minusMinutes(REDUCE_MINUTES), DateUtils.YYYY_MM_DD_HH_MM_SS); + } context.setQueryFromTime(queryFromTime); + if (!StringUtils.isEmpty(queryToTime)){ + queryToTime = DateUtils.ldt2str(now, DateUtils.YYYY_MM_DD_HH_MM_SS); + } context.setQueryToTime(queryToTime); + return context; + } + + public void execute(CJTRetryRequest request) { + if (request == null){ + return; + } + CJTJobContext context = new CJTJobContext(); + context.setQueryFromTime(request.getQueryFrom(REDUCE_MINUTES)); + context.setQueryToTime(request.getQueryTo()); + try { + log.info("===================== {} {} retry strat ======================",this.getClassName(),context.getQueryToTime()); + //初始化飞书信息及相关配置 + initLarkInfo(context); + //重置ticket + resetTicket(context); + //执行分页同步 + sync(context); + } catch (Exception e) { + log.error("{} ", getClassName(), e); + if (request.getCount() == 2){ + larkRobotHelper.sendMessageByBot(ROBOT_GROUP, buildRobotErrorCountMessage(e,request)); + }else { + request.addCount(); + redisCache.rightPush(getRetryKey(),request); + } + }finally { + log.info("===================== {} {} retry end ======================",this.getClassName(),context.getQueryToTime()); + } + } + + public void executeSync(String queryFromTime,String queryToTime) { + LocalDateTime now = LocalDateTime.now(); + CJTJobContext context = initContext(queryFromTime,queryToTime,now); try { log.info("===================== {} strat ======================",this.getClassName()); //初始化飞书信息及相关配置 @@ -96,15 +140,32 @@ public abstract class SyncAccountsJobAbstract { //执行分页同步 sync(context); } catch (Exception e) { - log.error("{} 执行失败" - , getClassName() - , e); - larkRobotHelper.sendMessageByBot(ROBOT_GROUP, buildRobotErrorMessage(e)); + log.error("{} 执行失败", getClassName(), e); + CJTRetryRequest request = new CJTRetryRequest(now,1); + redisCache.rightPush(getRetryKey(),request); }finally { log.info("===================== {} end ======================",this.getClassName()); } } + public String getRetryKey(){ + return String.format(RETRY_KEY, syncLarkAppType().getCode()); + } + + private String buildRobotErrorCountMessage(Exception e,CJTRetryRequest request){ + JSONObject jsonObject = new JSONObject(); + jsonObject.put("同步任务",getClassName()); + jsonObject.put("重试失败",request.getCount()); + jsonObject.put("重试开始时间",request.getQueryFrom(REDUCE_MINUTES)); + jsonObject.put("重试结束时间",request.getQueryTo()); + String errorMessage = e.getMessage(); + if (org.apache.commons.lang3.StringUtils.isNotBlank(errorMessage)){ + errorMessage = errorMessage.replaceAll("\\\\",""); + } + jsonObject.put("异常信息", errorMessage); + return jsonObject.toJSONString(); + } + private String buildRobotErrorMessage(Exception e){ JSONObject jsonObject = new JSONObject(); jsonObject.put("同步任务",getClassName()); @@ -150,7 +211,7 @@ public abstract class SyncAccountsJobAbstract { LocalDateTime now = LocalDateTime.now(); String startTime = context.getQueryFromTime(); if (StringUtils.isEmpty(startTime)){ - startTime = DateUtils.ldt2str(now.minusMinutes(4L), DateUtils.YYYY_MM_DD_HH_MM_SS); + startTime = DateUtils.ldt2str(now.minusMinutes(REDUCE_MINUTES), DateUtils.YYYY_MM_DD_HH_MM_SS); } String endTime = context.getQueryToTime(); if (StringUtils.isEmpty(endTime)){