隨筆:要求時(shí)限限制在7日

今日開發(fā)收到了一個(gè)“簡(jiǎn)單”的需求:

“這項(xiàng)任務(wù)需要限制被指派者在7日內(nèi)完成茧跋。”

“嗯卓囚,果然簡(jiǎn)單瘾杭!”

“哦,客戶說(shuō)是7個(gè)工作日哪亿,應(yīng)該好弄吧粥烁?”

“好,工作日……啥S蕖讨阻?工作日……讓朕三思一下……”

站在坑里的我心里跑過(guò)……各種方法。

其實(shí)按照正常情況下篡殷,在工作日創(chuàng)建任務(wù)并指派钝吮,然后限期在7個(gè)工作日后則必然就是9天后(加上周末兩天嗎,看咱這數(shù)學(xué)_)板辽。所以主要麻煩點(diǎn)在于“假期調(diào)休”奇瘦!就是要解決幾個(gè)問(wèn)題:

  • 假期調(diào)休數(shù)據(jù)來(lái)源
  • 假期數(shù)據(jù)的獲取及記錄
  • 當(dāng)前日期后指定工作日后的日期計(jì)算

假期調(diào)休數(shù)據(jù)來(lái)源

先考慮過(guò)手動(dòng)編輯,但是確實(shí)有些麻煩劲弦,網(wǎng)上搜索后決定使用【聚合數(shù)據(jù)的萬(wàn)年歷接口】耳标,可以免費(fèi)使用。

Tips:先要注冊(cè)用戶并申請(qǐng)使用接口獲取Key邑跪,這里省略此步次坡。

這里選擇使用【獲取近期假期】(http://v.juhe.cn/calendar/month)這個(gè)接口呼猪,因?yàn)樗祷氐膬?nèi)容更加詳細(xì),包括了假期中調(diào)休的日子砸琅。

Tips:聚合數(shù)據(jù)的請(qǐng)求接口參數(shù)的日期格式為yyyy-M宋距,返回?cái)?shù)據(jù)中的日期格式為yyyy-M-d;而項(xiàng)目中保存時(shí)需要的是yyyy-MM-dd明棍,要注意比對(duì)和轉(zhuǎn)換乡革。

假期數(shù)據(jù)的獲取及記錄

有了數(shù)據(jù)就可以開始開工了!

Tips:項(xiàng)目實(shí)際上是個(gè)定時(shí)任務(wù)后臺(tái)程序摊腋,工程采用了SpringBoot框架+MyBatis數(shù)據(jù)框架+Quartz任務(wù)調(diào)度框架沸版。MyBatis配置和Quartz動(dòng)態(tài)任務(wù)配置這些我后面其他系列的文章會(huì)有所涉及,這里就不說(shuō)了兴蒸。

創(chuàng)建數(shù)據(jù)表
字段名 格式 說(shuō)明
date_tag VARCHAR(11) 日期標(biāo)識(shí)视粮,格式為yyyy-MM-dd
name VARCHAR(64) 假期名稱
year INT(5) 所屬年份
date DATE 具體日期
配置訪問(wèn)地址

在application.properties(SpringBoot框架)中增加自定義配置項(xiàng):

#第三方接口
#聚合數(shù)據(jù)
other.juhe.url.holidayMonth=http://v.juhe.cn/calendar/month?key=我的Key&year-month=
添加數(shù)據(jù)層操作(mapper)

添加數(shù)據(jù)表的操作,包括了添加橙凳、刪除和獲取假期數(shù)量等操作:

/**
 * 對(duì)假期字典表進(jìn)行處理
 * Created by maqiang on 2017/6/7.
 */
public interface HolidayMapper
{
    /**
     * 獲取指定日期間的假期數(shù)量
     * @param startDate
     * @param endDate
     * @return
     */
    @Select("select count(date_tag) from c_holiday where date<=#{endDate} and date>=#{startDate}")
    Integer count(@Param("startDate") String startDate,@Param("endDate") String endDate);

