前言
一个设想:通过注解和参数达到定时任务执行前、后、异常情况下的通知
编码逻辑
定义注解
@Target({ElementType.METHOD})
//这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用.
@Retention(RetentionPolicy.RUNTIME)
@Documented
//注解表明制作javadoc时,是否将注解信息加入文档。如果注解在声明时使用了@Documented,则在制作javadoc时注解信息会加入javadoc。
public @interface TaskWeChatCrm {
// 方法名称
String taskName() default "";
//crm通知的code
String crmDictCode() default "";
}
使用
@RestController()
@RequestMapping(value ="task" )
public class TaskController {
@Autowired
ITaskService taskService;
@RequestMapping(value = "/taskTemplate", method = RequestMethod.GET)
public Map<String, Long> taskTemplate() {
return taskService.taskTemplate();
}
}
@Slf4j
@Service
public class TaskServiceImpl implements ITaskService {
@TaskWeChatCrm(taskName = "买家推送-日活推送", crmDictCode = "buyerDauPushTask")
@Override
public Map<String, Long> taskTemplate() {
ExecutorServiceTaskUtil execute = ExecutorServiceTaskUtil.execute("taskTemplate", 4,
(ExecutorServiceTaskUtil executorServiceTaskUtil) -> {
int pageSize = 1000;
Long index = 0L;
List<Long> dataList = List.of();
for (Integer pageNum = 1; pageNum <= 100; pageNum++) {
try {
dataList = getBaseList(index, pageSize);
} catch (Exception e) {
log.error("taskTemplate getBaseList error", e);
}
if (CollectionUtils.isEmpty(dataList)) {
break;
}
executorServiceTaskUtil.taskList(dataList.stream().map(
(cid) -> executorServiceTaskUtil.taskData(() -> taskData(cid), cid)).collect(Collectors.toList()));
//添加定时任务游标
index = dataList.get(dataList.size() - 1);
log.info("定时任务 {} 当前页数 {} 当前页数 {} 推送当前游标 {} listResult {}", "taskTemplate", pageNum,
dataList.size(), index, JSONUtil.toJsonStr(executorServiceTaskUtil.getTaskListCountMap()));
}
});
return execute.getTaskCountMap();
}
}
AOP拦截
@Slf4j
@Aspect
@Component
public class TaskWeChatCrmAopAspect {
private static final String DICT_CODE = "timeTask_weChat_crm";
//环绕通知监听定时任务情况
@Around(value="@annotation(taskWeChatCrm)")
public Object aroundMethod(ProceedingJoinPoint joinPoint,TaskWeChatCrm taskWeChatCrm) {
//前置
List<Integer> staffIds = crmWarnStaffIds(taskWeChatCrm);
crmWarn(staffIds,getBeforeMessage(taskWeChatCrm));
try {
//执行目标方法
Object result = joinPoint.proceed();
//返回
crmWarn(staffIds,getReturnMessage(taskWeChatCrm, result));
return result;
} catch (Throwable e) {
e.printStackTrace();
//异常
crmWarn(staffIds,getExceptionMessage(taskWeChatCrm, e));
throw new BusinessException(e.getMessage());
}
}
//获取不同情况下的文案和日志打印
private String getBeforeMessage(TaskWeChatCrm taskWeChatCrm){
String result = "定时任务开始执行";
if(Objects.nonNull(taskWeChatCrm)){
result = DateUtil.formatCurrentDate() + " " + taskWeChatCrm.taskName() + " 定时任务开始执行";
}
log.info(result);
return result;
}
private String getReturnMessage(TaskWeChatCrm taskWeChatCrm,Object resultObj){
String result = "定时任务执行结束";
if(Objects.nonNull(taskWeChatCrm)){
result = DateUtil.formatCurrentDate() + " " + taskWeChatCrm.taskName() + " 定时任务执行结束 返回值 " + JSONObject.toJSONString(resultObj);
}
log.info(result);
return result;
}
private String getExceptionMessage(TaskWeChatCrm taskWeChatCrm,Throwable e){
String result = "定时任务异常 " + e.getMessage();
if(Objects.nonNull(taskWeChatCrm)){
result = DateUtil.formatCurrentDate() + " " + taskWeChatCrm.taskName() + " 定时任务异常 " + e.getMessage() ;
}
log.error(result,e);
return result;
}
//查询需要发送的人
private List<Integer> crmWarnStaffIds(TaskWeChatCrm taskWeChatCrm){
if(Objects.isNull(taskWeChatCrm)){
return null;
}
String crmDictCode = taskWeChatCrm.crmDictCode();
if(StringUtils.isBlank(crmDictCode)){
return null;
}
return List.of(1,2);
}
@Autowired
WxApi wxApi;
//企业微信通知
private void crmWarn(List<Integer> staffIds,String message){
if(CollectionUtils.isEmpty(staffIds)){
return;
}
if(StringUtils.isBlank(message)){
return;
}
WxCorpTextDTO wxCorpTextDTO = new WxCorpTextDTO();
wxCorpTextDTO.setMessage(message);
wxCorpTextDTO.setStaffIds(staffIds);
Try.of( ()-> wxApi.sendCorpTextMsg(wxCorpTextDTO) )
.onFailure(e -> log.error("wxApi sendCorpTextMsg error",e));
}
}
此程序可以实现对定时任务前后异常情况下的通知。
- 注意点1: aop 只会对接口拦截。
- 注意点2: 使用注解做拦截更加灵活 且注释对象可直接注入。
- 注意点3: 使用环绕通知原因
- 1 异常通知参数无法传低
- 2 可只用查一次发送人
###效果展示