从前同事手中接手的项目中,有个考勤系统,它有一个工作日历表,需要导入节假日数据。刚立项的时候因为没有找到稳定的公共接口,数据也不多,更新频率又是一年一次(下一年的节假日是在这一年年底才会公布),所以就直接从找到的某个接口获取到json,处理一下之后手动导入数据库。
临近元旦轮到我维护该系统,我发现之前留下的系统接口仅仅是实现了普通的批量插入,没有解析的逻辑,估计当时是直接导入数据库的。
于是我考虑在该系统的考勤模块中添加一份导入代码,后面如果用的第三方接口不再维护,换接口也只是改个解析逻辑。
接口
首先是找一份可以获取年度节假日的接口,我找到的如下:
其中,如下项目1.7k star,从2019年就开始维护,直到现在,应该可以长期使用:GitHub - NateScarlet/holiday-cn: 📅🇨🇳中国法定节假日数据 自动每日抓取国务院公告
导入
如果这个功能也写个接口写个页面按钮来触发就比较麻烦,也没有必要。我打算实现一个每年手动单次执行、无界面、与主项目功能隔离(平时不启动) 的日历同步工具。
第一个想到的是JUnit的@Test就可以直接执行,但这东西放测试目录里面不伦不类的,是很不好的写法。
接着想到写多一个utils模块,但再次否决,毕竟这比写个接口来调用更麻烦,项目代码里面又会多个冗余的模块。
最后想到可以在同一个模块内,写第二个启动类,这样我使用第二个启动类,就可以直接执行同步代码了,享受和主启动类一样的环境,平时不用的时候直接使用主启动类即可。
向AI了解了一下得知是可行的,于是开始开发。
修改POM.xml
第一步是修改模块内的POM.xml,添加mainClass标签,指定原本的启动类,这样打包之后默认启动的就是该启动类,避免被新的启动类干扰。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <build> <finalName>scheduling-server</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring.boot.version}</version> <configuration> <mainClass>cn.iocoder.cloud.module.scheduling.server.SchedulingServerApplication</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
|
访问接口
说是接口,实际上就是个静态文件。这里通过RestTemplate来进行访问,关键代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(30000); factory.setReadTimeout(60000); RestTemplate restTemplate = new RestTemplate(factory); HttpHeaders headers = new HttpHeaders(); headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); headers.set("Accept", "application/json, text/plain, */*"); headers.set("Connection", "keep-alive"); HttpEntity<Void> requestEntity = new HttpEntity<>(null, headers);
System.out.println("发送请求到:" + HOLIDAY_API_URL.replace("{year}", String.valueOf(TARGET_YEAR))); ResponseEntity<String> responseEntity = restTemplate.exchange( HOLIDAY_API_URL, HttpMethod.GET, requestEntity, String.class, TARGET_YEAR );
String jsonStr = responseEntity.getBody();
|
最终代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
| package cn.iocoder.yudao.module.scheduling;
import cn.iocoder.yudao.module.scheduling.controller.admin.calendar.vo.CalendarSaveReqVO; import cn.iocoder.yudao.module.scheduling.enums.shift.CalendarTypeEnum; import cn.iocoder.yudao.module.scheduling.service.calendar.CalendarService; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.client.RestTemplate;
import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;
@SpringBootApplication public class CalendarSyncApplication {
private static final String HOLIDAY_API_URL = "https://cdn.jsdelivr.net/gh/NateScarlet/holiday-cn@master/{year}.json"; private static final int TARGET_YEAR = 2026;
@Data private static class YearHoliday { private int year; private HolidayDay[] days; }
@Data private static class HolidayDay { private String name; private String date; @JsonProperty("isOffDay") private boolean isOffDay; }
public static void main(String[] args) { ApplicationContext context = SpringApplication.run(CalendarSyncApplication.class, args); System.out.println("Spring容器启动成功,开始同步" + TARGET_YEAR + "年节假日数据...");
try { CalendarService calendarService = context.getBean(CalendarService.class);
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(30000); factory.setReadTimeout(60000); RestTemplate restTemplate = new RestTemplate(factory); HttpHeaders headers = new HttpHeaders(); headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); headers.set("Accept", "application/json, text/plain, */*"); headers.set("Connection", "keep-alive"); HttpEntity<Void> requestEntity = new HttpEntity<>(null, headers);
System.out.println("发送请求到:" + HOLIDAY_API_URL.replace("{year}", String.valueOf(TARGET_YEAR))); ResponseEntity<String> responseEntity = restTemplate.exchange( HOLIDAY_API_URL, HttpMethod.GET, requestEntity, String.class, TARGET_YEAR );
String jsonStr = responseEntity.getBody();
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); YearHoliday yearHoliday = objectMapper.readValue(jsonStr, YearHoliday.class); System.out.println("成功解析JSON数据,年份:" + yearHoliday.getYear());
Map<LocalDate, HolidayDay> holidayDateMap = new HashMap<>(); for (HolidayDay day : yearHoliday.getDays()) { LocalDate date = LocalDate.parse(day.getDate()); holidayDateMap.put(date, day); }
LocalDate startDate = LocalDate.of(TARGET_YEAR, 1, 1); LocalDate endDate = LocalDate.of(TARGET_YEAR, 12, 31); LocalDate currentDate = startDate; List<CalendarSaveReqVO> calendarList = new ArrayList<>();
while (!currentDate.isAfter(endDate)) { CalendarSaveReqVO reqVO = new CalendarSaveReqVO(); HolidayDay holidayDay = holidayDateMap.get(currentDate);
if (holidayDay != null) { if (holidayDay.isOffDay()) { reqVO.setType(CalendarTypeEnum.HOLIDAY.getCode()); reqVO.setRemark(holidayDay.getName()); } else { reqVO.setType(CalendarTypeEnum.WORKDAY.getCode()); reqVO.setRemark("补班(" + holidayDay.getName() + ")"); } } else { DayOfWeek dayOfWeek = currentDate.getDayOfWeek(); if (DayOfWeek.SATURDAY.equals(dayOfWeek) || DayOfWeek.SUNDAY.equals(dayOfWeek)) { reqVO.setType(CalendarTypeEnum.WEEKEND.getCode()); } else { reqVO.setType(CalendarTypeEnum.WORKDAY.getCode()); } }
reqVO.setId(null); reqVO.setDate(LocalDateTime.of(currentDate, LocalTime.MIDNIGHT));
calendarList.add(reqVO);
currentDate = currentDate.plusDays(1); }
Integer result = calendarService.batchCreateCalendars(calendarList); System.out.println(TARGET_YEAR + "年节假日数据同步完成!共处理 " + calendarList.size() + " 天数据,成功插入 " + result + " 条");
} catch (Exception e) { System.err.println("数据同步失败,异常信息:" + e.getMessage()); e.printStackTrace(); } finally { System.out.println("开始关闭Spring容器..."); int exitCode = SpringApplication.exit(context, () -> 0); System.exit(exitCode); } }
}
|