    /**
     * 添加或更新一條假期記錄
     *
     * @param holiday
     */
    @Insert("insert into c_holiday("+
                    "date_tag,"+
                    "name,"+
                    "year,"+
                    "date"+
                    ")VALUES("+
                    "#{holiday.date_tag},"+
                    "#{holiday.name},"+
                    "#{holiday.year},"+
                    "#{holiday.date}"+
                    ")")
    void insert(@Param("holiday") Map<String,Object> holiday);

    /**
     * 刪除指定日期的假期記錄
     *
     * @return
     */
    @Delete("delete from c_holiday where date_tag like concat(#{dateTag},'%')")
    void delete(@Param("dateTag") String dateTag);

    /**
     * 刪除指定日期之前的全部記錄
     *
     * @return
     */
    @Delete("delete from c_holiday where date<#{date}")
    void clearBefore(@Param("date") String date);
}

Tips:這里使用了MyBatis的注解方式配置蕾殴;未使用Bean,使用Map傳遞數(shù)據(jù)岛啸。

添加年度假期更新服務(wù)處理

在系統(tǒng)啟動(dòng)時(shí)對(duì)假期數(shù)據(jù)進(jìn)行初始化钓觉,如果現(xiàn)在為12月,則連明年的假日數(shù)據(jù)一起初始化坚踩。并增加定時(shí)任務(wù)每月1日定時(shí)調(diào)用此更新服務(wù)處理荡灾,具體流程如下:

數(shù)據(jù)初始化

Tips:每月更新便于及時(shí)獲取到假期的變化,并在12月就可以更新明年的假期數(shù)據(jù)瞬铸。

Tips:訪問(wèn)第三方接口使用的是apache的httpasyncclient框架批幌。這個(gè)項(xiàng)目我使用的是SpringBoot+Gradle配置,因此加入支持配置為:

//httpClient
compile('org.apache.httpcomponents:httpclient:4.5.3')
compile('org.apache.httpcomponents:httpasyncclient:4.1.3')
使用@value注解引入配置連接地址
    /**
     * 萬(wàn)年歷接口
     */
    @Value("${other.juhe.url.holidayMonth}")
    private String urlHolidayMonth;
方法:從第三方接口異步獲取月份假期詳情
    /**
     * 請(qǐng)求第三方接口獲取月份法定假期詳情
     *
     * @param month
     * @return
     */
    public List<Map<String,Object>> loadMonthHoliday(String month) throws Exception
    {
        //發(fā)送異步請(qǐng)求
        AsyncClientHttpRequestFactory asyncFactory=new HttpComponentsAsyncClientHttpRequestFactory();
        URI uri=new URI(urlHolidayMonth+month);
        AsyncClientHttpRequest asynReq=asyncFactory.createAsyncRequest(uri,HttpMethod.GET);
        ListenableFuture<ClientHttpResponse> future=asynReq.executeAsync();
        ClientHttpResponse response=future.get();

        //獲取結(jié)果
        InputStream inputStream=response.getBody();
        BufferedReader reader=new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        String strRead=null;
        StringBuffer sb=new StringBuffer();
        while((strRead=reader.readLine())!=null)
        {
            sb.append(strRead);
        }
        String result=sb.toString();

        //轉(zhuǎn)成JSON
        ObjectMapper objectMapper=new ObjectMapper();
        Map<String,Object> json=objectMapper.readValue(result,Map.class);
        //解析出結(jié)果是否正確以及data內(nèi)容
        Integer code=Integer.parseInt(json.get("error_code")+"");
        String reason=json.get("reason")+"";
        if(code!=0) throw new Exception(reason);

        String holiday=((Map)((Map)json.get("result")).get("data")).get("holiday")+"";
        return objectMapper.readValue(holiday,List.class);
    }

Tips:注意聚合數(shù)據(jù)返回的holiday字段是個(gè)Json格式的字符串嗓节,要做一下轉(zhuǎn)換荧缘。

