Java中小型项目定时任务解决方案 SpringBoot + Quartz

Quartz核心Api

官网:

  • Quartz是OpenSymphony开源组织在Job scheduling领域的一个开源项目,由Java开发,可以用来执行定时任务,类似于java.util.Timer。Quartz是功能强大的开源作业调度库,几乎可以集成到任何Java应用程序中。Quartz包含许多企业级功能,例如对JTA事务和集群的支持。Quartz作为定时任务组件,既可以单独使用,也可以整合Spring使用。

Quartz Enterprise Job Scheduler

  • Scheduler:任务调度器,使任务和触发器关联,统一进行任务的调度
  • StdSchedulerFactory:任务调度器工厂,可以创建Scheduler对象
  • JobDetail:任务对象
  • JobBuilder:任务构建器,用于创建JobDetail对象
  • Trigger:触发器对象
  • TriggerBuilder:触发器构建器,用于创建Trigger对象
  • JobExecutionContext:任务执行的上下文对象,通过此对象可以获取当前执行任务的相关信息,例如JobDetail、Trigger对象都可以获取到
  • JobDataMap:保存任务实例的状态信息
  • RAMJobStore:此类实现了一个利用RAM作为其存储设备的JobStore,访问速度极快,但是数据却易失,如果需要在程序关闭之前保持真正的持久性,则不应使用此JobStore
  • JobStoreTX:通过JDBC将所有数据保存在数据库中

必要依赖

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.3.0</version>
</dependency>

数据表

qrtz 开头的表自行寻找

CREATE TABLE `schedule_job` (
  `id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'id',
  `bean_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'spring bean名称',
  `params` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '参数',
  `cron_expression` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'cron表达式',
  `status` tinyint unsigned DEFAULT NULL COMMENT '任务状态  0:暂停  1:正常',
  `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
  `business_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '业务id:机构id',
  `creator` bigint DEFAULT NULL COMMENT '创建者',
  `create_date` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updater` bigint DEFAULT NULL COMMENT '更新者',
  `update_date` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_create_date` (`create_date`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=COMPACT COMMENT='定时任务';


CREATE TABLE `schedule_job_log` (
  `id` bigint NOT NULL COMMENT 'id',
  `job_id` bigint NOT NULL COMMENT '任务id',
  `bean_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'spring bean名称',
  `params` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '参数',
  `status` tinyint unsigned NOT NULL COMMENT '任务状态    0:失败    1:成功',
  `error` varchar(5000) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '失败信息',
  `times` int NOT NULL COMMENT '耗时(单位:毫秒)',
  `create_date` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_job_id` (`job_id`) USING BTREE,
  KEY `idx_create_date` (`create_date`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=COMPACT COMMENT='定时任务日志';

配置类

SchedulerFactoryBean 放入容器用于创建 Scheduler

/**
 * 定时任务配置
*/
@Configuration
public class ScheduleConfig {

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setDataSource(dataSource);

        //quartz参数
        Properties prop = new Properties();
        prop.put("org.quartz.scheduler.instanceName", "WitlinkedScheduler");
        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        //线程池配置
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", "20");
        prop.put("org.quartz.threadPool.threadPriority", "5");
        //JobStore配置
        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        //集群配置
        prop.put("org.quartz.jobStore.isClustered", "true");
        prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");

        prop.put("org.quartz.jobStore.misfireThreshold", "12000");
        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");

        //PostgreSQL数据库,需要打开此注释
        //prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");

        factory.setQuartzProperties(prop);

        factory.setSchedulerName("pdScheduler");
        //延时启动
        factory.setStartupDelay(5);
        factory.setApplicationContextSchedulerContextKey("applicationContextKey");
        //可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
        factory.setOverwriteExistingJobs(true);
        //设置自动启动,默认为true
        factory.setAutoStartup(true);

        return factory;
    }
}

必要工具类

提供了一些常用方法,用于操作调度器 Scheduler

/**
 * 定时任务工具类
 *
 * @author
 */
public class ScheduleUtils {
    private final static String JOB_NAME = "TASK_";
    /**
     * 任务调度参数key
     */
    public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";

    /**
     * 获取触发器key
     */
    public static TriggerKey getTriggerKey(String jobId) {
        return TriggerKey.triggerKey(JOB_NAME + jobId);
    }

    /**
     * 获取jobKey
     */
    public static JobKey getJobKey(String jobId) {
        return JobKey.jobKey(JOB_NAME + jobId);
    }

    /**
     * 获取表达式触发器
     */
    public static CronTrigger getCronTrigger(Scheduler scheduler, String jobId) {
        try {
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            throw new PdException("getCronTrigger ERROR", e);
        }
    }

    /**
     * 创建定时任务
     */
    public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
        try {
            //构建job信息
            JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getId())).build();

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build();

            //放入参数,运行时的方法可以获取
            jobDetail.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob);

            scheduler.scheduleJob(jobDetail, trigger);

            //暂停任务
            if (scheduleJob.getStatus() == ScheduleStatus.PAUSE.getValue()) {
                pauseJob(scheduler, scheduleJob.getId());
            }
        } catch (SchedulerException e) {
            throw new PdException("CREATE ERROR", e);
        }
    }

    /**
     * 更新定时任务
     */
    public static void updateScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
        try {
            TriggerKey triggerKey = getTriggerKey(scheduleJob.getId());

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId());

            //按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

            //参数
            trigger.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob);

            scheduler.rescheduleJob(triggerKey, trigger);

            //暂停任务
            if (scheduleJob.getStatus() == ScheduleStatus.PAUSE.getValue()) {
                pauseJob(scheduler, scheduleJob.getId());
            }

        } catch (SchedulerException e) {
            throw new PdException("UPDATE ERROR", e);
        }
    }

    /**
     * 立即执行任务
     */
    public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
        try {
            //参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(JOB_PARAM_KEY, scheduleJob);

            scheduler.triggerJob(getJobKey(scheduleJob.getId()), dataMap);
        } catch (SchedulerException e) {
            throw new PdException("RUN ERROR", e);
        }
    }

    /**
     * 暂停任务
     */
    public static void pauseJob(Scheduler scheduler, String jobId) {
        try {
            scheduler.pauseJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new PdException("PAUSE ERROR", e);
        }
    }

    /**
     * 恢复任务
     */
    public static void resumeJob(Scheduler scheduler, String jobId) {
        try {
            scheduler.resumeJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new PdException("RESUME ERROR", e);
        }
    }

    /**
     * 删除定时任务
     */
    public static void deleteScheduleJob(Scheduler scheduler, String jobId) {
        try {
            scheduler.deleteJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new PdException("DELETE ERROR", e);
        }
    }
}

