使用天行数据api,java查询节假日/调休/工作日/周末日期

 

最近开发遇到检查任务配置工作日类型,包括法定节假日/调休日/周末/工作日类型,因为每年的这些数据是通过国务院发放文件进行得知,不得不用到第三方api进行查询;考虑到次数(实际是给公司节省成本)以及可靠性,最终决定使用天行数据节假日api进行使用,试过阿里云的万易的api,期限以及次数比较鸡肋;

废话不多说,直接上代码,此帖子结合nacos以及表进行编写,可以根据自己的实际使用场景进行更改!

加了一个校验,如果查询的今年没有数据的话会优先去把今年的初始化,如果今年的数据完整会去初始化明年的数据(如果存在的话)  业务不同的可以进行更改

一、在天行数据官网注册并申请节假日api,得到自己账号的key

节假日地址:https://www.tianapi.com/apiview/139

二、我使用的框架是alibabacloud,实此功能实际使用到的其实顶多就是一个nacos,其它的倒是使用的很少,controller->service->mybaties/mybaties-plus

api调用返回结果格式为:

{
  "code": 200,
  "msg": "success",
  "result": {
    "update": true,
    "list": [
      {
        "holiday": "1月1号",
        "name": "元旦节",
        "vacation": "2023-12-30|2023-12-31|2024-01-01",
        "remark": "",
        "wage": "2024-01-01",
        "start": 0,
        "now": 0,
        "end": 2,
        "tip": "1月1日放假,与周末连休,共三天。",
        "rest": "2023年12月28日至12月29日请假2天,与周末连休可拼5天小长假。"
      },
      {
        "holiday": "2月10号",
        "name": "春节",
        "vacation": "2024-02-10|2024-02-11|2024-02-12|2024-02-13|2024-02-14|2024-02-15|2024-02-16|2024-02-17",
        "remark": "2024-02-04|2024-02-18",
        "wage": "2024-02-10|2024-02-11|2024-02-12",
        "start": 0,
        "now": 0,
        "end": 7,
        "tip": "2月10日至17日放假调休,共8天。2月4日(星期日)、2月18日(星期日)上班。鼓励各单位结合带薪年休假等制度落实,安排职工在除夕(2月9日)休息。",
        "rest": "2月8日至2月9日请假2天,与春节连休可拼10天长假。"
      },
      {
        "holiday": "4月4号",
        "name": "清明节",
        "vacation": "2024-04-04|2024-04-05|2024-04-06",
        "remark": "2024-04-07",
        "wage": "2024-04-04",
        "start": 0,
        "now": 0,
        "end": 2,
        "tip": "4月4日至6日放假调休,共3天。4月7日(星期日)上班。",
        "rest": "4月3日和4月7日请假2天,与清明节连休可拼5天小长假。"
      },
      {
        "holiday": "5月1号",
        "name": "劳动节",
        "vacation": "2024-05-01|2024-05-02|2024-05-03|2024-05-04|2024-05-05",
        "remark": "2024-04-28|2024-05-11",
        "wage": "2024-05-01",
        "start": 0,
        "now": 0,
        "end": 4,
        "tip": "5月1日至5日放假调休,共5天。4月28日(星期日)、5月11日(星期六)上班。",
        "rest": "4月28日至4月30日请假3天,周六与劳动节连休可拼9天长假。"
      },
      {
        "holiday": "6月10号",
        "name": "端午节",
        "vacation": "2024-06-08|2024-06-09|2024-06-10",
        "remark": "",
        "wage": "2024-06-10",
        "start": 0,
        "now": 0,
        "end": 2,
        "tip": "6月10日放假,与周末连休,共3天。",
        "rest": "6月6日至6月7日请假2天,与端午节连休可拼5天小长假。"
      },
      {
        "holiday": "9月15号",
        "name": "中秋节",
        "vacation": "2024-09-15|2024-09-16|2024-09-17",
        "remark": "2024-09-14",
        "wage": "2024-09-17",
        "start": 0,
        "now": 0,
        "end": 2,
        "tip": "9月15日至17日放假调休,共3天。9月14日(星期六)上班。",
        "rest": "9月13日至9月14日请假2天,与周日连休可拼5天小长假。"
      },
      {
        "holiday": "10月1号",
        "name": "国庆节",
        "vacation": "2024-10-01|2024-10-02|2024-10-03|2024-10-04|2024-10-05|2024-10-06|2024-10-07",
        "remark": "2024-09-29|2024-10-12",
        "wage": "2024-10-01|2024-10-02|2024-10-03",
        "start": 0,
        "now": 0,
        "end": 6,
        "tip": "10月1日至7日放假调休,共7天。9月29日(星期日)、10月12日(星期六)上班。",
        "rest": "9月29日至9月30号请假2天,周六与国庆节连休可拼10天长假。"
      }
    ]
  }
}

