ScheduleRedisService.java 11.3 KB
package com.bsth.redis;

import com.bsth.Application;
import com.bsth.common.BasicData;
import com.bsth.entity.ScheduleRealInfo;
import com.bsth.redis.util.RedisUtils;
import com.bsth.repository.ScheduleRealInfoRepository;
import com.bsth.server_rs.base_info.line.Line;
import com.bsth.server_rs.base_info.line.buffer.LineBufferData;
import com.bsth.util.ConfigUtil;
import com.bsth.util.ConvertUtil;
import com.google.common.collect.ArrayListMultimap;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 班次 redis 缓存管理
 * Created by panzhao on 2017/3/13.
 */
@Service
@Order(2)
public class ScheduleRedisService implements CommandLineRunner {

    @Autowired
    ScheduleRealInfoRepository scheduleRealInfoRepository;

    private final static String REDIS_KEY_PREFIX = "schedule:";

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    RedisUtils redisUtils;

    static Logger logger = LoggerFactory.getLogger(ScheduleRedisService.class);

    private final static long DAY_TIME = 1000 * 60 * 60 * 24L;

    /**
     * 将一批班次写入redis
     *
     * @param list
     */
    public void wirte(List<ScheduleRealInfo> list) {
        ArrayListMultimap<String, ScheduleRealInfo> multimap;
        try {
            if (list.size() == 0)
                return;
            //按日期和线路分组数据
            Class clazz = ScheduleRealInfo.class;
            multimap = new ConvertUtil().groupMultiList(list, ":", clazz.getDeclaredField("xlBm"), clazz.getDeclaredField("scheduleDateStr"));

            //写入redis
            Iterator<String> iterator = multimap.keySet().iterator();
            String key;
            while (iterator.hasNext()) {
                key = iterator.next();
                replace(key, multimap.get(key));
            }
        } catch (Exception e) {
            logger.error("", e);
        }
    }

    /**
     * 将 list 与redis里的数据合并
     *
     * @param key
     * @param list
     */
    public void mergeData(String key, List<ScheduleRealInfo> list) {
        key = REDIS_KEY_PREFIX + key.replaceAll("-", "");

        ListOperations<String, ScheduleRealInfo> ops = redisTemplate.opsForList();
        List<ScheduleRealInfo> cacheList = ops.range(key, 0, -1);

        for (ScheduleRealInfo sch : cacheList) {
            if (!list.contains(sch))
                list.add(sch);
        }

        //更新
        redisTemplate.execute(redisUtils.getUpdateCallback(key, list));
    }

    /**
     * 覆盖数据
     *
     * @param key
     * @param list
     */
    public void replace(String key, List<ScheduleRealInfo> list) {
        key = REDIS_KEY_PREFIX + key.replaceAll("-", "");
        redisTemplate.execute(redisUtils.getUpdateCallback(key, list));
    }

    /**
     * 删除数据
     *
     * @param key
     */
    public void delete(String lineCode, String rq) {
        String key = REDIS_KEY_PREFIX + (lineCode + ":" + rq).replaceAll("-", "");
        redisTemplate.delete(key);
    }

    /**
     * 根据日期和线路编码从redis获取班次
     *
     * @param dateStr
     * @param lineCode
     * @return
     */
    public List<ScheduleRealInfo> read(String dateStr, String lineCode) {
        return redisTemplate.opsForList().range(REDIS_KEY_PREFIX + lineCode + ":" + dateStr, 0, -1);
    }

    /**
     * 返回指定日期,公司的实际排班,并按线路_车辆分组
     *
     * @param rq
     * @param companyId
     * @return
     */
    public ArrayListMultimap<String, ScheduleRealInfo> findByDateAndGroupByNbbm(String rq, String companyId) {
        List<String> lineArray = LineBufferData.findCodesByCompany(companyId);
        ArrayListMultimap<String, ScheduleRealInfo> rs = ArrayListMultimap.create();

        rq = rq.replaceAll("-", "");
        List<ScheduleRealInfo> list;
        for (String lineCode : lineArray) {

            list = read(rq, lineCode);

            for (ScheduleRealInfo sch : list) {
                rs.put(sch.getXlBm() + "_" + sch.getClZbh(), sch);
            }
        }
        return rs;
    }

    /**
     * 返回指定日期,公司的实际排班,并按线路分组
     *
     * @param rq
     * @param companyId
     * @return
     */
    public ArrayListMultimap<String, ScheduleRealInfo> findByDateAndGroupByLine(String rq, String companyId) {

        List<String> lineArray = LineBufferData.findCodesByCompany(companyId);
        ArrayListMultimap<String, ScheduleRealInfo> rs = ArrayListMultimap.create();

        rq = rq.replaceAll("-", "");
        List<ScheduleRealInfo> list;
        for (String lineCode : lineArray) {

            list = read(rq, lineCode);

            for (ScheduleRealInfo sch : list) {
                rs.put(sch.getXlBm(), sch);
            }
        }
        return rs;
    }

    /**
     * 返回指定日期,所有实际排班,并按自编号分组
     *
     * @param rq
     * @return
     */
    public ArrayListMultimap<String, ScheduleRealInfo> findByDate(String rq) {

        ArrayListMultimap<String, ScheduleRealInfo> rs = ArrayListMultimap.create();

        List<ScheduleRealInfo> list = scheduleRealInfoRepository.findAll(rq);
        for (ScheduleRealInfo sch : list) {
            rs.put(sch.getClZbh(), sch);
        }
        return rs;
    }

