
使用天行数据api,java查询节假日/调休/工作日/周末日期
考虑到次数(实际是给公司节省成本)以及可靠性,最终决定使用天行数据节假日api进行使用,试过阿里云的万易的api,期限以及次数比较鸡肋;二、我使用的框架是alibabacloud,实此功能实际使用到的其实顶多就是一个nacos,其它的倒是使用的很少,controller->service->mybaties/mybaties-plus。加了一个校验,如果查询的今年没有数据的话会优先去把今年的初始化
最近开发遇到检查任务配置工作日类型,包括法定节假日/调休日/周末/工作日类型,因为每年的这些数据是通过国务院发放文件进行得知,不得不用到第三方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,看到会回复
更多推荐
所有评论(0)