方法:生成一年的周末數(shù)據(jù)
    /**
     * 生成一年的全部假期列表
     *
     * @return
     */
    public Map<String,Map<String,Object>> loadYearWeekly(final String year) throws ParseException
    {
        //獲取起始日期
        final SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
        String startDateString=year+"-01-01";
        String endDateString=year+"-12-31";
        Date startDate=format.parse(startDateString);

        //比較年份起始日和當(dāng)前日期
        Date today=new Date();
        startDate=(today.getTime()>startDate.getTime())?today:startDate;

        //遍歷全年每天,記錄全部周末
        Calendar calendar=Calendar.getInstance();
        //結(jié)束日為最后一天加一天
        calendar.setTime(format.parse(endDateString));
        calendar.add(Calendar.DATE,1);
        endDateString=format.format(calendar.getTime());
        //設(shè)置開始日期
        calendar.setTime(startDate);
        String curDateString=format.format(startDate);
        Date curDate=null;
        Map<String,Map<String,Object>> result=new LinkedHashMap<>();
        while(!curDateString.equals(endDateString))
        {
            //當(dāng)前日
            curDate=calendar.getTime();

            //當(dāng)前日如果為周末拦宣,則記錄結(jié)果
            if(calendar.get(Calendar.DAY_OF_WEEK)==Calendar.SATURDAY
                    || calendar.get(Calendar.DAY_OF_WEEK)==Calendar.SUNDAY)
            {
                final Date finalCurDate=curDate;
                result.put(format.format(finalCurDate),new LinkedHashMap()
                {{
                    put("date_tag",format.format(finalCurDate));
                    put("name","周末");
                    put("year",year);
                    put("date",finalCurDate);
                }});
            }

            //前進(jìn)一天
            calendar.add(Calendar.DATE,1);
            curDateString=format.format(calendar.getTime());
        }

        return result;
    }
方法:獲取一年的全部假期數(shù)據(jù)截粗,并從周末數(shù)據(jù)中扣除調(diào)休數(shù)據(jù)
    /**
     * 通過(guò)第三方接口,生成指定年份全部法定假期鸵隧;并扣除調(diào)休的周末日期
     *
     * @param year
     * @return
     * @throws Exception
     */
    public Map<String,Map<String,Object>> loadYearHoliday(String year,Map<String,Map<String,Object>> holidayList)
            throws Exception
    {
        //獲取起始日期
        final SimpleDateFormat monthFormat=new SimpleDateFormat("yyyy-M");
        final SimpleDateFormat readFormat=new SimpleDateFormat("yyyy-M-d");
        final SimpleDateFormat writeFormat=new SimpleDateFormat("yyyy-MM-dd");
        String startDateString=year+"-1";
        String endDateString=year+"-12";
        Date startDate=monthFormat.parse(startDateString);

        //比較年份起始日和當(dāng)前日期
        Date today=new Date();
        startDate=(today.getTime()>startDate.getTime())?today:startDate;

        //遍歷全年每月桐愉,解析并記錄全部法定假期
        Calendar calendar=Calendar.getInstance();
        //結(jié)束日為最后一個(gè)月加一個(gè)月
        calendar.setTime(monthFormat.parse(endDateString));
        calendar.add(Calendar.MONTH,1);
        endDateString=monthFormat.format(calendar.getTime());
        //設(shè)置開始日期
        calendar.setTime(startDate);
        String curDateString=monthFormat.format(startDate);
        while(!curDateString.equals(endDateString))
        {
            //獲取當(dāng)前月的法定假日
            List<Map<String,Object>> holiday=null;
            try
            {
                holiday=loadMonthHoliday(curDateString);
            }
            catch(Exception ex)
            {
                logger.error(ex.getMessage(),ex);
                holiday=null;
            }

            //解析和遍歷結(jié)果
            if(holiday!=null)
            {
                for(Map<String,Object> content : holiday)
                {
                    String name=content.get("name")+"";

                    for(Map<String,Object> dateMap : ((List<Map<String,Object>>)content.get("list")))
                    {
                        int status=Integer.parseInt(dateMap.get("status")+"");
                        String dateString=dateMap.get("date")+"";
                        dateString=writeFormat.format(readFormat.parse(dateString));
                        //增加假期
                        if(status==1)
                        {
                            Map data=new LinkedHashMap();
                            data.put("date_tag",dateString);
                            data.put("name",name);
                            data.put("year",year);
                            data.put("date",writeFormat.parse(dateString));
                            holidayList.put(dateString,data);
                        }
                        //扣除調(diào)休
                        if(status==2)
                        {
                            holidayList.remove(dateString);
                        }
                    }
                }
            }

            //前進(jìn)一個(gè)月
            calendar.add(Calendar.MONTH,1);
            curDateString=monthFormat.format(calendar.getTime());
        }

        return holidayList;
    }