直接上代码 

controller:

package safe.cloud.luntai.controller;
import com.github.pagehelper.PageInfo;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import safe.cloud.luntai.dto.DateInfoDTO;
import safe.cloud.luntai.entity.DateInfo;
import safe.cloud.luntai.model.BasicResponse;
import safe.cloud.luntai.model.BasicTable;
import safe.cloud.luntai.service.DateInfoService;
import safe.cloud.luntai.vo.DateInfoVO;
import javax.annotation.Resource;
/**
 * 

* 日期详情表(节假日/调休/周末/工作日) 前端控制器 *

* * @author zjc * @since 2024-04-19 16:02:51 */ @RestController @RequestMapping("/dateInfo") public class DateInfoController { @Resource private DateInfoService dateInfoService; /** * 获取全年日期(标注节假日/调休日/周末/工作日) * @return 返回封装 */ @PostMapping("/synDateInfo") public BasicResponse synDateInfo() { boolean operateState = dateInfoService.synDateInfo(); return BasicResponse.createSuccess(operateState); } }

service:

package safe.cloud.luntai.service;
import com.github.pagehelper.PageInfo;
import safe.cloud.luntai.entity.DateInfo;
import safe.cloud.luntai.vo.DateInfoVO;
import safe.cloud.luntai.dto.DateInfoDTO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.io.Serializable;
/**
 * 

* 日期详情表(节假日/调休/周末/工作日) 服务类 *

* * @author zjc * @since 2024-04-19 16:02:51 */ public interface DateInfoService extends IService { /** * 获取全年日期(标注节假日/调休日/周末/工作日) * @return 返回封装 */ boolean synDateInfo(); }

impl:

package safe.cloud.luntai.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import safe.cloud.luntai.dto.DateInfoDTO;
import safe.cloud.luntai.entity.DateInfo;
import safe.cloud.luntai.enums.RiskInfoEnums;
import safe.cloud.luntai.exception.CustomServerException;
import safe.cloud.luntai.mapper.DateInfoMapper;
import safe.cloud.luntai.service.DateInfoService;
import safe.cloud.luntai.utils.NacosCommonUtils;
import safe.cloud.luntai.utils.PageUtils;
import safe.cloud.luntai.vo.DateInfoVO;
import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
 * 

* 日期详情表(节假日/调休/周末/工作日) 服务实现类 *

