自定义注解 AOP解耦 应对变化的需求

41 阅读2分钟

前情提要:新增任务、更换人员、安排人员时,会检测冲突,并提供解决冲突的入口,这个需求是后续添加的,为了不变动现有接口的入参,实现代码解耦,我使用了自定义注解 AOP实现需求。

image-20240529150632405.png

注解接口

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HttpServletRequestParamLogger {
    String notes() default "";
​
}

注解Aspect

@Slf4j
@Aspect
@Component
public class HttpServletRequestParamLoggerAspect {
​
    @Autowired
    private WsbJobScheduleDataService wsbJobScheduleDataService;
​
    @Autowired
    private WsbJobSchedulingService wsbJobSchedulingService;
​
    @Autowired
    private WsbJobScheduleParamsService wsbJobScheduleParamsService;
​
    @Autowired
    private RedisUtil redisUtil;
    
    @Autowired
    private WsbFlightJobService wsbFlightJobService;
​
    @Autowired
    private WsoWorkerJobInfoService wsoWorkerJobInfoService;
​
    @Autowired
    private WebSocketService webSocketService;
​
    @AfterReturning(pointcut = "@annotation(com.eunitedtech.automatic.annotation.HttpServletRequestParamLogger)",
        returning = "returnValue")
    public void logHttpServletRequestParams(JoinPoint joinPoint, Object returnValue) {
​
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
​
        CompletableFuture.runAsync(() -> {
            try {
                if (attributes != null) {
                    HttpServletRequest request = attributes.getRequest();
                    
                    // 是否解决冲突
                    String isResolvedStr = request.getParameter("isResolved");
                    
                    // 参与解决的任务Code
                    String jobCodeStr = request.getParameter("jobCodeStr");
                    
                    // 用于获取参与解决的任务ID
                    String redisKey = request.getParameter("redisKey");
​
                    if (ObjUtil.isNotEmpty(isResolvedStr)) {
​
                        // 将字符串转换为 boolean 类型
                        boolean isResolved = Boolean.parseBoolean(isResolvedStr);
​
                        // 用户选择解决冲突
                        if (isResolved) {
                            log.info("解决冲突");
                            if (ObjUtil.isNotEmpty(jobCodeStr)) {
​
                                List<String> jobCodes = StrSplitter.splitTrim(jobCodeStr, ",", true);
                                WsbJobScheduling scheduling = wsbJobSchedulingService.getByJobCode(jobCodes.get(0));
​
                                Set<Object> jobIds = redisUtil.zRange(String.format(RedisConstant.REDIS_SOLVE_CONFLICT_KEY, redisKey), 0, -1);
​
                                if (CollUtil.isNotEmpty(jobIds)) {
                                    WsbJobScheduleParams params = wsbJobScheduleParamsService.getUsedByJobSchedulingId(scheduling.getGuid());
                                    if (ObjectUtil.isNull(params)) {
                                        throw new BusinessException("该任务未配置智能调度参数");
                                    }
​
                                    Long scheduleTime = wsbJobScheduleParamsService.getLastTime(scheduling.getGuid());
​
                                    log.info("redis中存入的taskIds:{}", jobIds);
​
                                    List<String> jobIdList = jobIds.stream().map(Object::toString).collect(Collectors.toList());
​
                                    Long endTime;
                                    WsbFlightJob flightJob = wsbFlightJobService.lambdaQuery().eq(WsbFlightJob::getGuid, jobIdList.get(0)).one();
​
                                    if (ObjUtil.isNull(flightJob)) {
                                        return;
                                    } else {
                                        endTime = flightJob.getPlanEndTime() == null || flightJob.getPlanEndTime() <= scheduleTime ? scheduleTime : flightJob.getPlanEndTime();
                                    }
​
                                    ScheduleReqVO vo = new ScheduleReqVO().setJobSchedulingId(scheduling.getGuid())
                                        .setTaskScheduleParams(params).setIsDropTask(true).setAutoSelect(true)
                                        .setDataSource(ScheduleSourceEnum.MANUAL_SOLVE_CONFLICT.getSource())
                                        .setIsResolveConflict(false).setIsMinAdjust(true)
                                        .setJobCodes(scheduling.getJobList())
                                        .setSolveConflictTasks(new HashSet<>(jobIdList))
                                        .setPriority(TaskPriorityEnum.PRIORITY_TOP.getPriority()).setEndTime(endTime);
​
                                    // 执行解决冲突方法
                                    log.info("执行解决冲突方法");
                                    
                                    // 算法调度,保留解决冲突的任务,变动其他任务
                                    ScheduleVO result = wsbJobScheduleDataService.handleAutoSchedule(vo);
                                    ScheduleStatusVO statusVO = null;
                                    try {
                                        do {
                                        
                                            // 查询调度的结果
                                            statusVO = wsbJobScheduleDataService.queryState(result.getScheduleId());
                                        } while (statusVO == null || (!ObjUtil.equals(ScheduleStateEnum.SUCCESS.getCode(), statusVO.getStatus())
                                            && !ObjUtil.equals(ScheduleStateEnum.FAIL.getCode(), statusVO.getStatus())));
​
                                    } catch (Exception e) {
​
                                    }
                                    WsoWorkerJobInfo workerJobInfo = wsoWorkerJobInfoService.lambdaQuery()
                                        .eq(WsoWorkerJobInfo::getFlightJobId, flightJob.getGuid()).one();
​
                                    if (ObjUtil.isNotEmpty(workerJobInfo)) {
                                        String operationMsg = String.format("将%s航班任务分配到人员%s上", flightJob.getFlightCombinationNo(),
                                                UserHelper.getWorkName(workerJobInfo.getWorkerCode()));
​
                                        statusVO.setOperationMsg(operationMsg);
                                    }
                                    
                                    // 发送webSocket,将执行结果通知给用户
                                    webSocketService.sendMessage(new MessageVO<ScheduleStatusVO>().setBody(statusVO).setType(MessageTypeEnum.resolveConflicts));
​
                                    log.info("解决冲突执行结束");
                                }
​
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Error logging request parameters: {}", e.getMessage());
            }
        }, Executors.newVirtualThreadPerTaskExecutor());
​
    }
​
    @AfterThrowing(pointcut = "@annotation(com.eunitedtech.automatic.annotation.HttpServletRequestParamLogger)",
            throwing = "exception")
    public void handleException(JoinPoint joinPoint, Exception exception) {
​
        log.error("异常信息:" + exception.getMessage());
    }
​
}

注解的调用 (在controller层的方法上调用)

@HttpServletRequestParamLogger(notes = "解决冲突")
@Operation(summary = "新增任务", description = "新增任务")
@PostMapping(value = "/addTask")
public Result addTask(@Validated @NotEmpty(message = "任务信息不能为空") @RequestBody List<AddFlightTaskVO> vos, @CurrentUser LoginUser user) { 
    // 业务接口
}

结语: 随手小记,欢迎评论区讨论