方法:初始化一年的全部假期

    /**
     * 載入全年的的假期
     *
     * @param year
     */
    public void loadYearAllHoliday(String year) throws Exception
    {
        logger.info("Start holiday init!");
        Map<String,Map<String,Object>> holidayMap=new LinkedHashMap<>();

        //載入當(dāng)前一年的周末
        holidayMap.putAll(loadYearWeekly(year));
        logger.info("Load all week holiday("+year+"),size:"+holidayMap.size());

        //獲取當(dāng)前一年的全部法定假日,并清除調(diào)休
        holidayMap=loadYearHoliday(year,holidayMap);
        logger.info("Load all holiday("+year+"),size:"+holidayMap.size());

        //將全部假期結(jié)果寫入數(shù)據(jù)庫(kù)
        clearAndInsert(year,holidayMap);
        logger.info("Insert holiday success");
    }
    
    /**
     * 批量插入假期記錄
     *
     * @param holidayMap
     */
    public void clearAndInsert(String deletePattern,Map<String,Map<String,Object>> holidayMap)
    {
        //插入前清除數(shù)據(jù)
        if(StringUtils.nonEmptyString(deletePattern))
        {
            holidayMapper.delete(deletePattern);
        }

        //插入數(shù)據(jù)
        for(Map<String,Object> holiday : holidayMap.values())
        {
            holidayMapper.insert(holiday);
        }
    }
最后是啟動(dòng)時(shí)數(shù)據(jù)初始化和定時(shí)任務(wù)調(diào)用的處理
    /**
     * 初始化:獲取今年假期數(shù)據(jù)掰派,初始化假期字典表
     * 再根據(jù)系統(tǒng)參數(shù)配置的工作日天數(shù)从诲,計(jì)算出實(shí)際工作日+假期的天數(shù)并記錄
     */
    @PostConstruct
    public void init() throws Exception
    {
        //載入今年的假期
        SimpleDateFormat format=new SimpleDateFormat("yyyy");
        String year=format.format(new Date());
        loadYearAllHoliday(year);

        //如果當(dāng)前為12月,則明年的假期一起載入
        SimpleDateFormat monthFormat=new SimpleDateFormat("MM");
        String month=monthFormat.format(new Date());
        if("12".equals(month))
        {
            Calendar calendar=Calendar.getInstance();
            calendar.add(Calendar.YEAR,1);
            year=format.format(calendar.getTime());
            loadYearAllHoliday(year);
        }

        ...
    }

Tips:這里代碼并未寫全靡羡,結(jié)尾會(huì)有代碼純凈版系洛。

這樣俊性,假期數(shù)據(jù)就全部載入并記錄到數(shù)據(jù)庫(kù)中,下面就要執(zhí)行計(jì)算了描扯。

當(dāng)前日期后指定工作日后的日期計(jì)算

這里首先考慮實(shí)際上每日的7個(gè)工作日后在當(dāng)天是肯定不會(huì)變的定页,所以不需要在每次創(chuàng)建任務(wù)時(shí)就跑全部流程計(jì)算日期。而是在每日凌晨時(shí)執(zhí)行定時(shí)任務(wù)計(jì)算好7個(gè)工作日實(shí)際間隔的日數(shù)并保存在Redis中绽诚,這樣當(dāng)天創(chuàng)建的任務(wù)可以直接取出間隔計(jì)算限期就好了典徊。

Tips:同時(shí)在系統(tǒng)啟動(dòng)初始化也需計(jì)算此間隔,并記錄在Redis中恩够。

工作日的算法如下:

工作日間隔算法