    @Autowired
    ScheduleRefreshThread scheduleRefreshThread;
    @Autowired
    ScheduleClearThread scheduleClearThread;

    @Override
    public void run(String... strings) throws Exception {
        //用子线程去加载,,不要阻塞
        Application.mainServices.schedule(new Runnable() {
            @Override
            public void run() {
            	try {
            		logger.info("redis 实际排班 start...");
	                int cacheDays = Integer.parseInt(ConfigUtil.get("cache.days"));
	                //设置key 序列化器
	                redisTemplate.setKeySerializer(new StringRedisSerializer());
	
	                DateTime dt = new DateTime();
	                dt = dt.minusDays(cacheDays);
	                List<ScheduleRealInfo> list = scheduleRealInfoRepository.findByDateLT(dt.toString("yyyy-MM-dd"));
	                calcTime(list);
	                //写入redis
	                wirte(list);
	                logger.info("redis 实际排班 over...");
            	} catch (Exception e) {
            		logger.info("redis 实际排班 异常", e);
            	}
            }
        }, 5, TimeUnit.SECONDS);

        //定时刷新一次当日实际排班
        int minute = 5;
        Application.mainServices.scheduleWithFixedDelay(scheduleRefreshThread, minute, minute, TimeUnit.MINUTES);
        //24小时清理一次实际排班数据
        Application.mainServices.scheduleWithFixedDelay(scheduleClearThread, 24, 24, TimeUnit.HOURS);
    }

    public List<ScheduleRealInfo> findByMultiLine(List<String> lineArray, String rq) {
        rq = rq.replaceAll("-", "");
        List<ScheduleRealInfo> rs = new ArrayList<>();
        for (String lineCode : lineArray) {
            rs.addAll(read(rq, lineCode));
        }
        return rs;
    }

    @Component
    public static class ScheduleRefreshThread extends Thread {

        @Autowired
        ScheduleRealInfoRepository realInfoRepository;

        @Autowired
        ScheduleRedisService scheduleRedisService;

        @Override
        public void run() {
            try {
                DateTime dt = new DateTime();
                DateTime yesterday = dt.minusDays(5);

                String rq = yesterday.toString("yyyy-MM-dd");
                logger.info("refresh lt yesterday ..." + rq + " -start");
                List<ScheduleRealInfo> list = realInfoRepository.findByDateLT(rq);

                //计算时间戳
                scheduleRedisService.calcTime(list);
                scheduleRedisService.wirte(list);

                logger.info("refresh lt yesterday ..." + rq + " -end### size: " + list.size());
            } catch (Exception e) {
                logger.error("", e);
            }
        }
    }

    @Component
    public static class ScheduleClearThread extends Thread {

        @Autowired
        ScheduleRedisService scheduleRedisService;

        @Override
        public void run() {
            try {
                int cacheDays = Integer.parseInt(ConfigUtil.get("cache.days"));
                DateTime dt = new DateTime();
                dt = dt.minusDays(cacheDays);
                String rq = dt.toString("yyyy-MM-dd");

                List<Line> lines = LineBufferData.findAll();
                for (Line line : lines) {
                    scheduleRedisService.delete(line.getLineCode(), rq);
                }
            } catch (Exception e) {
                logger.error("", e);
            }
        }
    }

    /**
     * ############ 时间戳计算 ##########
     */
    private static DateTimeFormatter fmtyyyyMMddHHmm = DateTimeFormat.forPattern("yyyy-MM-ddHH:mm"),
            fmtHHmm = DateTimeFormat.forPattern("HH:mm");

    public void calcTime(List<ScheduleRealInfo> list) {
        if (list.size() == 0)
            return;

        //计算真实执行日期 和 时间戳
        for (ScheduleRealInfo sch : list) {
            calcRealDate(BasicData.lineStartTimeMap.get(sch.getXlBm()), sch);
        }
    }

    /**
     * @Title: calcRealDate
     * @Description: TODO(计算班次的真实执行日期)
     */
    public void calcRealDate(String startTime, ScheduleRealInfo sch) {
        try {
            if (null == startTime)
                return;

            if (null == sch.getBcsj())
                sch.setBcsj(0);

            String rq = sch.getScheduleDateStr();
            //计发时间
            sch.setFcsjT(parseTime(rq, sch.getFcsj(), startTime));
            //待发时间
            sch.setDfsjT(parseTime(rq, sch.getDfsj(), startTime));
            //计划终点时间
            if (StringUtils.isEmpty(sch.getZdsj())) {
                sch.setZdsjT(sch.getFcsjT() + (sch.getBcsj() * 60 * 1000));
                sch.setZdsj(fmtHHmm.print(sch.getZdsjT()));
            } else
                sch.setZdsjT(sch.getFcsjT() + (sch.getBcsj() * 60 * 1000));
            //实发时间
            if (StringUtils.isNotEmpty(sch.getFcsjActual()))
                sch.setFcsjActualAll(parseTime(rq, sch.getFcsjActual(), startTime));

            //实达时间
            if (StringUtils.isNotEmpty(sch.getZdsjActual()))
                sch.setZdsjActualAll(parseTime(rq, sch.getZdsjActual(), startTime));
        } catch (Exception e) {
            logger.error("", e);
        }
    }

    private long parseTime(String rq, String sj, String startTime) {
        long t = fmtyyyyMMddHHmm.parseMillis(rq + sj);
        if (sj.compareTo(startTime) < 0) {
            t += DAY_TIME;
        }
        return t;
    }
}