调度器数据预热

用于项目启动时从定时任务表中查询定时任务信息

@Slf4j
@Component
public class DispatchCommandLineRunner implements CommandLineRunner {

    @Autowired
    private Scheduler scheduler;

    @Autowired
    private IScheduleJobService scheduleJobService;

    @Override
    public void run(String... args) throws Exception {
        log.info("定时任务初始化");
        List<ScheduleJobEntity> scheduleJobEntities = scheduleJobService.list();
        for (ScheduleJobEntity scheduleJob : scheduleJobEntities) {
            CronTrigger trigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId());
            if(trigger == null){
                ScheduleUtils.createScheduleJob(scheduler,scheduleJob);
            } else {
                ScheduleUtils.updateScheduleJob(scheduler,scheduleJob);
            }

        }
    }
}

定时任务执行入口

通过继承 QuartzJobBean 重写 executeInternal() 当有定时任务被触发时就会对此函数进行回调,JobExecutionContext 中可以获取任务所需参数。

/**
 * 定时任务
 */
public class ScheduleJob extends QuartzJobBean {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    protected void executeInternal(JobExecutionContext context) {
        ScheduleJobEntity scheduleJob = (ScheduleJobEntity)context.getMergedJobDataMap().get(ScheduleUtils.JOB_PARAM_KEY);


        ScheduleJobLogEntity scheduleJobLog = new ScheduleJobLogEntity();
        scheduleJobLog.setBeanName(scheduleJob.getBeanName());
        scheduleJobLog.setId(IdUtils.get());
        scheduleJobLog.setJobId(scheduleJob.getId());
        scheduleJobLog.setParams(scheduleJob.getParams());
        scheduleJobLog.setCreateDate(new Date());
        System.out.println(new Date() + "定时任务开始执行...,定时任务id:" + scheduleJob.getId());

        //任务开始时间
        long startTime = System.currentTimeMillis();
        try {
            Object bean = SpringContextUtils.getBean(scheduleJob.getBeanName());
            Method method = bean.getClass().getDeclaredMethod("run", String.class, String.class, String.class, String.class);
            method.invoke(bean,scheduleJob.getBusinessId(),scheduleJob.getParams(),scheduleJob.getId(),scheduleJobLog.getId());

            long times = System.currentTimeMillis() - startTime;
            scheduleJobLog.setTimes((int) times);
            scheduleJobLog.setStatus(1);

            logger.info("定时任务执行耗时: {}",times);

        } catch (Exception e) {
            logger.error("任务执行失败,异常信息:{}", e.getMessage());
            //任务执行总时长
            long times = System.currentTimeMillis() - startTime;
            scheduleJobLog.setTimes((int) times);
            //任务状态
            scheduleJobLog.setStatus(0);
            scheduleJobLog.setError(ExceptionUtils.getErrorStackTrace(e));
        } finally {
            IScheduleJobLogService jobLogService = SpringContextUtils.getBean(IScheduleJobLogService.class);
            jobLogService.save(scheduleJobLog);
        }
    }
}
/**
 * Spring Context 工具类
 */
@Component
public class SpringContextUtils implements ApplicationContextAware {
    public static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
    throws BeansException {
        SpringContextUtils.applicationContext = applicationContext;
    }

    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    public static <T> T getBean(Class<T> cls) {
        return applicationContext.getBean(cls);
    }

    public static <T> T getBean(String name, Class<T> requiredType) {
        return applicationContext.getBean(name, requiredType);
    }

    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    public static Class<? extends Object> getType(String name) {
        return applicationContext.getType(name);
    }

}

任务处理类

ScheduleJob.executeInternal() 触发后,会通过反射调用 DispatchTask.run() , 对定时任务的业务处理以此类为入口,逻辑在 ITask为前缀的 service 中。

@Slf4j
@Component("dispatchTask")
public class DispatchTask {

    @Autowired
    private ITaskOrderClassifyService orderClassifyService;
    @Autowired
    private ITaskTripsSchedulingService truckSchedulingService;
    @Autowired
    private ITaskRoutePlanningService routePlanningService;
    @Autowired
    private IBusinessOperationService businessOperationService;

    @SneakyThrows
    public void run(String businessId, String params, String jobId, String logId) {
        String LOGID = businessId + System.currentTimeMillis();
        log.info("[{}]TestTask定时任务正在执行,参数为:{},{},{},{}", LOGID, businessId, params, jobId, logId);

        // 订单分类
        List<OrderClassifyGroupDTO> orderClassifys = orderClassifyService.execute(businessId, jobId, logId);
        log.info("[{}] 订单分类完成:{}", LOGID, orderClassifys);

        // 路线规划
        List<OrderLineSimpleDTO> orderLineSimpleDTOS = routePlanningService.execute(orderClassifys, businessId, jobId, logId, params);
        log.info("[{}] 路线规划完成:{}", LOGID, orderLineSimpleDTOS);

        //  创建运输任务; 关联运单
        Map<String, TaskTransportDTO> transportTaskMap = businessOperationService.createTransportOrderTask(orderLineSimpleDTOS);
        log.info("[{}] 运输任务创建完成:{}", LOGID, transportTaskMap);

        // 车次 车辆 司机
        List<OrderLineTripsTruckDriverDTO> orderLineTripsTruckDriverDTOS = truckSchedulingService.execute(orderLineSimpleDTOS, businessId, jobId, logId, params);
        log.info("[{}] 规划车次车辆司机:{}", LOGID, orderLineTripsTruckDriverDTOS);

        //  完善运输任务信息; 创建司机作业任务;
        businessOperationService.updateTransportTask(orderLineTripsTruckDriverDTOS, transportTaskMap);
        log.info("[{}] 安排车次车辆司机,当前机构调度结束!!!!!", LOGID);
    }

}