在今日日期到配置指定的工作日數(shù)量之間卒落,統(tǒng)計(jì)數(shù)據(jù)庫(kù)中這段時(shí)間內(nèi)的假期數(shù)量,如果為0則當(dāng)前指定工作日數(shù)即為實(shí)際天數(shù)蜂桶;如果不為0儡毕,則實(shí)際天數(shù)為當(dāng)前值增加假期數(shù);然后再次統(tǒng)計(jì)增加的這段天數(shù)中的假期數(shù)量扑媚,再次處理腰湾;以此類推,直到統(tǒng)計(jì)假期數(shù)為0疆股。

計(jì)算方法流程如下:

工作日計(jì)算流程

其實(shí)代碼看著要更簡(jiǎn)單些:

    /**
     * 獲取指定的工作日的實(shí)際天數(shù)
     *
     * @param workDay
     * @return
     */
    public Integer calculateRealDay(Date startDate,Integer workDay)
    {
        SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
        Calendar calendar=Calendar.getInstance();
        calendar.setTime(startDate);

        //循環(huán)查詢假期數(shù)并增加
        Date curStartDate=startDate;
        calendar.add(Calendar.DATE,workDay.intValue());
        int holidayCount=-1;
        while(holidayCount!=0)
        {
            //查詢假期表
          holidayCount=holidayMapper.count(
              format.format(curStartDate),
              format.format(calendar.getTime()));

            if(holidayCount>0)
            {
                //增加結(jié)果數(shù)
                workDay=workDay.intValue()+holidayCount;
                //轉(zhuǎn)移開始日期和結(jié)束日期
                calendar.add(Calendar.DATE,1);
                curStartDate=calendar.getTime();
                calendar.add(Calendar.DATE,holidayCount-1);
            }
        }

        return workDay;
    }

至此费坊,這個(gè)“簡(jiǎn)單任務(wù)”終于完成了。

image.png

代碼純凈版

application.properties

#第三方接口
#聚合數(shù)據(jù)
other.juhe.url.holidayMonth=http://v.juhe.cn/calendar/month?key=我的Key&year-month=

HolidayMapper.java

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.Map;

/**
 * 對(duì)假期字典表進(jìn)行處理
 * Created by maqiang on 2017/6/7.
 */
public interface HolidayMapper
{
    /**
     * 獲取指定日期間的假期數(shù)量
     * @param startDate
     * @param endDate
     * @return
     */
    @Select("select count(date_tag) from c_holiday where date<=#{endDate} and date>=#{startDate}")
    Integer count(@Param("startDate") String startDate,@Param("endDate") String endDate);

    /**
     * 添加或更新一條假期記錄
     *
     * @param holiday
     */
    @Insert("insert into c_holiday("+
                    "date_tag,"+
                    "name,"+
                    "year,"+
                    "date"+
                    ")VALUES("+
                    "#{holiday.date_tag},"+
                    "#{holiday.name},"+
                    "#{holiday.year},"+
                    "#{holiday.date}"+
                    ")")
    void insert(@Param("holiday") Map<String,Object> holiday);

    /**
     * 刪除指定日期的假期記錄
     *
     * @return
     */
    @Delete("delete from c_holiday where date_tag like concat(#{dateTag},'%')")
    void delete(@Param("dateTag") String dateTag);

    /**
     * 刪除指定日期之前的全部記錄
     *
     * @return
     */
    @Delete("delete from c_holiday where date<#{date}")
    void clearBefore(@Param("date") String date);
}

HolidayService.java

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mchange.v2.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.AsyncClientHttpRequest;
import org.springframework.http.client.AsyncClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;
import org.springframework.util.concurrent.ListenableFuture;
import pb.analyzer.data.platform.HolidayMapper;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 處理假期相關(guān)的業(yè)務(wù)
 * Created by maqiang on 2017/6/7.
 */
@Transactional(rollbackFor=Exception.class)
@Service
public class HolidayService
{
    //日志
    private static final Logger logger=LoggerFactory.getLogger(HolidayService.class);

    /**
     * 假期字典表管理
     */
    @Autowired
    HolidayMapper holidayMapper;

    /**
     * Redis管理(平臺(tái)業(yè)務(wù))
     */
    @Autowired
    PlatformRedisService platformRedisService;

    /**
     * 萬(wàn)年歷接口
     */
    @Value("${other.juhe.url.holidayMonth}")
    private String urlHolidayMonth;

