使用天行数据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;

/**
 * <p>
 * 日期详情表(节假日/调休/周末/工作日) 前端控制器
 * </p>
 *
 * @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;

/**
 * <p>
 * 日期详情表(节假日/调休/周末/工作日) 服务类
 * </p>
 *
 * @author zjc
 * @since 2024-04-19 16:02:51
 */
public interface DateInfoService extends IService<DateInfo> {

  
    /**
     * 获取全年日期(标注节假日/调休日/周末/工作日)
     * @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;

/**
 * <p>
 * 日期详情表(节假日/调休/周末/工作日) 服务实现类
 * </p>
 *
 * @author zjc
 * @since 2024-04-19 16:02:51
 */
@Slf4j
@Service
public class DateInfoServiceImpl extends ServiceImpl<DateInfoMapper, DateInfo> 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<DateInfo>().eq(DateInfo::getYear, year));
		long checkTomorrowYearCount = this.count(new LambdaQueryWrapper<DateInfo>().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);
		}

		//获取指定年份的List<LocalDate>
		List<LocalDate> datesOfYear = getDatesOfYear(doYear);
		Assert.isTrue(CollectionUtil.isNotEmpty(datesOfYear), "获取指定年份的日期为空");
		//获取指定年份的周末
		List<LocalDate> yearWeekends = findYearWeekends(doYear);
		Assert.isTrue(CollectionUtil.isNotEmpty(yearWeekends), "获取指定年份的周末日期为空");


		//如果不为0,先执行一次删除操作,避免因为某些原因存入脏数据
		long checkDoYearCount = this.count(new LambdaQueryWrapper<DateInfo>().eq(DateInfo::getYear, doYear));
		if(checkDoYearCount != 0){
			this.remove(new LambdaQueryWrapper<DateInfo>().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<String, Object> resultMap = jsonObject.getJSONObject("result");
		if(CollectionUtil.isEmpty(resultMap)){
			log.info("===============================");
			log.info("响应body未拿到!");
			return false;
		}
		List<Map<String, String>> list = (List<Map<String, String>>) resultMap.get("list");
		if(CollectionUtil.isEmpty(list)){
			log.info("===============================");
			log.info("通过api查询指定年份未查到日期数据!");
			return false;
		}
		//节假日
		List<LocalDate> holidayList = new ArrayList<>();
		//调休日
		List<LocalDate> compensatoryLeaveList = new ArrayList<>();
		for (Map<String, String> item:list) {
			String holidayString = item.get("vacation");
			List<LocalDate> holidayItem = splitStringToLocalDate(holidayString);
			holidayList.addAll(holidayItem);
			//调休日
			String compensatoryString = item.get("remark");
			List<LocalDate> compensatoryItem = splitStringToLocalDate(compensatoryString);
			compensatoryLeaveList.addAll(compensatoryItem);
		}

		//处理数据
		List<DateInfo> 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<LocalDate> splitStringToLocalDate(String dateString){
		if(StrUtil.isBlank(dateString)){
			return Collections.emptyList();
		}
		// 拆分日期字符串
		String[] dates = dateString.split("\\|");
		// 创建一个 List 对象来存储 LocalDate
		List<LocalDate> dateList = new ArrayList<>();
		// 遍历拆分后的日期字符串数组,并将每个日期字符串解析为 LocalDate 添加到 List 中
		for (String dateStr : dates) {
			LocalDate date = LocalDate.parse(dateStr);
			dateList.add(date);
		}
		return dateList;
	}

	/**
	 * 查询指定年份的List<LocalDate>
	 * @param year
	 * @return
	 */
	public static List<LocalDate> getDatesOfYear(String year) {
		if(StrUtil.isBlank(year)){
			return Collections.emptyList();
		}
		List<LocalDate> 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<LocalDate> 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;

/**
 * <p>
 * 日期详情表(节假日/调休/周末/工作日) Mapper 接口
 * </p>
 *
 * @author zjc
 * @since 2024-04-19 16:02:51
 */
@Mapper
public interface DateInfoMapper extends BaseMapper<DateInfo> {

   

}

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,看到会回复

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值