处理定时任务service部分代码

用于生成订单分类数据

/**
 * 订单分类实现类
 */
@Service
@Slf4j
public class TaskOrderClassifyServiceImpl implements ITaskOrderClassifyService {
    @Autowired
    private OrderFeign orderFeign;
    @Autowired
    private AgencyScopeFeign agencyScopeFeign;
    @Autowired
    private IOrderClassifyService orderClassifyService;
    @Autowired
    private IOrderClassifyOrderService orderClassifyOrderService;


    /**
     * 订单分类核心逻辑
     * @param agencyId 机构id(网点或者转运中心的id)
     * @param jobId 定时任务id
     * @param logId 日志id
     * @return
     */
    @Override
    public List<OrderClassifyGroupDTO> execute(String agencyId, String jobId, String logId) {

        //存放当前机构的订单
        List<OrderClassifyDTO> orderClassifyDTOList = new ArrayList<>();
        //存放分类后的订单信息
        List<OrderClassifyGroupDTO> orderClassifyGroupDTOList = new ArrayList<>();

        //将新订单存入集合中
        orderClassifyDTOList.addAll(buildNewOrder(agencyId));

        //将中转订单放入集合中
        orderClassifyDTOList.addAll(buildTransferOrder(agencyId));

        //对分类后的订单进行分组
        Map<String, List<OrderClassifyDTO>> listMap = orderClassifyDTOList.stream().collect(Collectors.groupingBy(OrderClassifyDTO::groupBy));
        OrderClassifyGroupDTO.OrderClassifyGroupDTOBuilder orderClassifyGroupDTOBuilder = OrderClassifyGroupDTO.builder();
        //生成分类结果数据
        listMap.forEach((k,v)->{
            orderClassifyGroupDTOBuilder.key(k);
            orderClassifyGroupDTOBuilder.orders(v.stream().map(OrderClassifyDTO::getOrder).collect(Collectors.toList()));
            orderClassifyGroupDTOList.add(orderClassifyGroupDTOBuilder.build());
        });
        //保存分类结果数据
        saveRecord(orderClassifyGroupDTOList,jobId,logId);
        return orderClassifyGroupDTOList;
    }

    /**
     * 保存订单分类结果
     * @param orderClassifyGroupDTOS
     * @param jobId
     * @param logId
     */
    private void saveRecord(List<OrderClassifyGroupDTO> orderClassifyGroupDTOS, String jobId, String logId) {
        orderClassifyGroupDTOS.forEach(item -> {
            if(item.isNew()){
                log.info("新订单 保存分组信息");
                OrderClassifyEntity classifyEntity = new OrderClassifyEntity();
                classifyEntity.setClassify(item.getKey());
                classifyEntity.setTotal(item.getOrders().size());
                if (!classifyEntity.getClassify().equals("ERROR")) {
                    classifyEntity.setStartAgencyId(item.getStartAgencyId());
                    classifyEntity.setEndAgencyId(item.getEndAgencyId());
                }
                classifyEntity.setId(IdUtils.get());
                classifyEntity.setJobId(jobId);
                classifyEntity.setJobLogId(logId);
                classifyEntity.setCreateDate(new Date());

                List<OrderClassifyOrderEntity> classifyOrderEntities = item.getOrders().stream().map((order) -> {
                    OrderClassifyOrderEntity orderClassifyOrderEntity = new OrderClassifyOrderEntity();
                    orderClassifyOrderEntity.setOrderId(order.getId());
                    orderClassifyOrderEntity.setOrderClassifyId(classifyEntity.getId());
                    orderClassifyOrderEntity.setId(IdUtils.get());
                    return orderClassifyOrderEntity;
                }).collect(Collectors.toList());

                orderClassifyOrderService.saveBatch(classifyOrderEntities);
                orderClassifyService.save(classifyEntity);

                item.setId(classifyEntity.getId());
            } else {
                log.info("中转订单,查询分组信息");
                //中转订单
                List<String> orderIdList = item.getOrders().stream().map(Order::getId).collect(Collectors.toList());
                LambdaQueryWrapper<OrderClassifyOrderEntity> orderEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
                orderEntityLambdaQueryWrapper.in(OrderClassifyOrderEntity::getOrderId,orderIdList);

                List<OrderClassifyOrderEntity> orderEntities = orderClassifyOrderService.list(orderEntityLambdaQueryWrapper);
                Set<String> classifyEntitySet = orderEntities.stream().map(OrderClassifyOrderEntity::getOrderClassifyId).collect(Collectors.toSet());
                if(ObjectUtil.isEmpty(classifyEntitySet)){
                    exceptionHappend("查询订单异常");
                }

                item.setId(classifyEntitySet.iterator().next());

            }


        });
    }