    /**
     * 初始化:獲取今年假期數(shù)據(jù)旬痹,初始化假期字典表
     * 再根據(jù)系統(tǒng)參數(shù)配置的工作日天數(shù)附井,計(jì)算出實(shí)際工作日+假期的天數(shù)并記錄
     */
    @PostConstruct
    public void init() throws Exception
    {
        //載入今年的假期
        SimpleDateFormat format=new SimpleDateFormat("yyyy");
        String year=format.format(new Date());
        loadYearAllHoliday(year);

        //如果當(dāng)前為12月,則明年的假期一起載入
        SimpleDateFormat monthFormat=new SimpleDateFormat("MM");
        String month=monthFormat.format(new Date());
        if("12".equals(month))
        {
            Calendar calendar=Calendar.getInstance();
            calendar.add(Calendar.YEAR,1);
            year=format.format(calendar.getTime());
            loadYearAllHoliday(year);
        }

        //更新系統(tǒng)配置天數(shù)對(duì)應(yīng)的今日的真實(shí)工作日數(shù)
        updateRealDay();
        logger.info("Holiday init success!");
    }

    /**
     * 載入全年的的假期
     *
     * @param year
     */
    public void loadYearAllHoliday(String year) throws Exception
    {
        logger.info("Start holiday init!");
        Map<String,Map<String,Object>> holidayMap=new LinkedHashMap<>();

        //載入當(dāng)前一年的周末
        holidayMap.putAll(loadYearWeekly(year));
        logger.info("Load all week holiday("+year+"),size:"+holidayMap.size());

        //獲取當(dāng)前一年的全部法定假日唱凯,并清除調(diào)休
        holidayMap=loadYearHoliday(year,holidayMap);
        logger.info("Load all holiday("+year+"),size:"+holidayMap.size());

        //將全部假期結(jié)果寫入數(shù)據(jù)庫(kù)
        clearAndInsert(year,holidayMap);
        logger.info("Insert holiday success");
    }

    /**
     * 生成一年的全部假期列表
     *
     * @return
     */
    public Map<String,Map<String,Object>> loadYearWeekly(final String year) throws ParseException
    {
        //獲取起始日期
        final SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
        String startDateString=year+"-01-01";
        String endDateString=year+"-12-31";
        Date startDate=format.parse(startDateString);

        //比較年份起始日和當(dāng)前日期
        Date today=new Date();
        startDate=(today.getTime()>startDate.getTime())?today:startDate;

        //遍歷全年每天羡忘,記錄全部周末
        Calendar calendar=Calendar.getInstance();
        //結(jié)束日為最后一天加一天
        calendar.setTime(format.parse(endDateString));
        calendar.add(Calendar.DATE,1);
        endDateString=format.format(calendar.getTime());
        //設(shè)置開始日期
        calendar.setTime(startDate);
        String curDateString=format.format(startDate);
        Date curDate=null;
        Map<String,Map<String,Object>> result=new LinkedHashMap<>();
        while(!curDateString.equals(endDateString))
        {
            //當(dāng)前日
            curDate=calendar.getTime();

            //當(dāng)前日如果為周末谎痢,則記錄結(jié)果
            if(calendar.get(Calendar.DAY_OF_WEEK)==Calendar.SATURDAY
                    || calendar.get(Calendar.DAY_OF_WEEK)==Calendar.SUNDAY)
            {
                final Date finalCurDate=curDate;
                result.put(format.format(finalCurDate),new LinkedHashMap()
                {{
                    put("date_tag",format.format(finalCurDate));
                    put("name","周末");
                    put("year",year);
                    put("date",finalCurDate);
                }});
            }

            //前進(jìn)一天
            calendar.add(Calendar.DATE,1);
            curDateString=format.format(calendar.getTime());
        }

        return result;
    }

