Administrator hai 1 ano
pai
achega
64b69d9fb0

+ 10 - 0
ruoyi-common/pom.xml

@@ -125,7 +125,17 @@
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+            <version>4.10.0</version>
+        </dependency>
 
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.16.2</version>
+        </dependency>
     </dependencies>
 
 </project>

+ 324 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/ChinaHolidaysUtils.java

@@ -0,0 +1,324 @@
+package com.ruoyi.common.utils;
+
+
+
+import com.ruoyi.common.utils.http.HttpUtils;
+import okhttp3.*;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+import java.io.*;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @Author: tjf
+ * @Date: 2023/11/2 9:20
+ * @Describe:
+ */
+public class ChinaHolidaysUtils {
+    /**
+     * 国务院发布的节假日安排的通知 保存的文件路径
+     */
+    private static final String HOLIDAY_NOTICES_FILE_PATH = ChinaHolidaysUtils.class.getResource("/").getPath()+"国务院发布的节假日安排的通知/";
+    /**
+     * 国务院文件搜索地址
+     */
+    private static final String GOV_URL = "https://sousuo.www.gov.cn/sousuo/search.shtml?code=17da70961a7&dataTypeId=107&";
+
+    private static Set<String> publicHolidays = new ConcurrentSkipListSet<>();
+    private static Set<String> oxenHorseDays = new ConcurrentSkipListSet<>();
+
+    public static void main(String[] args) {
+        try {
+            //System.out.println(isOxenHorseDays("2023-12-30"));
+            System.out.println(isPublicHolidays("2024-12-30"));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        System.out.println("publicHolidays: " + publicHolidays);
+        System.out.println("oxenHorseDays: " + oxenHorseDays);
+    }
+
+
+    /**
+     * 是否为调休补班日
+     * @param localDate
+     * @return
+     * @throws IOException
+     */
+    public static boolean isOxenHorseDays(String localDate) throws IOException {
+        if(oxenHorseDays.isEmpty()){
+            getDays(localDate, publicHolidays, oxenHorseDays);
+        }
+        return oxenHorseDays.contains(localDate);
+    }
+
+    /**
+     * 是否为法定节假日
+     * @param localDate
+     * @return
+     * @throws IOException
+     */
+    public static boolean isPublicHolidays(String localDate) throws IOException {
+        if(publicHolidays.isEmpty()){
+            getDays(localDate, publicHolidays, oxenHorseDays);
+        }
+        return publicHolidays.contains(localDate);
+    }
+
+    private static synchronized void getDays(String localDate, Set<String> publicHolidays, Set<String> oxenHorseDays) throws IOException {
+        //获取xxx年的节假日数据
+        String year = null;
+        if(!StringUtils.isEmpty(localDate)){
+            year = String.valueOf(LocalDate.parse(localDate).getYear());
+        }else{
+            year = String.valueOf(LocalDate.now().getYear());
+        }
+        //先通过缓存文件,否则使用http获取
+        String html = getHtmlByCacheFiles(year);
+        if(html == null){
+            html = getHtmlByHttp(year);
+        }
+        Document doc = Jsoup.parse(html);
+        Element content = doc.select("div.b12c.pages_content").first();
+        Elements paragraphs = content.select("p");
+        for (Element p : paragraphs) {
+            String text = p.text();
+            if (text.contains("、") && text.contains(":") && text.contains("。") && text.contains("放假")) {
+                text = text.substring(text.indexOf(":")+1);
+                String[] sentences = text.split("。");
+                for (String sentence : sentences) {
+                    if (sentence.contains("放假")) {
+                        String t = sentence.split("放假")[0];
+                        if (t.contains("至")) {
+                            String start = t.split("至")[0];
+                            String startDay=null, startMonth=null, startYear=null;
+                            if(start.contains("日") || start.contains("月") || start.contains("年")){
+                                startDay = getDigit(start, "日");
+                                startMonth = getDigit(start, "月");
+                                startYear = getDigit(start, "年");
+                            }
+                            LocalDate startDate = parseDate(startYear==null?year:startYear, startMonth, startDay);
+                            String end = t.split("至")[1];
+                            String endDay=null, endMonth=null, endYear=null;
+                            if(end.contains("日") || end.contains("月") || end.contains("年")){
+                                endDay = getDigit(end, "日");
+                                endMonth = getDigit(end, "月");
+                                endYear = getDigit(end, "年");
+                            }
+                            LocalDate endDate = parseDate(endYear==null?(startYear==null?year:startYear):endYear, endMonth==null?startMonth:endMonth, endDay);
+                            for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
+                                publicHolidays.add(date.toString());
+                            }
+                        } else {
+                            String tDay=null, tMonth=null, tYear=null;
+                            if(t.contains("日") || t.contains("月") || t.contains("年")){
+                                tDay = getDigit(t, "日");
+                                tMonth = getDigit(t, "月");
+                                tYear = getDigit(t, "年");
+                            }
+                            LocalDate date = parseDate(tYear==null? year:tYear, tMonth, tDay);
+                            publicHolidays.add(date.toString());
+                        }
+                    }
+                    if (sentence.contains("上班")) {
+                        String t = sentence.split("上班")[0];
+                        if (sentence.contains("、")) {
+                            String[] dates = sentence.split("、");
+                            for (String dateStr : dates) {
+                                String tDay=null, tMonth=null, tYear=null;
+                                if(dateStr.contains("日") || dateStr.contains("月") || dateStr.contains("年")){
+                                    tDay = getDigit(dateStr, "日");
+                                    tMonth = getDigit(dateStr, "月");
+                                    tYear = getDigit(dateStr, "年");
+                                }
+                                LocalDate date = parseDate(tYear==null? year:tYear, tMonth, tDay);
+                                oxenHorseDays.add(date.toString());
+                            }
+                        }else{
+                            String tDay=null, tMonth=null, tYear=null;
+                            if(t.contains("日") || t.contains("月") || t.contains("年")){
+                                tDay = getDigit(t, "日");
+                                tMonth = getDigit(t, "月");
+                                tYear = getDigit(t, "年");
+                            }
+                            LocalDate date = parseDate(tYear==null? year:tYear, tMonth, tDay);
+                            oxenHorseDays.add(date.toString());
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 模拟人为操作的参数
+     * @param url
+     */
+    private static Request getRequestSetUnifiedHead(String url){
+        Request.Builder builder = new Request.Builder();
+        builder.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36")
+                .header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
+                .header("Host", "www.gov.cn")
+                .header("Referer", "http://sousuo.www.gov.cn/")
+//             .header("Accept-Encoding", "gzip, deflate, br") 造成乱码问题
+                .header("Accept-Language", "zh-CN,zh;q=0.9")
+                .header("Cache-Control", "max-age=0")
+                .header("Connection", "keep-alive")
+                .header("Sec-Fetch-Dest", "document")
+                .header("Sec-Fetch-Mode", "navigate")
+                .header("Sec-Fetch-Site", "cross-site")
+                .header("Sec-Fetch-User", "?1")
+                .header("Upgrade-Insecure-Requests", "1")
+                .header("sec-ch-ua", "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"")
+                .header("sec-ch-ua-mobile", "?0")
+                .header("sec-ch-ua-platform", "Windows");
+        return builder.url(url).build();
+    }
+
+    /**
+     * http get请求
+     * @param client
+     * @param url
+     * @return
+     * @throws IOException
+     */
+    private static String httpGet(OkHttpClient client, String url) throws IOException {
+        Request request = getRequestSetUnifiedHead(url);
+        Response response = client.newCall(request).execute();
+        if (!response.isSuccessful()) {
+            throw new IOException("获取数据失败:" + url);
+        }
+        String html = response.body().string();
+        System.out.println("进行了一次http get请求:" + url);
+        return html;
+    }
+    /**
+     * 通过http获取国务院发布xxxx年的节假日安排的通知
+     * @return
+     * @throws IOException
+     */
+    private static String getHtmlByHttp(String year) throws IOException {
+        OkHttpClient client = new OkHttpClient();
+        String searchWord ="searchWord=" + year + "节假日";
+        String html = httpGet(client, GOV_URL+searchWord);
+        Document doc = Jsoup.parse(html);
+        Elements resList = doc.select("li.res-list");
+        if (!resList.isEmpty()) {
+            Optional<Element> optional = resList.stream().filter(res -> res.text().contains("国务院办公厅关于"+year+"年")).findFirst();
+            if (!optional.isPresent()) {
+                throw new IOException("未获取到"+ year +"年节假日安排的通知:" + GOV_URL);
+            }
+            Element element = optional.get();
+            String linkUrl = element.select("a[href]").attr("abs:href");
+            html = httpGet(client, linkUrl);
+            str2File(html, HOLIDAY_NOTICES_FILE_PATH, year+"节假日安排的通知-源数据", ".html");
+            Document resDoc = Jsoup.parse(html);
+            str2File(html, HOLIDAY_NOTICES_FILE_PATH, resDoc.title(), ".html");
+            return html;
+        }
+        return null;
+    }
+
+    /**
+     * 先通过缓存节假日通知的指定目录中获取 当年的 节假日通知文件
+     * @param year
+     * @return
+     */
+    private static String getHtmlByCacheFiles(String year) {
+        String[] paths = new File(HOLIDAY_NOTICES_FILE_PATH).list();
+        if(paths != null && paths.length > 0){
+            Optional<String> yearPath = Arrays.stream(paths).filter(p -> p.contains(year)).findFirst();
+            if (yearPath.isPresent()){
+                return file2Str(HOLIDAY_NOTICES_FILE_PATH + yearPath.get());
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * 根据年月日字符转为 yyyy-M-d 格式的LocalDate
+     * @param year
+     * @param month
+     * @param day
+     * @return
+     */
+    private static LocalDate parseDate(String year, String month, String day) {
+        String dateStr = year + "-" + month + "-" + day;
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-M-d");
+        return LocalDate.parse(dateStr, formatter);
+    }
+
+    /**
+     * 将字符串内容转为文件存到指定路径下
+     * @param str
+     * @param filePath
+     */
+    public static void str2File(String str, String filePath, String fileName, String fileSuffix) throws IOException {
+        File file = new File(filePath + fileName + fileSuffix);
+        if (!file.getParentFile().exists()) {
+            boolean created = file.getParentFile().mkdirs();
+            if (!created) {
+                throw new IOException("文件路径创建失败");
+            }
+        }
+        try (FileWriter writer = new FileWriter(file)) {
+            writer.write(str);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 将指定路径的文件转为字符串
+     * @param filePath
+     * @return
+     */
+    private static String file2Str(String filePath) {
+        File file = new File(filePath);
+        try (FileReader reader = new FileReader(file)) {
+            char[] buffer = new char[(int) file.length()];
+            reader.read(buffer);
+            return new String(buffer);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 获取指定字符串前的数字
+     */
+    private static final Pattern PATTERN = Pattern.compile("[^\\d]");
+    private static String getDigit(String content, String targetStr) {
+        if(!content.contains(targetStr)){
+            return null;
+        }
+        content = content.substring(0, content.indexOf(targetStr));
+        StringBuffer sb = new StringBuffer(content);
+        content = sb.reverse().toString();
+        //使用正则表达式匹配第一个非数字
+        Matcher matcher = PATTERN.matcher(content);
+        if (matcher.find()) {
+            content = content.substring(0, matcher.start());
+            sb = new StringBuffer(content);
+            return sb.reverse().toString();
+        }
+        //是否为数字
+        if(NumberUtils.isCreatable(content)){
+            return new StringBuffer(content).reverse().toString();
+        }
+        return null;
+    }
+
+
+}

+ 2 - 2
ruoyi-generator/src/main/resources/vm/java/controller.java.vm

@@ -96,7 +96,7 @@ public class ${ClassName}Controller extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")
     @Log(title = "${functionName}", businessType = BusinessType.UPDATE)
-    @PutMapping
+    @PostMapping("/put")
     public AjaxResult edit(@RequestBody ${ClassName} ${className})
     {
         return toAjax(${className}Service.update${ClassName}(${className}));
@@ -107,7 +107,7 @@ public class ${ClassName}Controller extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')")
     @Log(title = "${functionName}", businessType = BusinessType.DELETE)
-	@DeleteMapping("/{${pkColumn.javaField}s}")
+	@GetMapping("/delete/{${pkColumn.javaField}s}")
     public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s)
     {
         return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s));