    /**
     *查询中转订单
     * @param agencyId
     * @return
     */
    private List<OrderClassifyDTO> buildTransferOrder(String agencyId) {
        //1.获取中转订单信息
        OrderSearchDTO orderSearchDTO = new OrderSearchDTO();
        orderSearchDTO.setStatus(OrderStatus.IN_TRANSIT.getCode());
        orderSearchDTO.setCurrentAgencyId(agencyId);
        List<Order> orders = orderFeign.list(orderSearchDTO);
        //2.封装订单分类数据
        OrderClassifyDTO.OrderClassifyDTOBuilder builder = OrderClassifyDTO.builder();
        List<OrderClassifyDTO> orderClassifyDTOS = orders.stream().map((item) -> {
            builder.startAgencyId(getStartAgencyId(item));
            builder.endAgencyId(getEndAgencyId(item));
            builder.order(item);
            builder.orderType(item.getOrderType());
            return builder.build();
        }).collect(Collectors.toList());

        return orderClassifyDTOS;
    }

    /**
     * 根据订单获得起始机构id
     * @param order
     * @return
     */
    private String getStartAgencyId(Order order) {
        //1.获取地址描述
        String address = senderFullAddress(order);
        if (StringUtils.isBlank(address)) {
            exceptionHappend("下单时发货地址不能为空");
        }
        //2.获取坐标
        String location = EntCoordSyncJob.getCoordinate(address);
        log.info("订单发货地址和坐标-->" + address + "--" + location);
        if (StringUtils.isBlank(location)) {
            exceptionHappend("下单时发货地址不能为空");
        }
        //3.获取区域信息
        Map position = EntCoordSyncJob.getLocationByPosition(location);
        if (ObjectUtils.isEmpty(position)) {
            exceptionHappend("根据地图获取区域信息为空");
        }

        //4.查询区域信息
        String adcode = position.getOrDefault("adcode", "").toString();
        R<Area> r = areaApi.getByCode(adcode + "000000");
        if(!r.getIsSuccess()){
            Result.error(r.getMsg());
        }
        //5.查询网点信息
        Area area = r.getData();
        if (area == null) {
            exceptionHappend("区域编码:" + adcode + "区域信息未从库中获取到");
        }
        String areaID = area.getId().toString();
        if (!order.getSenderCountyId().equals(areaID)) {
            exceptionHappend("参数中发货区域id和坐标计算出真实区域id不同,数据不合法");
        }

        List<AgencyScopeDto> agencyScope = agencyScopeFeign.findAllAgencyScope(areaID, null, null, null);
        if(agencyScope == null || agencyScope.size() == 0){
            exceptionHappend("根据区域无法从机构范围获取网点信息列表");
        }
        Result result = this.caculate(agencyScope, location);
        return result.get("agencyId").toString();
    }

    /**
     * 获得发件人详细地址信息
     * @param order
     * @return
     */
    @SneakyThrows
    private String senderFullAddress(Order order) {
        Long province = Long.valueOf(order.getSenderProvinceId());
        Long city = Long.valueOf(order.getSenderCityId());
        Long county = Long.valueOf(order.getSenderCountyId());

        Set<Long> set = new HashSet<>();
        set.add(province);
        set.add(city);
        set.add(county);


        CompletableFuture<Map<Long, Area>> completableFuture = PdCompletableFuture.areaMapFuture(areaApi, null, set);
        Map<Long, Area> areaMap = completableFuture.get();
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(areaMap.get(province).getName());
        stringBuffer.append(areaMap.get(city).getName());
        stringBuffer.append(areaMap.get(county).getName());
        return stringBuffer.toString();
    }

    /**
     * 获取指定机构下的新订单
     * @param agencyId
     * @return
     */
    private List<OrderClassifyDTO> buildNewOrder(String agencyId) {
        //1.查询订单信息
        OrderSearchDTO orderSearchDTO = new OrderSearchDTO();
        //订单状态为网点入库
        orderSearchDTO.setStatus(OrderStatus.OUTLETS_WAREHOUSE.getCode());
        orderSearchDTO.setCurrentAgencyId(agencyId);
        List<Order> orders = orderFeign.list(orderSearchDTO);
        log.info("查询[网点入库]状态订单:{} 条", orders.size());
        //2.组装订单分类信息
        OrderClassifyDTO.OrderClassifyDTOBuilder builder = OrderClassifyDTO.builder();
        List<OrderClassifyDTO> classifyDTOS = orders.stream().map((item) -> {
            builder.order(item);
            builder.startAgencyId(agencyId);
            builder.endAgencyId(getEndAgencyId(item));
            builder.orderType(item.getOrderType());
            return builder.build();
        }).collect(Collectors.toList());
        log.info("订单分类:首次发出订单{}条", classifyDTOS.size());
        return classifyDTOS;
    }