    /**
     * 通過(guò)第三方接口磕昼,生成指定年份全部法定假期;并扣除調(diào)休的周末日期
     *
     * @param year
     * @return
     * @throws Exception
     */
    public Map<String,Map<String,Object>> loadYearHoliday(String year,Map<String,Map<String,Object>> holidayList)
            throws Exception
    {
        //獲取起始日期
        final SimpleDateFormat monthFormat=new SimpleDateFormat("yyyy-M");
        final SimpleDateFormat readFormat=new SimpleDateFormat("yyyy-M-d");
        final SimpleDateFormat writeFormat=new SimpleDateFormat("yyyy-MM-dd");
        String startDateString=year+"-1";
        String endDateString=year+"-12";
        Date startDate=monthFormat.parse(startDateString);

        //比較年份起始日和當(dāng)前日期
        Date today=new Date();
        startDate=(today.getTime()>startDate.getTime())?today:startDate;

        //遍歷全年每月节猿,解析并記錄全部法定假期
        Calendar calendar=Calendar.getInstance();
        //結(jié)束日為最后一個(gè)月加一個(gè)月
        calendar.setTime(monthFormat.parse(endDateString));
        calendar.add(Calendar.MONTH,1);
        endDateString=monthFormat.format(calendar.getTime());
        //設(shè)置開始日期
        calendar.setTime(startDate);
        String curDateString=monthFormat.format(startDate);
        while(!curDateString.equals(endDateString))
        {
            //獲取當(dāng)前月的法定假日
            List<Map<String,Object>> holiday=null;
            try
            {
                holiday=loadMonthHoliday(curDateString);
            }
            catch(Exception ex)
            {
                logger.error(ex.getMessage(),ex);
                holiday=null;
            }

            //解析和遍歷結(jié)果
            if(holiday!=null)
            {
                for(Map<String,Object> content : holiday)
                {
                    String name=content.get("name")+"";

                    for(Map<String,Object> dateMap : ((List<Map<String,Object>>)content.get("list")))
                    {
                        int status=Integer.parseInt(dateMap.get("status")+"");
                        String dateString=dateMap.get("date")+"";
                        dateString=writeFormat.format(readFormat.parse(dateString));
                        //增加假期
                        if(status==1)
                        {
                            Map data=new LinkedHashMap();
                            data.put("date_tag",dateString);
                            data.put("name",name);
                            data.put("year",year);
                            data.put("date",writeFormat.parse(dateString));
                            holidayList.put(dateString,data);
                        }
                        //扣除調(diào)休
                        if(status==2)
                        {
                            holidayList.remove(dateString);
                        }
                    }
                }
            }

            //前進(jìn)一個(gè)月
            calendar.add(Calendar.MONTH,1);
            curDateString=monthFormat.format(calendar.getTime());
        }

        return holidayList;
    }

    /**
     * 請(qǐng)求第三方接口獲取月份法定假期詳情
     *
     * @param month
     * @return
     */
    public List<Map<String,Object>> loadMonthHoliday(String month) throws Exception
    {
        //發(fā)送異步請(qǐng)求
        AsyncClientHttpRequestFactory asyncFactory=new HttpComponentsAsyncClientHttpRequestFactory();
        URI uri=new URI(urlHolidayMonth+month);
        AsyncClientHttpRequest asynReq=asyncFactory.createAsyncRequest(uri,HttpMethod.GET);
        ListenableFuture<ClientHttpResponse> future=asynReq.executeAsync();
        ClientHttpResponse response=future.get();

        //獲取結(jié)果
        InputStream inputStream=response.getBody();
        BufferedReader reader=new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        String strRead=null;
        StringBuffer sb=new StringBuffer();
        while((strRead=reader.readLine())!=null)
        {
            sb.append(strRead);
        }
        String result=sb.toString();

        //轉(zhuǎn)成JSON
        ObjectMapper objectMapper=new ObjectMapper();
        Map<String,Object> json=objectMapper.readValue(result,Map.class);
        //解析出結(jié)果是否正確以及data內(nèi)容
        Integer code=Integer.parseInt(json.get("error_code")+"");
        String reason=json.get("reason")+"";
        if(code!=0) throw new Exception(reason);

        String holiday=((Map)((Map)json.get("result")).get("data")).get("holiday")+"";
        return objectMapper.readValue(holiday,List.class);
    }