* * @author zjc * @since 2024-04-19 16:02:51 */ @Slf4j @Service public class DateInfoServiceImpl extends ServiceImpl implements DateInfoService { @Resource NacosCommonUtils nacosCommonUtils; /** * 获取全年日期(标注节假日/调休日/周末/工作日) * @return 返回封装 */ @Override @Transactional(rollbackFor = Exception.class) public boolean synDateInfo() { int year = LocalDate.now().getYear(); //如果在库里面今年或者明年已经存在数据 那么就直接返回不进行操作 long checkNowYearCount = this.count(new LambdaQueryWrapper().eq(DateInfo::getYear, year)); long checkTomorrowYearCount = this.count(new LambdaQueryWrapper().eq(DateInfo::getYear, year + 1)); if((checkNowYearCount == 365 || checkNowYearCount == 366) && (checkTomorrowYearCount == 365 || checkTomorrowYearCount == 366) ){ log.info("系统已存在对应数据,无需再次初始化!"); return true; } String doYear; //库里面查询今年的数据是否存在 如果不存在,那么就先进行初始化今年的数据 if(checkNowYearCount == 365 || checkNowYearCount == 366){ doYear = String.valueOf(year + 1); }else{ doYear = String.valueOf(year); } //获取指定年份的ListList datesOfYear = getDatesOfYear(doYear); Assert.isTrue(CollectionUtil.isNotEmpty(datesOfYear), "获取指定年份的日期为空"); //获取指定年份的周末 List yearWeekends = findYearWeekends(doYear); Assert.isTrue(CollectionUtil.isNotEmpty(yearWeekends), "获取指定年份的周末日期为空"); //如果不为0,先执行一次删除操作,避免因为某些原因存入脏数据 long checkDoYearCount = this.count(new LambdaQueryWrapper().eq(DateInfo::getYear, doYear)); if(checkDoYearCount != 0){ this.remove(new LambdaQueryWrapper().eq(DateInfo::getYear,doYear)); } String tianApiData = ""; try { URL url = new URL( nacosCommonUtils.getTianXingApiUrl()); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); conn.setDoOutput(true); conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); OutputStream outputStream = conn.getOutputStream(); String key = nacosCommonUtils.getTianXingApiKey(); String type = "1"; String content = "key=" + key + "&date=" + doYear + "&type=" + type; outputStream.write(content.getBytes()); outputStream.flush(); outputStream.close(); InputStream inputStream = conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader (inputStream,"utf-8"); BufferedReader bufferedReader = new BufferedReader (inputStreamReader); StringBuilder tianapi = new StringBuilder(); String temp = null; while ( null != (temp = bufferedReader.readLine())){ tianapi.append(temp); } tianApiData = tianapi.toString(); inputStream.close(); } catch (IOException e) { e.printStackTrace(); } // 使用 JSONUtil 将 JSON 字符串转换为 Map 对象 JSONObject jsonObject = JSONUtil.parseObj(tianApiData); int code = jsonObject.getInt("code"); RiskInfoEnums.TianXingApiCodeEnums tianXingApiCodeEnums = RiskInfoEnums.TianXingApiCodeEnums.getByCode(code); if(Boolean.FALSE.equals(tianXingApiCodeEnums.getCode() == 200)){ throw new CustomServerException(tianXingApiCodeEnums.getMsg()); } Map resultMap = jsonObject.getJSONObject("result"); if(CollectionUtil.isEmpty(resultMap)){ log.info("==============================="); log.info("响应body未拿到!"); return false; } List> list = (List>) resultMap.get("list"); if(CollectionUtil.isEmpty(list)){ log.info("==============================="); log.info("通过api查询指定年份未查到日期数据!"); return false; } //节假日 List holidayList = new ArrayList<>(); //调休日 List compensatoryLeaveList = new ArrayList<>(); for (Map item:list) { String holidayString = item.get("vacation"); List holidayItem = splitStringToLocalDate(holidayString); holidayList.addAll(holidayItem); //调休日 String compensatoryString = item.get("remark"); List compensatoryItem = splitStringToLocalDate(compensatoryString); compensatoryLeaveList.addAll(compensatoryItem); } //处理数据 List collect = datesOfYear.stream().map(value -> { DateInfo dateInfo = new DateInfo(); dateInfo.setDayOfWeek(dayOfWeekValue(value)); dateInfo.setYear(value.getYear()); dateInfo.setDateInfo(value); //查询是否是周末 if (yearWeekends.contains(value)) { dateInfo.setDayTypeCode(RiskInfoEnums.DateTypeEnums.WEEKEND.getCode()); dateInfo.setDayTypeMsg(RiskInfoEnums.DateTypeEnums.WEEKEND.getMsg()); } //查询是否节假日 if (holidayList.contains(value)) { dateInfo.setDayTypeCode(RiskInfoEnums.DateTypeEnums.HOLIDAY.getCode()); dateInfo.setDayTypeMsg(RiskInfoEnums.DateTypeEnums.HOLIDAY.getMsg()); } //查询是否是调休 if (compensatoryLeaveList.contains(value)) { dateInfo.setDayTypeCode(RiskInfoEnums.DateTypeEnums.COMPENSATORY.getCode()); dateInfo.setDayTypeMsg(RiskInfoEnums.DateTypeEnums.COMPENSATORY.getMsg()); } //工作日 即:不在节假日,调休,周末 if(Boolean.FALSE.equals(yearWeekends.contains(value) || holidayList.contains(value) || compensatoryLeaveList.contains(value))) { dateInfo.setDayTypeCode(RiskInfoEnums.DateTypeEnums.WORKDAY.getCode()); dateInfo.setDayTypeMsg(RiskInfoEnums.DateTypeEnums.WORKDAY.getMsg()); } return dateInfo; }).collect(Collectors.toList()); //保存在库里面 return super.saveBatch(collect); } public static List splitStringToLocalDate(String dateString){ if(StrUtil.isBlank(dateString)){ return Collections.emptyList(); } // 拆分日期字符串 String[] dates = dateString.split("\\|"); // 创建一个 List 对象来存储 LocalDate List dateList = new ArrayList<>(); // 遍历拆分后的日期字符串数组,并将每个日期字符串解析为 LocalDate 添加到 List 中 for (String dateStr : dates) { LocalDate date = LocalDate.parse(dateStr); dateList.add(date); } return dateList; } /** * 查询指定年份的List * @param year * @return */ public static List getDatesOfYear(String year) { if(StrUtil.isBlank(year)){ return Collections.emptyList(); } List datesOfYear = new ArrayList<>(); Integer yearInt = Integer.valueOf(year); // 指定年份的第一天 LocalDate startDate = LocalDate.of(yearInt, 1, 1); // 指定年份的最后一天 LocalDate endDate = LocalDate.of(yearInt, 12, 31); // 循环生成从第一天到最后一天的所有日期,并添加到列表中 LocalDate currentDate = startDate; while (!currentDate.isAfter(endDate)) { datesOfYear.add(currentDate); // 加一天 currentDate = currentDate.plusDays(1); } return datesOfYear; } /** * 转换周几 * @param date * @return */ public static String dayOfWeekValue(LocalDate date){ // 获取星期几的值(1表示星期一,7表示星期日) int dayOfWeekValue = date.getDayOfWeek().getValue(); // 将星期几的值转换为相应的字符串表示 String result = null; switch (dayOfWeekValue) { case 1: result ="周一"; break; case 2: result = "周二"; break; case 3: result ="周三"; break; case 4: result ="周四"; break; case 5: result = "周五"; break; case 6: result = "周六"; break; case 7: result = "周日"; break; default: break; } return result; } /** * 获取指定年份的所有的周末list * @param year * @return */ public static List findYearWeekends(String year){ if(StrUtil.isBlank(year)){ return Collections.emptyList(); } LocalDate startDate = LocalDate.of(Integer.parseInt(year), 1, 1); LocalDate endDate = LocalDate.of(Integer.parseInt(year), 12, 31); return IntStream.rangeClosed(1, endDate.getDayOfYear()) .mapToObj(startDate::plusDays) .filter(date -> date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY) .collect(Collectors.toList()); } }

mapper:

package safe.cloud.luntai.mapper;
import safe.cloud.luntai.entity.DateInfo;
import safe.cloud.luntai.dto.DateInfoDTO;
import safe.cloud.luntai.vo.DateInfoVO;
import safe.cloud.luntai.vo.DateInfoVO;
import safe.cloud.luntai.dto.DateInfoDTO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * 

* 日期详情表(节假日/调休/周末/工作日) Mapper 接口 *

* * @author zjc * @since 2024-04-19 16:02:51 */ @Mapper public interface DateInfoMapper extends BaseMapper { }

nacosutil:可以直接自己写死测试,或者更改自己的nacos值配置,关于nacos不再做过多的赘述

package safe.cloud.luntai.utils;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
/**
 * 
 * @ClassName NacosCommonUtils
 * @date 2022/12/24 15:51
 * @Version 1.0
 */
@Component
@RefreshScope
@Getter
public class NacosCommonUtils {
    /**
     * 天行数据节假日api   url
     */
    @Value("${tianXingApi.url}")
    String tianXingApiUrl;
    /**
     * 天行数据节假日api  key
     */
    @Value("${tianXingApi.key}")
    String tianXingApiKey;
}

enums:

package safe.cloud.luntai.enums;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import safe.cloud.luntai.exception.CustomClientException;
import java.util.Arrays;
import java.util.Objects;
public class RiskInfoEnums {
   
    @AllArgsConstructor
    public enum DateTypeEnums {
        /**
         * 日期类型
         */
        HOLIDAY("1","假期"),
        COMPENSATORY("2","调休"),
        WEEKEND("3","周末"),
        WORKDAY("4","工作日"),
        ;
        @Getter
        String code;
        @Getter
        String msg;
        public static RiskInfoEnums.DateTypeEnums getByCode(String code) {
            if (StrUtil.isBlank(code)) {
                return null;
            }
            return Arrays.stream(RiskInfoEnums.DateTypeEnums.values())
                    .filter(f -> f.code.equals(code))
                    .findFirst()
                    .orElseThrow(() -> new CustomClientException("暂不支持的业务类型"));
        }
    }
	
	
	
	 @AllArgsConstructor
    public enum TianXingApiCodeEnums {
        /**
         * 天行第三方api调用结果状态码
         */
        NBCW(100, "内部服务器错误"),
        APIXX(110, "当前API已下线"),
        APIWH(120, "API暂时维护中"),
        APICX(130, "API调用频率超限"),
        APIMYQX(140, "API没有调用权限"),
        APICSBZ(150, "API可用次数不足"),
        ZHWSQAPI(160, "账号未申请该API"),
        REFERERSX(170, "Referer请求来源受限"),
        IPSX(180, "IP请求来源受限"),
        KEYBKY(190, "当前key不可用"),
        KEYCW(230, "key错误或为空"),
        QSKEY(240, "缺少key参数"),
        SJWK(250, "数据返回为空"),
        CSWK(260, "参数值不得为空"),
        CSBFHYQ(270, "参数值不符合要求"),
        QSBYCS(280, "缺少必要的参数"),
        CGCD(290, "超过最大输入限制"),
        SUCCESS(200, "请求成功"),
        ;
        @Getter
        private final int code;
        @Getter
        private final String msg;
        public static TianXingApiCodeEnums getByCode(int code) {
            if (ObjectUtil.isNull(code)) {
                return null;
            }
            return Arrays.stream(TianXingApiCodeEnums.values())
                    .filter(f ->f.code == code).findFirst().orElse(null);
        }
    }
}

状态响应码以及日期类型枚举:

mysql的创表语句:

CREATE TABLE `tb_date_info` (
  `auto_id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `date_info` date DEFAULT NULL COMMENT '日期',
  `day_type_code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类型(1:假期,2:调休,3:周末,4:工作日) code',
  `year` year DEFAULT NULL COMMENT '年份',
  `day_of_week` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '周几',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `create_user` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `update_user` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
  `day_type_msg` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类型(1:假期,2:调休,3:周末,4:工作日) 中文',
  PRIMARY KEY (`auto_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='日期详情表(节假日/调休/周末/工作日)';

最后执行就可以了,根据自己的业务调整使用定时任务还是powerJob去跑,这里就不过多演示,有疑问的可以进行留言或者加我企鹅2466961646,看到会回复