    /**
     * 抛出异常
     * @param msg
     */
    private void exceptionHappend(String msg){
        try {
            throw new Exception(msg);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 获取目的地网点id
     * @param order
     * @return
     */
    private String getEndAgencyId(Order order) {
        //1.获取地址信息
        String address = receiverFullAddress(order);
        if(StringUtils.isEmpty(address)){
            exceptionHappend("下单时收货地址不能为空");
        }
        //2.获取地址
        String location = EntCoordSyncJob.getCoordinate(address);
        log.info("订单收货地址和坐标-->" + address + "--" + location);
        if(StringUtils.isEmpty(location)){
            exceptionHappend("下单时收货地址不能正确");
        }
        //3.获取区域信息
        Map map = EntCoordSyncJob.getLocationByPosition(location);
        if(ObjectUtil.isEmpty(map)){
            exceptionHappend("根据地图获取区域信息为空");
        }
        //4.获取区域编码
        String adcode = map.getOrDefault("adcode", "").toString();
        R<Area> r = areaApi.getByCode(adcode + "000000");
        Area area = r.getData();
        if(area == null){
            exceptionHappend("区域编码:" + adcode + "区域信息未从库中获取到");
        }
        //5.校验区域编码是否一致
        String areaId = String.valueOf(area.getId());
        if(!order.getReceiverAddressId().equals(areaId)){
            exceptionHappend("区域编码:" + adcode + "区域信息未从库中获取到");
        }
        //6.获取网点信息列表
        List<AgencyScopeDto> agencyScope = agencyScopeFeign.findAllAgencyScope(areaId, null, null, null);
        if(agencyScope == null || agencyScope.size() == 0){
            exceptionHappend("根据区域无法从机构范围获取网点信息列表");
        }

        //7.计算距离最近的网点
        Result res = this.caculate(agencyScope, location);
        return res.get("agencyId").toString();
    }

    /**
     * 从给定网点中查找覆盖指定点的网点
     * @param agencyScopes
     * @param location
     * @return
     */
    private Result caculate(List<AgencyScopeDto> agencyScopes,String location) {
        try {
            Map agencyMap = Maps.newHashMap();
            for (AgencyScopeDto agencyScopeDto : agencyScopes) {
                List<List<Map>> points = agencyScopeDto.getMutiPoints();
                for (List<Map> point : points) {
                        //注释掉的代码有bug,虽然说距离最近,但可能这个网点不负责这片区域
//                        String p = getPoint(map);
//                        Double distance = EntCoordSyncJob.getDistance(location, p);
//                        Double preDistance = (Double) agencyMap.get(agencyScopeDto.getAgencyId());
//                        if(preDistance == null || distance < preDistance){
//                            agencyMap.put(agencyScopeDto.getAgencyId(),distance);
//                        }
                        String[] originArray = location.split(",");
                    boolean inScope = EntCoordSyncJob.isInScope(point, Double.parseDouble(originArray[0]), Double.parseDouble(originArray[1]));
                    if(inScope){
                        return  Result.ok().put("agencyId",agencyScopeDto.getAgencyId());
                    }
                }
            }
//            List<Map.Entry<String,Double>> list= new ArrayList<>(agencyMap.entrySet());
//            list.sort(Comparator.comparingDouble(Map.Entry::getValue));

//            return Result.ok().put("agencyId",list.get(0).getKey());
        } catch (Exception e) {
            log.error("获取最短距离网点失败,error:{}",e.getMessage());
            return Result.error(5000, "获取最短距离网点失败");
        }
        return Result.error(5000, "获取所属网点失败");
    }

    /**
     * 获取坐标值
     * @param map
     * @return
     */
    private String getPoint(Map map){
        String lng = map.get("lng").toString();
        String lat = map.get("lat").toString();
        return lng + "," + lat;
    }

    public static void main(String[] args) {

            Path2D.Double generalPath = new Path2D.Double();
            //绘制区域范围
            generalPath.moveTo(0,0);
            generalPath.lineTo(1,0);
            generalPath.lineTo(1,1);
            generalPath.lineTo(0,1);
            generalPath.lineTo(0,0);
            generalPath.closePath();
            //判断指定点是否在上面的区域范围内
            boolean contains = generalPath.contains(0.1, 0.1);
            System.out.println(contains);
    }

    @Autowired
    private AreaApi areaApi;

    /**
     * 根据订单获取对应的完整收件人地址信息
     * @param order
     * @return
     */
    @SneakyThrows
    private String receiverFullAddress(Order order) {
        Long province = Long.valueOf(order.getReceiverProvinceId());
        Long city = Long.valueOf(order.getReceiverCityId());
        Long county = Long.valueOf(order.getReceiverCountyId());

        Set<Long> set = new HashSet<>();
        set.add(province);
        set.add(city);
        set.add(county);


        CompletableFuture<Map<Long, Area>> mapCompletableFuture = PdCompletableFuture.areaMapFuture(areaApi, null, set);
        Map<Long, Area> longAreaMap = mapCompletableFuture.get();
        StringBuffer buffer = new StringBuffer();
        buffer.append(longAreaMap.get(province).getName());
        buffer.append(longAreaMap.get(city).getName());
        buffer.append(longAreaMap.get(county).getName());
        buffer.append(order.getReceiverAddress());
        return buffer.toString();
    }
}

操作Quartz可视化接口

controller

/**
 * 定时任务
 *
 * @author
 */
@RestController
@RequestMapping("/schedule")
@Api(tags = "定时任务")
public class ScheduleJobController {
    private static final List<Integer> ORG_TYPE = ImmutableList.of(OrgType.BUSINESS_HALL.getType(), OrgType.TOP_TRANSFER_CENTER.getType(), OrgType.TOP_TRANSFER_CENTER.getType()).asList();

    @Autowired
    private IScheduleJobService scheduleJobService;

    @Autowired
    private OrgApi orgApi;


    @GetMapping("page")
    @ApiOperation("分页")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "pageSize", paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "name", value = "name", paramType = "query", dataType = "String")
    })
    public R<List<OrgJobTreeDTO>> page(@ApiIgnore @RequestParam Map<String, Object> params) {

        List<OrgJobTreeDTO> tree = scheduleJobService.page(params);

        return R.success(tree);
    }

    @GetMapping("{id}")
    @ApiOperation("信息")
    public ScheduleJobDTO info(@PathVariable("id") String id) {
        ScheduleJobDTO schedule = scheduleJobService.get(id);
        return schedule;
    }

    @GetMapping("dispatch/{id}")
    @ApiOperation("调度信息")
    public Result dispatchInfo(@PathVariable("id") String id) {

        LambdaQueryWrapper<ScheduleJobEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ScheduleJobEntity::getBusinessId, id);
        ScheduleJobEntity scheduleJobEntity = scheduleJobService.getOne(wrapper);
        if (scheduleJobEntity == null) {
            return Result.error(404, "机构没有任务信息");
        }

        ScheduleJobDTO schedule = new ScheduleJobDTO();
        BeanUtils.copyProperties(scheduleJobEntity, schedule);
        return Result.ok().put("data", schedule);
    }

    @PostMapping
    @ApiOperation("保存")
    public Result save(@RequestBody ScheduleJobDTO dto) {

        scheduleJobService.save(dto);

        return Result.ok();
    }

    @PostMapping("dispatch")
    @ApiOperation("保存或修改")
    public Result dispatch(@RequestBody ScheduleJobDTO dto) {

        String businessId = dto.getBusinessId();

        R<Org> orgR = orgApi.get(Long.valueOf(businessId));

        Integer orgType = orgR.getData().getOrgType();
        if (!ORG_TYPE.contains(orgType)) {
            return Result.error(400, "无法给转运中心以上的机构增加调度任务");
        }

        if (StringUtils.isNotBlank(dto.getId())) {
            dto.setUpdateDate(new Date());
            scheduleJobService.update(dto);
            return Result.ok();
        } else {
            dto.setId(IdUtils.get());
            dto.setBeanName("dispatchTask");
            dto.setCreateDate(new Date());
            scheduleJobService.save(dto);

            return Result.ok();
        }
    }

    @PutMapping
    @ApiOperation("修改")
    public Result update(@RequestBody ScheduleJobDTO dto) {

        scheduleJobService.update(dto);

        return Result.ok();
    }

    @DeleteMapping
    @ApiOperation("删除")
    public Result delete(@RequestBody String[] ids) {
        scheduleJobService.deleteBatch(ids);

        return Result.ok();
    }

    @PutMapping("/run/{id}")
    @ApiOperation("立即执行")
    public Result run(@PathVariable String id) {
        scheduleJobService.run(new String[]{id});

        return Result.ok();
    }

    @PutMapping("/run")
    @ApiOperation("立即执行")
    public Result run(@RequestBody String[] ids) {
        scheduleJobService.run(ids);

        return Result.ok();
    }

    @PutMapping("/pause/{id}")
    @ApiOperation("暂停")
    public Result pause(@PathVariable String id) {
        scheduleJobService.pause(new String[]{id});

        return Result.ok();
    }

    @PutMapping("/pause")
    @ApiOperation("暂停")
    public Result pause(@RequestBody String[] ids) {
        scheduleJobService.pause(ids);

        return Result.ok();
    }

    @PutMapping("/resume/{id}")
    @ApiOperation("恢复")
    public Result resume(@PathVariable String id) {
        scheduleJobService.resume(new String[]{id});

        return Result.ok();
    }

    @PutMapping("/resume")
    @ApiOperation("恢复")
    public Result resume(@RequestBody String[] ids) {
        scheduleJobService.resume(ids);

        return Result.ok();
    }

}