    /**
     * 批量插入假期記錄
     *
     * @param holidayMap
     */
    public void clearAndInsert(String deletePattern,Map<String,Map<String,Object>> holidayMap)
    {
        //插入前清除數(shù)據(jù)
        if(StringUtils.nonEmptyString(deletePattern))
        {
            holidayMapper.delete(deletePattern);
        }

        //插入數(shù)據(jù)
        for(Map<String,Object> holiday : holidayMap.values())
        {
            holidayMapper.insert(holiday);
        }
    }

    /**
     * 清除今天之前的假期記錄
     */
    public void clearBefore()
    {
        SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
        holidayMapper.clearBefore(format.format(new Date()));
    }

    /**
     * 更新系統(tǒng)配置天數(shù)對(duì)應(yīng)的今日的真實(shí)工作日數(shù)
     */
    public void updateRealDay() throws IOException
    {
        //Redis更新的內(nèi)容自己寫吧
        ...
    }

    /**
     * 獲取指定的工作日的實(shí)際天數(shù)
     *
     * @param workDay
     * @return
     */
    public Integer calculateRealDay(Date startDate,Integer workDay)
    {
        SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
        Calendar calendar=Calendar.getInstance();
        calendar.setTime(startDate);

        //循環(huán)查詢假期數(shù)并增加
        Date curStartDate=startDate;
        calendar.add(Calendar.DATE,workDay.intValue());
        int holidayCount=-1;
        while(holidayCount!=0)
        {
            //查詢假期表
            holidayCount=holidayMapper.count(
                format.format(curStartDate),
                format.format(calendar.getTime()));

            if(holidayCount>0)
            {
                //增加結(jié)果數(shù)
                workDay=workDay.intValue()+holidayCount;
                //轉(zhuǎn)移開始日期和結(jié)束日期
                calendar.add(Calendar.DATE,1);
                curStartDate=calendar.getTime();
                calendar.add(Calendar.DATE,holidayCount-1);
            }
        }

        return workDay;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末票从,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子滨嘱,更是在濱河造成了極大的恐慌峰鄙,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件太雨,死亡現(xiàn)場(chǎng)離奇詭異吟榴,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)囊扳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門吩翻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)兜看,“玉大人,你說(shuō)我怎么就攤上這事狭瞎∠敢疲” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵熊锭,是天一觀的道長(zhǎng)弧轧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)碗殷,這世上最難降的妖魔是什么精绎? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮亿扁,結(jié)果婚禮上捺典,老公的妹妹穿的比我還像新娘。我一直安慰自己从祝,他們只是感情好襟己,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著牍陌,像睡著了一般擎浴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上毒涧,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天贮预,我揣著相機(jī)與錄音,去河邊找鬼契讲。 笑死仿吞,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捡偏。 我是一名探鬼主播唤冈,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼银伟!你這毒婦竟也來(lái)了你虹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤彤避,失蹤者是張志新(化名)和其女友劉穎傅物,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琉预,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡董饰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卒暂。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贮缅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出介却,到底是詐尸還是另有隱情谴供,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布齿坷,位于F島的核電站桂肌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏永淌。R本人自食惡果不足惜崎场,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望遂蛀。 院中可真熱鬧谭跨,春花似錦、人聲如沸李滴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)所坯。三九已至谆扎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芹助,已是汗流浹背堂湖。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留状土,地道東北人无蜂。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蒙谓,于是被迫代替她去往敵國(guó)和親斥季。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,522評(píng)論 25 707
  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說(shuō)閱讀 10,869評(píng)論 6 13
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理彼乌,服務(wù)發(fā)現(xiàn)泻肯,斷路器渊迁,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • 跟上了當(dāng)當(dāng)網(wǎng)圖書打折的尾巴慰照,“滿100減50”、“10元無(wú)門檻優(yōu)惠券”……我總是抵制不住這樣的誘惑琉朽,即使目前我沒有...
    藍(lán)天大海你閱讀 2,768評(píng)論 7 3
  • 你是可愛的毒租,才會(huì)有人愛你;你是美好的,世界才會(huì)美好墅垮。做一個(gè)圓心惕医,與世界和平相處,既不征服也不討好算色,就是理想的生活抬伺。
    醉半壺閱讀 248評(píng)論 0 0