service

@Service
public class ScheduleJobServiceImpl extends ServiceImpl<ScheduleJobMapper, ScheduleJobEntity> implements IScheduleJobService {
    @Autowired
    private Scheduler scheduler;
    @Autowired
    private OrgApi orgApi;

    @Override
    public List<OrgJobTreeDTO> page(Map<String, Object> params) {
        R<List<OrgTreeDTO>> orgTree = orgApi.tree("", true);
        List<OrgTreeDTO> orgTreeDTOS = orgTree.getData();
        List<ScheduleJobEntity> schedulerEntity = baseMapper.selectList(null);
        Map<String, ScheduleJobEntity> schedulerMap = schedulerEntity.stream().collect(Collectors.toMap(ScheduleJobEntity::getBusinessId, item -> item));

        List<OrgJobTreeDTO> orgJobTreeDTOS = new ArrayList<>();
        mergeOrgJob(orgJobTreeDTOS, orgTreeDTOS, schedulerMap);

        return orgJobTreeDTOS;
    }

    private void mergeOrgJob(List<OrgJobTreeDTO> orgJobTreeDTOS, List<OrgTreeDTO> orgTreeDTOS, Map<String, ScheduleJobEntity> schedulerMap) {
        orgTreeDTOS.forEach(item -> {
            OrgJobTreeDTO orgJobTreeDTO = new OrgJobTreeDTO();
            BeanUtils.copyProperties(item, orgJobTreeDTO);
            Long orgId = orgJobTreeDTO.getId();
            ScheduleJobEntity schedulerDto = schedulerMap.get(orgId.toString());
            if (schedulerDto != null) {
                orgJobTreeDTO.setCronExpression(schedulerDto.getCronExpression());
                orgJobTreeDTO.setJobId(schedulerDto.getId());
                orgJobTreeDTO.setParams(schedulerDto.getParams());
                orgJobTreeDTO.setRemark(schedulerDto.getRemark());
                orgJobTreeDTO.setJobStatus(schedulerDto.getStatus());
            }
            orgJobTreeDTOS.add(orgJobTreeDTO);
        });
    }


    @Override
    public ScheduleJobDTO get(String id) {
        ScheduleJobEntity entity = baseMapper.selectById(id);
        return ConvertUtils.sourceToTarget(entity, ScheduleJobDTO.class);
    }

    @Override
    public ScheduleJobDTO getByOrgId(String id) {
        LambdaQueryWrapper<ScheduleJobEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ScheduleJobEntity::getBusinessId, id);
        ScheduleJobEntity entity = baseMapper.selectOne(wrapper);
        return ConvertUtils.sourceToTarget(entity, ScheduleJobDTO.class);
    }


    private QueryWrapper<ScheduleJobEntity> getWrapper(Map<String, Object> params) {
        String beanName = (String) params.get("beanName");

        QueryWrapper<ScheduleJobEntity> wrapper = new QueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(beanName), "bean_name", beanName);

        return wrapper;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(ScheduleJobDTO dto) {
        ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class);

        entity.setStatus(ScheduleStatus.NORMAL.getValue());
        baseMapper.insert(entity);

        ScheduleUtils.createScheduleJob(scheduler, entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(ScheduleJobDTO dto) {
        ScheduleJobEntity entity = this.getById(dto.getId());

        BeanUtils.copyProperties(dto, entity, ConvertUtils.getNullPropertyNames(dto));

        ScheduleUtils.updateScheduleJob(scheduler, entity);

        this.updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(String[] ids) {
        for (String id : ids) {
            ScheduleUtils.deleteScheduleJob(scheduler, id);
        }

        //删除数据
        baseMapper.deleteBatchIds(Arrays.asList(ids));
    }

    @Override
    public int updateBatch(String[] ids, int status) {
        Map<String, Object> map = new HashMap<>(2);
        map.put("ids", ids);
        map.put("status", status);

        ScheduleJobEntity scheduleJobEntity = new ScheduleJobEntity();
        scheduleJobEntity.setStatus(status);
        LambdaQueryWrapper<ScheduleJobEntity> wrapper = new LambdaQueryWrapper();
        wrapper.in(ScheduleJobEntity::getId, ids);
        return baseMapper.update(scheduleJobEntity, wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void run(String[] ids) {
        for (String id : ids) {
            ScheduleUtils.run(scheduler, baseMapper.selectById(id));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pause(String[] ids) {
        for (String id : ids) {
            ScheduleUtils.pauseJob(scheduler, id);
        }

        updateBatch(ids, ScheduleStatus.PAUSE.getValue());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resume(String[] ids) {
        for (String id : ids) {
            ScheduleUtils.resumeJob(scheduler, id);
        }

        updateBatch(ids, ScheduleStatus.NORMAL.getValue());
    }

}

日志接口

/**
 * 定时任务日志
 *
 * @author
 */
@Slf4j
@RestController
@RequestMapping("/scheduleLog")
@Api(tags = "定时任务日志")
public class ScheduleJobLogController {
    @Autowired
    private IScheduleJobLogService scheduleJobLogService;
    @Autowired
    private IScheduleJobService scheduleJobService;

    @Autowired
    private OrgApi orgApi;
    @Autowired
    private IOrderClassifyService orderClassifyService;
    @Autowired
    private IOrderClassifyAttachService orderClassifyAttachService;
    @Autowired
    private ICacheLineUseService cacheLineUseService;
    @Autowired
    private ICacheLineService cacheLineService;
    @Autowired
    private ICacheLineDetailService cacheLineDetailService;

    @Autowired
    private TransportLineFeign transportLineFeign;
    @Autowired
    private TransportTripsFeign transportTripsFeign;
    @Autowired
    private UserApi userApi;
    @Autowired
    private TruckFeign truckFeign;


    @GetMapping("page")
    @ApiOperation("分页")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "jobId", value = "jobId", paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "orgId", value = "orgId", paramType = "query", dataType = "String")
    })
    public PageResponse<ScheduleJobLogDTO> page(@ApiIgnore @RequestParam Map<String, Object> params) {
        if (!params.containsKey("page")) {
            params.put("page", 1);
        }
        if (!params.containsKey("pageSize")) {
            params.put("pageSize", 10);
        }
        if (!params.containsKey("jobId")) {
            String orgId = params.get("orgId").toString();
            ScheduleJobDTO scheduleJobDto = scheduleJobService.getByOrgId(orgId);
            params.put("jobId", scheduleJobDto.getId());
        }

        IPage<ScheduleJobLogEntity> page = scheduleJobLogService.page(params);

        List<ScheduleJobLogDTO> newItems = page.getRecords().stream().map(item -> {
            ScheduleJobLogDTO scheduleJobLogDTO = new ScheduleJobLogDTO();
            BeanUtils.copyProperties(item, scheduleJobLogDTO);
            return scheduleJobLogDTO;
        }).collect(Collectors.toList());

        return PageResponse.<ScheduleJobLogDTO>builder()
                .page(Integer.valueOf(String.valueOf(page.getCurrent())))
                .pagesize(Integer.valueOf(String.valueOf(page.getSize())))
                .pages(page.getPages())
                .counts(page.getTotal())
                .items(newItems)
                .build();
    }

    @SneakyThrows
    @GetMapping("{id}")
    @ApiOperation("信息")
    public ScheduleJobLogDTO info(@PathVariable("id") Long id) {
        ScheduleJobLogDTO logDto = scheduleJobLogService.get(id);
        // 组合 订单分组等信息
        List<OrderClassifyEntity> orderClassifyEntities = orderClassifyService.findByJobLogId(logDto.getId());
        Set<String> agencySet = new HashSet<>();
        agencySet.addAll(orderClassifyEntities.stream().map(item -> item.getStartAgencyId()).collect(Collectors.toSet()));
        agencySet.addAll(orderClassifyEntities.stream().map(item -> item.getEndAgencyId()).collect(Collectors.toSet()));

        CompletableFuture<Map<Long, Org>> agnecyMapFuture = PdCompletableFuture.agencyMapFuture(orgApi, null, agencySet, null);
        Map<Long, Org> agencyMap = agnecyMapFuture.get();

        List<OrderClassifyLogDTO> orderClassifyLogDTOS = orderClassifyEntities.stream().map(item -> {
            OrderClassifyLogDTO orderClassifyLogDTO = new OrderClassifyLogDTO();
            BeanUtils.copyProperties(item, orderClassifyLogDTO);
            if (StringUtils.isNotEmpty(orderClassifyLogDTO.getStartAgencyId())) {
                orderClassifyLogDTO.setStartAgency(agencyMap.get(Long.parseLong(orderClassifyLogDTO.getStartAgencyId())).getName());
            }
            if (StringUtils.isNotBlank(orderClassifyLogDTO.getEndAgencyId())) {
                orderClassifyLogDTO.setEndAgency(agencyMap.get(Long.parseLong(orderClassifyLogDTO.getEndAgencyId())).getName());
            }

            buildTransportLine(item, orderClassifyLogDTO);

            buildTripsTruckDriver(item, orderClassifyLogDTO);

            return orderClassifyLogDTO;
        }).collect(Collectors.toList());

        logDto.setOrderClassifyLogDTOS(orderClassifyLogDTOS);
        return logDto;
    }

    /**
     * 获取当前使用的车次 车辆 司机信息
     *
     * @param item
     * @param orderClassifyLogDTO
     */
    @SneakyThrows
    private void buildTripsTruckDriver(OrderClassifyEntity item, OrderClassifyLogDTO orderClassifyLogDTO) {
        LambdaQueryWrapper<OrderClassifyAttachEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderClassifyAttachEntity::getOrderClassifyId, item.getId());
        wrapper.orderByAsc(OrderClassifyAttachEntity::getCreateDate);
        List<OrderClassifyAttachEntity> orderClassifyAttachs = orderClassifyAttachService.list(wrapper);

        Set<String> tripsIdSet = orderClassifyAttachs.stream().map(OrderClassifyAttachEntity::getTripsId).collect(Collectors.toSet());
        Set<String> truckIdSet = orderClassifyAttachs.stream().map(OrderClassifyAttachEntity::getTruckId).collect(Collectors.toSet());
        Set<String> driverIdSet = orderClassifyAttachs.stream().map(OrderClassifyAttachEntity::getDriverId).collect(Collectors.toSet());

        CompletableFuture<Map<String, TransportTripsDto>> tripsMapFuture = PdCompletableFuture.tripsMapFuture(transportTripsFeign, tripsIdSet);
        CompletableFuture<Map<String, TruckDto>> truckMapFuture = PdCompletableFuture.truckMapFuture(truckFeign, truckIdSet);
        CompletableFuture<Map<Long, User>> driverMapFuture = PdCompletableFuture.driverMapFuture(userApi, driverIdSet);

        Set<String> tripsSet = new LinkedHashSet<>();
        Set<String> truckSet = new LinkedHashSet<>();
        Set<String> driverSet = new LinkedHashSet<>();
        Map<String, TransportTripsDto> tripsMap = tripsMapFuture.get();
        Map<String, TruckDto> truckMap = truckMapFuture.get();
        Map<Long, User> driverMap = driverMapFuture.get();
        for (OrderClassifyAttachEntity orderClassifyAttach : orderClassifyAttachs) {
            tripsSet.add(tripsMap.get(orderClassifyAttach.getTripsId()).getName());
            truckSet.add(truckMap.get(orderClassifyAttach.getTruckId()).getLicensePlate());
            driverSet.add(driverMap.get(Long.parseLong(orderClassifyAttach.getDriverId())).getName());
        }
        orderClassifyLogDTO.setTripsSet(tripsSet);
        orderClassifyLogDTO.setTruckSet(truckSet);
        orderClassifyLogDTO.setDriverSet(driverSet);
    }

    /**
     * 构建当前分类的订单经过的全部机构 和 全部的线路
     *
     * @param item
     * @param orderClassifyLogDTO
     */
    @SneakyThrows
    private void buildTransportLine(OrderClassifyEntity item, OrderClassifyLogDTO orderClassifyLogDTO) {
        CacheLineUseEntity cacheLineUseEntity = cacheLineUseService.getByOrderClassifyId(item.getId());
        Set<String> lineSet = new LinkedHashSet<>();
        Set<String> transportLineSet = new LinkedHashSet<>();
        if (cacheLineUseEntity != null) {
            CacheLineEntity cacheLineEntity = cacheLineService.getById(cacheLineUseEntity.getCacheLineId());
            List<CacheLineDetailEntity> cacheLineDetailEntities = cacheLineDetailService.findByCacheLineId(cacheLineUseEntity.getCacheLineId());
            Set<String> transportLineIdSet = cacheLineDetailEntities.stream().map(cacheLineDetailEntity -> cacheLineDetailEntity.getTransportLineId()).collect(Collectors.toSet());
            CompletableFuture<Map<String, TransportLineDto>> transportLineMapFuture = PdCompletableFuture.transportLineIdMapFuture(transportLineFeign, transportLineIdSet);

            Set<String> agencySet = new HashSet<>();
            agencySet.add(cacheLineEntity.getStartAgencyId());
            agencySet.add(cacheLineEntity.getEndAgencyId());
            agencySet.addAll(cacheLineDetailEntities.stream().map(cacheLineDetailEntity -> cacheLineDetailEntity.getStartAgencyId()).collect(Collectors.toSet()));
            agencySet.addAll(cacheLineDetailEntities.stream().map(cacheLineDetailEntity -> cacheLineDetailEntity.getEndAgencyId()).collect(Collectors.toSet()));

            CompletableFuture<Map<Long, Org>> agnecyMapFu = PdCompletableFuture.agencyMapFuture(orgApi, null, agencySet, null);
            Map<String, TransportLineDto> transportLineMap = transportLineMapFuture.get();
            Map<Long, Org> agency = agnecyMapFu.get();

            cacheLineDetailEntities.forEach(cacheLineDetailEntity -> {
                CacheLineDetailDTO cacheLineDetailDTO = new CacheLineDetailDTO();
                BeanUtils.copyProperties(cacheLineDetailEntity, cacheLineDetailDTO);
                if (StringUtils.isNotEmpty(cacheLineDetailDTO.getStartAgencyId())) {
                    cacheLineDetailDTO.setStartAgency(agency.get(Long.parseLong(cacheLineDetailDTO.getStartAgencyId())).getName());
                }
                if (StringUtils.isNotBlank(cacheLineDetailDTO.getEndAgencyId())) {
                    cacheLineDetailDTO.setEndAgency(agency.get(Long.parseLong(cacheLineDetailDTO.getEndAgencyId())).getName());
                }
                transportLineSet.add(transportLineMap.get(cacheLineDetailDTO.getTransportLineId()).getName());

                lineSet.add(cacheLineDetailDTO.getStartAgency());
                lineSet.add(cacheLineDetailDTO.getEndAgency());

            });

            orderClassifyLogDTO.setTransportLineSet(transportLineSet);
            orderClassifyLogDTO.setLineSet(lineSet);


            CacheLineDTO cacheLineDTO = new CacheLineDTO();
            BeanUtils.copyProperties(cacheLineEntity, cacheLineDTO);
            orderClassifyLogDTO.setCacheLineDTO(cacheLineDTO);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值