From 16923cc86a036d3a53b152024275e6d1b498e1ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 28 Mar 2025 22:51:32 +0800 Subject: [PATCH 001/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81excel=E6=96=B9=E6=B3=95=E6=8A=9B=E5=87=BAjson=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/common/excel/utils/ExcelUtil.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java index 0e9569677..5c5e5ccca 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java @@ -23,7 +23,6 @@ import org.dromara.common.excel.handler.DataWriteHandler; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.List; import java.util.Map; @@ -84,7 +83,6 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { try { - resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, false, os, null); } catch (IOException e) { @@ -103,7 +101,6 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response, List options) { try { - resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, false, os, options); } catch (IOException e) { @@ -122,7 +119,6 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response) { try { - resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, merge, os, null); } catch (IOException e) { @@ -142,7 +138,6 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response, List options) { try { - resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, merge, os, options); } catch (IOException e) { @@ -186,6 +181,12 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, OutputStream os, List options) { + if (CollUtil.isEmpty(list)) { + throw new IllegalArgumentException("数据为空"); + } + if (os instanceof HttpServletResponse response) { + resetResponse(sheetName, response); + } ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) .autoCloseStream(false) // 自动适配 @@ -215,9 +216,8 @@ public class ExcelUtil { */ public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { try { - resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); - exportTemplate(data, templatePath, os); + exportTemplate(data, filename, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } @@ -232,10 +232,13 @@ public class ExcelUtil { * @param data 模板需要的数据 * @param os 输出流 */ - public static void exportTemplate(List data, String templatePath, OutputStream os) { + public static void exportTemplate(List data, String filename, String templatePath, OutputStream os) { if (CollUtil.isEmpty(data)) { throw new IllegalArgumentException("数据为空"); } + if (os instanceof HttpServletResponse response) { + resetResponse(filename, response); + } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) @@ -267,7 +270,7 @@ public class ExcelUtil { try { resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); - exportTemplateMultiList(data, templatePath, os); + exportTemplateMultiList(data, filename, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } @@ -285,9 +288,8 @@ public class ExcelUtil { */ public static void exportTemplateMultiSheet(List> data, String filename, String templatePath, HttpServletResponse response) { try { - resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); - exportTemplateMultiSheet(data, templatePath, os); + exportTemplateMultiSheet(data, filename, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } @@ -302,10 +304,13 @@ public class ExcelUtil { * @param data 模板需要的数据 * @param os 输出流 */ - public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { + public static void exportTemplateMultiList(Map data, String filename, String templatePath, OutputStream os) { if (CollUtil.isEmpty(data)) { throw new IllegalArgumentException("数据为空"); } + if (os instanceof HttpServletResponse response) { + resetResponse(filename, response); + } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) @@ -336,10 +341,13 @@ public class ExcelUtil { * @param data 模板需要的数据 * @param os 输出流 */ - public static void exportTemplateMultiSheet(List> data, String templatePath, OutputStream os) { + public static void exportTemplateMultiSheet(List> data, String filename, String templatePath, OutputStream os) { if (CollUtil.isEmpty(data)) { throw new IllegalArgumentException("数据为空"); } + if (os instanceof HttpServletResponse response) { + resetResponse(filename, response); + } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) @@ -366,7 +374,7 @@ public class ExcelUtil { /** * 重置响应体 */ - private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException { + private static void resetResponse(String sheetName, HttpServletResponse response) { String filename = encodingFilename(sheetName); FileUtils.setAttachmentResponseHeader(response, filename); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); From 7129ad4fac42e43980524f2942c6326b403481f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 28 Mar 2025 23:11:11 +0800 Subject: [PATCH 002/121] =?UTF-8?q?Revert=20"update=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=20=E6=94=AF=E6=8C=81excel=E6=96=B9=E6=B3=95=E6=8A=9B=E5=87=BAj?= =?UTF-8?q?son=E5=BC=82=E5=B8=B8"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 16923cc86a036d3a53b152024275e6d1b498e1ba. --- .../dromara/common/excel/utils/ExcelUtil.java | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java index 5c5e5ccca..0e9569677 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java @@ -23,6 +23,7 @@ import org.dromara.common.excel.handler.DataWriteHandler; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.List; import java.util.Map; @@ -83,6 +84,7 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { try { + resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, false, os, null); } catch (IOException e) { @@ -101,6 +103,7 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response, List options) { try { + resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, false, os, options); } catch (IOException e) { @@ -119,6 +122,7 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response) { try { + resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, merge, os, null); } catch (IOException e) { @@ -138,6 +142,7 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response, List options) { try { + resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, merge, os, options); } catch (IOException e) { @@ -181,12 +186,6 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, OutputStream os, List options) { - if (CollUtil.isEmpty(list)) { - throw new IllegalArgumentException("数据为空"); - } - if (os instanceof HttpServletResponse response) { - resetResponse(sheetName, response); - } ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) .autoCloseStream(false) // 自动适配 @@ -216,8 +215,9 @@ public class ExcelUtil { */ public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { try { + resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); - exportTemplate(data, filename, templatePath, os); + exportTemplate(data, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } @@ -232,13 +232,10 @@ public class ExcelUtil { * @param data 模板需要的数据 * @param os 输出流 */ - public static void exportTemplate(List data, String filename, String templatePath, OutputStream os) { + public static void exportTemplate(List data, String templatePath, OutputStream os) { if (CollUtil.isEmpty(data)) { throw new IllegalArgumentException("数据为空"); } - if (os instanceof HttpServletResponse response) { - resetResponse(filename, response); - } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) @@ -270,7 +267,7 @@ public class ExcelUtil { try { resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); - exportTemplateMultiList(data, filename, templatePath, os); + exportTemplateMultiList(data, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } @@ -288,8 +285,9 @@ public class ExcelUtil { */ public static void exportTemplateMultiSheet(List> data, String filename, String templatePath, HttpServletResponse response) { try { + resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); - exportTemplateMultiSheet(data, filename, templatePath, os); + exportTemplateMultiSheet(data, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } @@ -304,13 +302,10 @@ public class ExcelUtil { * @param data 模板需要的数据 * @param os 输出流 */ - public static void exportTemplateMultiList(Map data, String filename, String templatePath, OutputStream os) { + public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { if (CollUtil.isEmpty(data)) { throw new IllegalArgumentException("数据为空"); } - if (os instanceof HttpServletResponse response) { - resetResponse(filename, response); - } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) @@ -341,13 +336,10 @@ public class ExcelUtil { * @param data 模板需要的数据 * @param os 输出流 */ - public static void exportTemplateMultiSheet(List> data, String filename, String templatePath, OutputStream os) { + public static void exportTemplateMultiSheet(List> data, String templatePath, OutputStream os) { if (CollUtil.isEmpty(data)) { throw new IllegalArgumentException("数据为空"); } - if (os instanceof HttpServletResponse response) { - resetResponse(filename, response); - } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) @@ -374,7 +366,7 @@ public class ExcelUtil { /** * 重置响应体 */ - private static void resetResponse(String sheetName, HttpServletResponse response) { + private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException { String filename = encodingFilename(sheetName); FileUtils.setAttachmentResponseHeader(response, filename); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); From e71d6fa9837f74539ea6d943685668f75233d91b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 28 Mar 2025 23:12:40 +0800 Subject: [PATCH 003/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81excel=E6=96=B9=E6=B3=95=E6=8A=9B=E5=87=BAjson=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/common/excel/utils/ExcelUtil.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java index 0e9569677..70ab31d9c 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java @@ -215,6 +215,9 @@ public class ExcelUtil { */ public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); exportTemplate(data, templatePath, os); @@ -233,9 +236,6 @@ public class ExcelUtil { * @param os 输出流 */ public static void exportTemplate(List data, String templatePath, OutputStream os) { - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) @@ -265,6 +265,9 @@ public class ExcelUtil { */ public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) { try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); exportTemplateMultiList(data, templatePath, os); @@ -285,6 +288,9 @@ public class ExcelUtil { */ public static void exportTemplateMultiSheet(List> data, String filename, String templatePath, HttpServletResponse response) { try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); exportTemplateMultiSheet(data, templatePath, os); @@ -303,9 +309,6 @@ public class ExcelUtil { * @param os 输出流 */ public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) @@ -337,9 +340,6 @@ public class ExcelUtil { * @param os 输出流 */ public static void exportTemplateMultiSheet(List> data, String templatePath, OutputStream os) { - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) From 124bcc4bba585d92007615309b95195f96566ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 31 Mar 2025 09:41:46 +0800 Subject: [PATCH 004/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20sse=20?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B9=8B=E5=90=8E=20=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E5=AE=8C=E6=88=90=20=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E5=86=85=E5=AD=98=E6=B3=84=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/sse/core/SseEmitterManager.java | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java index 64dfcff37..ccf358ae3 100644 --- a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java @@ -39,14 +39,27 @@ public class SseEmitterManager { Map emitters = USER_TOKEN_EMITTERS.computeIfAbsent(userId, k -> new ConcurrentHashMap<>()); // 创建一个新的 SseEmitter 实例,超时时间设置为 0 表示无限制 - SseEmitter emitter = new SseEmitter(0L); - - emitters.put(token, emitter); + SseEmitter emitter = emitters.computeIfAbsent(token, k -> new SseEmitter(0L)); // 当 emitter 完成、超时或发生错误时,从映射表中移除对应的 token - emitter.onCompletion(() -> emitters.remove(token)); - emitter.onTimeout(() -> emitters.remove(token)); - emitter.onError((e) -> emitters.remove(token)); + emitter.onCompletion(() -> { + SseEmitter remove = emitters.remove(token); + if (remove != null) { + remove.complete(); + } + }); + emitter.onTimeout(() -> { + SseEmitter remove = emitters.remove(token); + if (remove != null) { + remove.complete(); + } + }); + emitter.onError((e) -> { + SseEmitter remove = emitters.remove(token); + if (remove != null) { + remove.complete(); + } + }); try { // 向客户端发送一条连接成功的事件 @@ -106,7 +119,10 @@ public class SseEmitterManager { .name("message") .data(message)); } catch (Exception e) { - emitters.remove(entry.getKey()); + SseEmitter remove = emitters.remove(entry.getKey()); + if (remove != null) { + remove.complete(); + } } } } else { From 5868fadbf5de2142e895953a47c30ffdcf425bac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 31 Mar 2025 09:42:30 +0800 Subject: [PATCH 005/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20sse=20?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B9=8B=E5=90=8E=20=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E5=AE=8C=E6=88=90=20=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E5=86=85=E5=AD=98=E6=B3=84=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/dromara/common/sse/core/SseEmitterManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java index ccf358ae3..cb9442850 100644 --- a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java @@ -39,7 +39,9 @@ public class SseEmitterManager { Map emitters = USER_TOKEN_EMITTERS.computeIfAbsent(userId, k -> new ConcurrentHashMap<>()); // 创建一个新的 SseEmitter 实例,超时时间设置为 0 表示无限制 - SseEmitter emitter = emitters.computeIfAbsent(token, k -> new SseEmitter(0L)); + SseEmitter emitter = new SseEmitter(0L); + + emitters.put(token, emitter); // 当 emitter 完成、超时或发生错误时,从映射表中移除对应的 token emitter.onCompletion(() -> { From 40eac0778959fa33da4dfa80cd930eb2af4ba35f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 31 Mar 2025 10:56:03 +0800 Subject: [PATCH 006/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20FlwNodeEx?= =?UTF-8?q?tServiceImpl=20=E4=BB=A3=E7=A0=81=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/FlwNodeExtServiceImpl.java | 46 ++++++++----------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java index 231487d04..a6af73988 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java @@ -26,34 +26,16 @@ import java.util.*; @Service public class FlwNodeExtServiceImpl implements NodeExtService { - /** - * 权限页code - */ - private static final String PERMISSION_TAB = "wf_button_tab"; - - /** - * 权限页名称 - */ - private static final String PERMISSION_TAB_NAME = "权限"; - - /** - * 基础设置 - */ - private static final int TYPE_BASE_SETTING = 1; - - /** - * 新页签 - */ - private static final int TYPE_NEW_TAB = 2; - /** * 存储不同 dictType 对应的配置信息 */ - private static final Map> CHILD_NODE_MAP = new HashMap<>(); + private static final Map CHILD_NODE_MAP = new HashMap<>(); + + record ButtonPermission(String label, Integer type, Boolean must, Boolean multiple) {} static { CHILD_NODE_MAP.put(ButtonPermissionEnum.class.getSimpleName(), - Map.of("label", "权限按钮", "type", 4, "must", false, "multiple", true)); + new ButtonPermission("权限按钮", 4, false, true)); } private final DictService dictService; @@ -67,8 +49,10 @@ public class FlwNodeExtServiceImpl implements NodeExtService { public List getNodeExt() { List nodeExtList = new ArrayList<>(); // 构建按钮权限页面 - nodeExtList.add(buildNodeExt(PERMISSION_TAB, PERMISSION_TAB_NAME, TYPE_NEW_TAB, + nodeExtList.add(buildNodeExt("wf_button_tab", "权限", 2, List.of(ButtonPermissionEnum.class))); + // 自定义构建 规则参考 NodeExt 与 warm-flow文档说明 + // nodeExtList.add(buildNodeExt("xxx_xxx", "xxx", 1, List); return nodeExtList; } @@ -160,15 +144,21 @@ public class FlwNodeExtServiceImpl implements NodeExtService { */ private NodeExt.ChildNode buildChildNodeMap(String key) { NodeExt.ChildNode childNode = new NodeExt.ChildNode(); - Map map = CHILD_NODE_MAP.get(key); + ButtonPermission bp = CHILD_NODE_MAP.get(key); + if (bp == null) { + childNode.setType(1); + childNode.setMust(false); + childNode.setMultiple(true); + return childNode; + } // label名称 - childNode.setLabel((String) map.get("label")); + childNode.setLabel(bp.label()); // 1:输入框 2:输入框 3:下拉框 4:选择框 - childNode.setType(Convert.toInt(map.get("type"), 1)); + childNode.setType(bp.type()); // 是否必填 - childNode.setMust(Convert.toBool(map.get("must"), false)); + childNode.setMust(bp.must()); // 是否多选 - childNode.setMultiple(Convert.toBool(map.get("multiple"), true)); + childNode.setMultiple(bp.multiple()); return childNode; } From 7c3f3523ea0d8f46c25e5879b0d5ba96c3a128a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 31 Mar 2025 11:31:05 +0800 Subject: [PATCH 007/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20record=20=E7=AE=80=E5=8C=96vo=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/monitor/CacheController.java | 13 ++++----- .../controller/system/SysMenuController.java | 16 +++++----- .../system/SysProfileController.java | 18 ++++++------ .../controller/system/SysRoleController.java | 11 ++++--- .../dromara/system/domain/vo/AvatarVo.java | 18 ------------ .../system/domain/vo/CacheListInfoVo.java | 23 --------------- .../system/domain/vo/DeptTreeSelectVo.java | 26 ----------------- .../system/domain/vo/MenuTreeSelectVo.java | 26 ----------------- .../dromara/system/domain/vo/ProfileVo.java | 29 ------------------- 9 files changed, 31 insertions(+), 149 deletions(-) delete mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/AvatarVo.java delete mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/CacheListInfoVo.java delete mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/DeptTreeSelectVo.java delete mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MenuTreeSelectVo.java delete mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ProfileVo.java diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java index 6b7499ace..1e9c6558b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java @@ -1,10 +1,9 @@ package org.dromara.system.controller.monitor; import cn.dev33.satoken.annotation.SaCheckPermission; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; import org.dromara.common.core.utils.StringUtils; -import org.dromara.system.domain.vo.CacheListInfoVo; -import lombok.RequiredArgsConstructor; import org.redisson.spring.data.connection.RedissonConnectionFactory; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.web.bind.annotation.GetMapping; @@ -45,11 +44,11 @@ public class CacheController { }); } - CacheListInfoVo infoVo = new CacheListInfoVo(); - infoVo.setInfo(connection.commands().info()); - infoVo.setDbSize(connection.commands().dbSize()); - infoVo.setCommandStats(pieList); - return R.ok(infoVo); + return R.ok(new CacheListInfoVo( + connection.commands().info(), + connection.commands().dbSize(), pieList)); } + public record CacheListInfoVo(Properties info, Long dbSize, List> commandStats) {} + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java index d8cd335ce..a6c3e115e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java @@ -15,7 +15,6 @@ import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.SysMenu; import org.dromara.system.domain.bo.SysMenuBo; -import org.dromara.system.domain.vo.MenuTreeSelectVo; import org.dromara.system.domain.vo.RouterVo; import org.dromara.system.domain.vo.SysMenuVo; import org.dromara.system.service.ISysMenuService; @@ -96,9 +95,9 @@ public class SysMenuController extends BaseController { @GetMapping(value = "/roleMenuTreeselect/{roleId}") public R roleMenuTreeselect(@PathVariable("roleId") Long roleId) { List menus = menuService.selectMenuList(LoginHelper.getUserId()); - MenuTreeSelectVo selectVo = new MenuTreeSelectVo(); - selectVo.setCheckedKeys(menuService.selectMenuListByRoleId(roleId)); - selectVo.setMenus(menuService.buildMenuTreeSelect(menus)); + MenuTreeSelectVo selectVo = new MenuTreeSelectVo( + menuService.selectMenuListByRoleId(roleId), + menuService.buildMenuTreeSelect(menus)); return R.ok(selectVo); } @@ -112,9 +111,9 @@ public class SysMenuController extends BaseController { @GetMapping(value = "/tenantPackageMenuTreeselect/{packageId}") public R tenantPackageMenuTreeselect(@PathVariable("packageId") Long packageId) { List menus = menuService.selectMenuList(LoginHelper.getUserId()); - MenuTreeSelectVo selectVo = new MenuTreeSelectVo(); - selectVo.setCheckedKeys(menuService.selectMenuListByPackageId(packageId)); - selectVo.setMenus(menuService.buildMenuTreeSelect(menus)); + MenuTreeSelectVo selectVo = new MenuTreeSelectVo( + menuService.selectMenuListByPackageId(packageId), + menuService.buildMenuTreeSelect(menus)); return R.ok(selectVo); } @@ -171,4 +170,7 @@ public class SysMenuController extends BaseController { return toAjax(menuService.deleteMenuById(menuId)); } + public record MenuTreeSelectVo(List checkedKeys, List> menus) { + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java index 5f187cb91..7a6bc2c1d 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java @@ -17,8 +17,6 @@ import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.bo.SysUserPasswordBo; import org.dromara.system.domain.bo.SysUserProfileBo; -import org.dromara.system.domain.vo.AvatarVo; -import org.dromara.system.domain.vo.ProfileVo; import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysOssService; @@ -50,10 +48,9 @@ public class SysProfileController extends BaseController { @GetMapping public R profile() { SysUserVo user = userService.selectUserById(LoginHelper.getUserId()); - ProfileVo profileVo = new ProfileVo(); - profileVo.setUser(user); - profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserId())); - profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserId())); + String roleGroup = userService.selectUserRoleGroup(user.getUserId()); + String postGroup = userService.selectUserPostGroup(user.getUserId()); + ProfileVo profileVo = new ProfileVo(user, roleGroup, postGroup); return R.ok(profileVo); } @@ -123,11 +120,14 @@ public class SysProfileController extends BaseController { String avatar = oss.getUrl(); boolean updateSuccess = DataPermissionHelper.ignore(() -> userService.updateUserAvatar(LoginHelper.getUserId(), oss.getOssId())); if (updateSuccess) { - AvatarVo avatarVo = new AvatarVo(); - avatarVo.setImgUrl(avatar); - return R.ok(avatarVo); + return R.ok(new AvatarVo(avatar)); } } return R.fail("上传图片异常,请联系管理员"); } + + public record AvatarVo(String imgUrl) {} + + public record ProfileVo(SysUserVo user, String roleGroup, String postGroup) {} + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java index d4a9dc8a4..e6afa3c07 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java @@ -1,6 +1,7 @@ package org.dromara.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.lang.tree.Tree; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; @@ -14,7 +15,6 @@ import org.dromara.system.domain.SysUserRole; import org.dromara.system.domain.bo.SysDeptBo; import org.dromara.system.domain.bo.SysRoleBo; import org.dromara.system.domain.bo.SysUserBo; -import org.dromara.system.domain.vo.DeptTreeSelectVo; import org.dromara.system.domain.vo.SysRoleVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysDeptService; @@ -221,9 +221,12 @@ public class SysRoleController extends BaseController { @SaCheckPermission("system:role:list") @GetMapping(value = "/deptTree/{roleId}") public R roleDeptTreeselect(@PathVariable("roleId") Long roleId) { - DeptTreeSelectVo selectVo = new DeptTreeSelectVo(); - selectVo.setCheckedKeys(deptService.selectDeptListByRoleId(roleId)); - selectVo.setDepts(deptService.selectDeptTreeList(new SysDeptBo())); + DeptTreeSelectVo selectVo = new DeptTreeSelectVo( + deptService.selectDeptListByRoleId(roleId), + deptService.selectDeptTreeList(new SysDeptBo())); return R.ok(selectVo); } + + public record DeptTreeSelectVo(List checkedKeys, List> depts) {} + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/AvatarVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/AvatarVo.java deleted file mode 100644 index 46c020b7c..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/AvatarVo.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.dromara.system.domain.vo; - -import lombok.Data; - -/** - * 用户头像信息 - * - * @author Michelle.Chung - */ -@Data -public class AvatarVo { - - /** - * 头像地址 - */ - private String imgUrl; - -} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/CacheListInfoVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/CacheListInfoVo.java deleted file mode 100644 index f827cba04..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/CacheListInfoVo.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.dromara.system.domain.vo; - -import lombok.Data; - -import java.util.List; -import java.util.Map; -import java.util.Properties; - -/** - * 缓存监控列表信息 - * - * @author Michelle.Chung - */ -@Data -public class CacheListInfoVo { - - private Properties info; - - private Long dbSize; - - private List> commandStats; - -} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/DeptTreeSelectVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/DeptTreeSelectVo.java deleted file mode 100644 index 6f7db2869..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/DeptTreeSelectVo.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.dromara.system.domain.vo; - -import cn.hutool.core.lang.tree.Tree; -import lombok.Data; - -import java.util.List; - -/** - * 角色部门列表树信息 - * - * @author Michelle.Chung - */ -@Data -public class DeptTreeSelectVo { - - /** - * 选中部门列表 - */ - private List checkedKeys; - - /** - * 下拉树结构列表 - */ - private List> depts; - -} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MenuTreeSelectVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MenuTreeSelectVo.java deleted file mode 100644 index 072453853..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MenuTreeSelectVo.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.dromara.system.domain.vo; - -import cn.hutool.core.lang.tree.Tree; -import lombok.Data; - -import java.util.List; - -/** - * 角色菜单列表树信息 - * - * @author Michelle.Chung - */ -@Data -public class MenuTreeSelectVo { - - /** - * 选中菜单列表 - */ - private List checkedKeys; - - /** - * 菜单下拉树结构列表 - */ - private List> menus; - -} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ProfileVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ProfileVo.java deleted file mode 100644 index c0476519d..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ProfileVo.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.dromara.system.domain.vo; - -import lombok.Data; - -/** - * 用户个人信息 - * - * @author Michelle.Chung - */ -@Data -public class ProfileVo { - - /** - * 用户信息 - */ - private SysUserVo user; - - /** - * 用户所属角色组 - */ - private String roleGroup; - - /** - * 用户所属岗位组 - */ - private String postGroup; - - -} From 9e551a0b2ab566363363c6c9ee9c76d596b5f5a6 Mon Sep 17 00:00:00 2001 From: Binary Date: Tue, 1 Apr 2025 06:29:08 +0000 Subject: [PATCH 008/121] =?UTF-8?q?!665=20admin=20Dockerfile=20=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E6=96=87=E4=BB=B6=E6=96=B0=E5=A2=9E=E6=9A=B4=E9=9C=B2?= =?UTF-8?q?=20snail=20job=20=E5=AE=A2=E6=88=B7=E7=AB=AF=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=20=E7=94=A8=E4=BA=8E=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E8=B0=83=E5=BA=A6=E4=B8=AD=E5=BF=83=E9=80=9A=E4=BF=A1=20*=20ad?= =?UTF-8?q?min=20Dockerfile=20=E6=9E=84=E5=BB=BA=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=9A=B4=E9=9C=B2=20snail=20job=20=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E7=AB=AF=E5=8F=A3=20=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=E8=B0=83=E5=BA=A6=E4=B8=AD?= =?UTF-8?q?=E5=BF=83=E9=80=9A=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-admin/Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ruoyi-admin/Dockerfile b/ruoyi-admin/Dockerfile index b489ab57c..a3a66756f 100644 --- a/ruoyi-admin/Dockerfile +++ b/ruoyi-admin/Dockerfile @@ -11,9 +11,11 @@ RUN mkdir -p /ruoyi/server/logs \ WORKDIR /ruoyi/server -ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" +ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" EXPOSE ${SERVER_PORT} +# 暴露 snail job 客户端端口 用于定时任务调度中心通信 +EXPOSE ${SNAIL_PORT} ADD ./target/ruoyi-admin.jar ./app.jar # 工作流字体文件 @@ -22,6 +24,7 @@ ADD ./zhFonts/ /usr/share/fonts/zhFonts/ SHELL ["/bin/bash", "-c"] ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ + -Dsnail-job.port=${SNAIL_PORT} \ # 应用名称 如果想区分集群节点监控 改成不同的名称即可 #-Dskywalking.agent.service_name=ruoyi-server \ #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \ From 023ceaaf91447ca5b853392b91c131e7a21f28af Mon Sep 17 00:00:00 2001 From: velenooo <8834950+velenooo@user.noreply.gitee.com> Date: Wed, 2 Apr 2025 05:20:53 +0000 Subject: [PATCH 009/121] =?UTF-8?q?!666=20fix:=20=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=A4=9A=E4=B8=AA=E5=AD=97=E6=AE=B5=E4=B8=8B?= =?UTF-8?q?=E6=8B=89=E5=80=BC=E8=B6=85=E8=BF=87100=E4=B8=AA=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=EF=BC=8C=E9=87=87=E7=94=A8=E5=A4=9A=E4=B8=AAsheet?= =?UTF-8?q?=E7=9A=84=E6=96=B9=E6=A1=88=E8=A7=A3=E5=86=B3=E3=80=82=20*=20fi?= =?UTF-8?q?x:=20=E6=A8=A1=E6=9D=BF=E5=AF=BC=E5=87=BA=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=B8=8B=E6=8B=89=E5=80=BC=E8=B6=85=E8=BF=87?= =?UTF-8?q?100=E4=B8=AA=E5=BC=82=E5=B8=B8=EF=BC=8C=E9=87=87=E7=94=A8?= =?UTF-8?q?=E5=A4=9A=E4=B8=AAsheet=E7=9A=84=E6=96=B9=E6=A1=88=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/excel/core/ExcelDownHandler.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java index 32fee7a6c..d10ec70e6 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java @@ -291,9 +291,11 @@ public class ExcelDownHandler implements SheetWriteHandler { * @param value 下拉选可选值 */ private void dropDownWithSheet(DataValidationHelper helper, Workbook workbook, Sheet sheet, Integer celIndex, List value) { + //由于poi的写出相关问题,超过100个会被临时写进硬盘,导致后续内存合并会出Attempting to write a row[] in the range [] that is already written to disk + String tmpOptionsSheetName = OPTIONS_SHEET_NAME + "_" + currentOptionsColumnIndex; // 创建下拉数据表 - Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(OPTIONS_SHEET_NAME))) - .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(OPTIONS_SHEET_NAME))); + Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName))) + .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName))); // 将下拉表隐藏 workbook.setSheetHidden(workbook.getSheetIndex(simpleDataSheet), true); // 完善纵向的一级选项数据表 @@ -302,9 +304,9 @@ public class ExcelDownHandler implements SheetWriteHandler { // 获取每一选项行,如果没有则创建 Row row = Optional.ofNullable(simpleDataSheet.getRow(i)) .orElseGet(() -> simpleDataSheet.createRow(finalI)); - // 获取本级选项对应的选项列,如果没有则创建 - Cell cell = Optional.ofNullable(row.getCell(currentOptionsColumnIndex)) - .orElseGet(() -> row.createCell(currentOptionsColumnIndex)); + // 获取本级选项对应的选项列,如果没有则创建。上述采用多个sheet,默认索引为1列 + Cell cell = Optional.ofNullable(row.getCell(0)) + .orElseGet(() -> row.createCell(0)); // 设置值 cell.setCellValue(value.get(i)); } @@ -312,13 +314,13 @@ public class ExcelDownHandler implements SheetWriteHandler { // 创建名称管理器 Name name = workbook.createName(); // 设置名称管理器的别名 - String nameName = String.format("%s_%d", OPTIONS_SHEET_NAME, celIndex); + String nameName = String.format("%s_%d", tmpOptionsSheetName, celIndex); name.setNameName(nameName); // 以纵向第一列创建一级下拉拼接引用位置 String function = String.format("%s!$%s$1:$%s$%d", - OPTIONS_SHEET_NAME, - getExcelColumnName(currentOptionsColumnIndex), - getExcelColumnName(currentOptionsColumnIndex), + tmpOptionsSheetName, + getExcelColumnName(0), + getExcelColumnName(0), value.size()); // 设置名称管理器的引用位置 name.setRefersToFormula(function); From 07fdc240d78cebe5360404b65b6e4c3997cfecb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Wed, 2 Apr 2025 14:04:41 +0800 Subject: [PATCH 010/121] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20=E5=9C=A8?= =?UTF-8?q?=E7=BA=BF=E7=94=A8=E6=88=B7=E8=AE=BE=E7=BD=AE=E8=BF=87=E6=9C=9F?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E4=B8=8E=E5=AE=A2=E6=88=B7=E7=AB=AF=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/dromara/web/listener/UserActionListener.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java index 07595e092..86d26edf7 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java @@ -1,6 +1,5 @@ package org.dromara.web.listener; -import cn.dev33.satoken.config.SaTokenConfig; import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; @@ -35,7 +34,6 @@ import java.time.Duration; @Slf4j public class UserActionListener implements SaTokenListener { - private final SaTokenConfig tokenConfig; private final SysLoginService loginService; /** @@ -59,10 +57,10 @@ public class UserActionListener implements SaTokenListener { dto.setDeviceType(loginModel.getDevice()); dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY)); TenantHelper.dynamic(tenantId, () -> { - if(tokenConfig.getTimeout() == -1) { + if(loginModel.getTimeout() == -1) { RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); } else { - RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); + RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginModel.getTimeout())); } }); // 记录登录日志 From 8e99dd306a3635d5410897de5138231b535fcc68 Mon Sep 17 00:00:00 2001 From: gssong <1742057357@qq.com> Date: Wed, 2 Apr 2025 21:03:58 +0800 Subject: [PATCH 011/121] =?UTF-8?q?https://gitee.com/dromara/RuoYi-Vue-Plu?= =?UTF-8?q?s/issues/IBYCY7=20fix=20=E4=BF=AE=E5=A4=8D=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E4=BC=9A=E7=AD=BE=E4=BA=BA=E5=91=98=E5=90=8E?= =?UTF-8?q?=EF=BC=8C=E4=BC=9A=E7=AD=BE=E5=AE=A1=E6=89=B9=E5=87=BA=E7=8E=B0?= =?UTF-8?q?=E6=AF=8F=E4=B8=AA=E4=BB=BB=E5=8A=A1=E7=9A=84=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E4=BA=BA=E9=83=BD=E6=98=AF=E9=80=89=E6=8B=A9=E7=9A=84=E5=A4=9A?= =?UTF-8?q?=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflow/service/impl/FlwTaskServiceImpl.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index d93ba00ab..990c32383 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -183,7 +183,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { // 消息通知 flwCommonService.sendMessage(definition.getFlowName(), ins.getId(), messageType, notice); //设置下一环节处理人 - setNextHandler(ins.getId()); + setNextHandler(ins.getId(), completeTaskBo.getAssigneeMap()); return true; } catch (Exception e) { log.error(e.getMessage(), e); @@ -194,9 +194,13 @@ public class FlwTaskServiceImpl implements IFlwTaskService { /** * 设置下一环节处理人 * - * @param instanceId 实例ID + * @param instanceId 实例ID + * @param assigneeMap 办理人 */ - private void setNextHandler(Long instanceId) { + private void setNextHandler(Long instanceId, Map assigneeMap) { + if (CollUtil.isEmpty(assigneeMap)) { + return; + } Instance inst = insService.getById(instanceId); List flowTaskList = selectByInstId(instanceId); Map variableMap = inst.getVariableMap(); From 5bf901cdcd05e02d5cb513fa31575f0f7b4fc59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Thu, 3 Apr 2025 13:34:41 +0800 Subject: [PATCH 012/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0api=E5=AE=A1=E6=89=B9=E7=AE=80=E5=8C=96=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/core/service/WorkflowService.java | 11 ++++++++++- .../service/impl/WorkflowServiceImpl.java | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java index abbcbff19..9d1a90195 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java @@ -78,9 +78,18 @@ public interface WorkflowService { /** * 办理任务 + * 系统后台发起审批 无用户信息 需要忽略权限 + * completeTask.getVariables().put("ignore", true); * * @param completeTask 参数 - * @return 结果 */ boolean completeTask(CompleteTaskDTO completeTask); + + /** + * 办理任务 + * + * @param taskId 任务ID + * @param message 办理意见 + */ + boolean completeTask(Long taskId, String message); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java index f8a20b5b4..0c0224000 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java @@ -122,6 +122,8 @@ public class WorkflowServiceImpl implements WorkflowService { /** * 办理任务 + * 系统后台发起审批 无用户信息 需要忽略权限 + * completeTask.getVariables().put("ignore", true); * * @param completeTask 参数 */ @@ -129,4 +131,21 @@ public class WorkflowServiceImpl implements WorkflowService { public boolean completeTask(CompleteTaskDTO completeTask) { return flwTaskService.completeTask(BeanUtil.toBean(completeTask, CompleteTaskBo.class)); } + + /** + * 办理任务 + * + * @param taskId 任务ID + * @param message 办理意见 + */ + @Override + public boolean completeTask(Long taskId, String message) { + CompleteTaskBo completeTask = new CompleteTaskBo(); + completeTask.setTaskId(taskId); + completeTask.setMessage(message); + // 忽略权限(系统后台发起审批 无用户信息 需要忽略权限) + completeTask.getVariables().put("ignore", true); + return flwTaskService.completeTask(completeTask); + } + } From 48d3ef9818eafc150d818aa0e6957fe0aa0238cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Tue, 8 Apr 2025 10:16:20 +0800 Subject: [PATCH 013/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E6=A0=A1=E9=AA=8C=E6=B3=A8=E8=A7=A3=E9=95=BF=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/common/core/domain/model/RegisterBody.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java index 6ea8a764a..cced26b2b 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java @@ -18,14 +18,14 @@ public class RegisterBody extends LoginBody { * 用户名 */ @NotBlank(message = "{user.username.not.blank}") - @Length(min = 2, max = 20, message = "{user.username.length.valid}") + @Length(min = 2, max = 30, message = "{user.username.length.valid}") private String username; /** * 用户密码 */ @NotBlank(message = "{user.password.not.blank}") - @Length(min = 5, max = 20, message = "{user.password.length.valid}") + @Length(min = 5, max = 30, message = "{user.password.length.valid}") private String password; private String userType; From ef39ad710713139c63e945f459f04012e2faf7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Tue, 8 Apr 2025 10:16:31 +0800 Subject: [PATCH 014/121] =?UTF-8?q?update=20=E5=A2=9E=E5=8A=A0=E8=B5=9E?= =?UTF-8?q?=E5=8A=A9=E5=95=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fc3131a24..a37615025 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow
数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc
**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/**
+Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11
[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group) # 本框架与RuoYi的功能差异 From c37b92978a54e03899970fd8c045dceefb43727f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Wed, 9 Apr 2025 10:23:59 +0800 Subject: [PATCH 015/121] =?UTF-8?q?update=20=E8=B0=83=E6=95=B4=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=20=E5=88=A0=E9=99=A4=E4=B8=8D=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E7=9A=84=E9=94=99=E8=AF=AF=E7=9A=84=E4=B8=9C=E8=A5=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-admin/src/main/resources/application.yml | 3 --- .../java/org/dromara/common/doc/config/SpringDocConfig.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index c15d89c02..ee059dc3c 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -177,9 +177,6 @@ springdoc: api-docs: # 是否开启接口文档 enabled: true -# swagger-ui: -# # 持久化认证数据 -# persistAuthorization: true info: # 标题 title: '标题:RuoYi-Vue-Plus多租户管理系统_接口文档' diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java index 069ef9ac8..c199015c0 100644 --- a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java @@ -30,7 +30,7 @@ import java.util.Optional; import java.util.Set; /** - * Swagger 文档配置 + * 接口文档配置 * * @author Lion Li */ From 70aa14ecf846e442b43ed2d01a8144816ba863d2 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Wed, 9 Apr 2025 10:35:08 +0800 Subject: [PATCH 016/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E5=8A=9E=E7=90=86=E4=BA=BA=E6=9D=83=E9=99=90?= =?UTF-8?q?=E5=A4=84=E7=90=86=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handler/WorkflowPermissionHandler.java | 33 ++----------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java index c18e4ed52..53dbd20ed 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java @@ -1,22 +1,16 @@ package org.dromara.workflow.handler; -import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.domain.model.LoginUser; -import org.dromara.workflow.common.ConditionalOnEnable; -import org.dromara.workflow.common.enums.TaskAssigneeEnum; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.handler.PermissionHandler; import org.dromara.warm.flow.core.service.impl.TaskServiceImpl; +import org.dromara.workflow.common.ConditionalOnEnable; import org.springframework.stereotype.Component; -import java.util.ArrayList; -import java.util.Collection; +import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * 办理人权限处理器 @@ -36,28 +30,7 @@ public class WorkflowPermissionHandler implements PermissionHandler { */ @Override public List permissions() { - LoginUser loginUser = LoginHelper.getLoginUser(); - if (ObjectUtil.isNull(loginUser)) { - return new ArrayList<>(); - } - // 使用一个流来构建权限列表 - return Stream.of( - // 角色权限前缀 - loginUser.getRoles().stream() - .map(role -> TaskAssigneeEnum.ROLE.getCode() + role.getRoleId()), - - // 岗位权限前缀 - Stream.ofNullable(loginUser.getPosts()) - .flatMap(Collection::stream) - .map(post -> TaskAssigneeEnum.POST.getCode() + post.getPostId()), - - // 用户和部门权限 - Stream.of(String.valueOf(loginUser.getUserId()), - TaskAssigneeEnum.DEPT.getCode() + loginUser.getDeptId() - ) - ) - .flatMap(stream -> stream) - .collect(Collectors.toList()); + return Collections.singletonList(LoginHelper.getUserIdStr()); } /** From b50904c6ff79efdf052504edc93a7abab6b5d09a Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Wed, 9 Apr 2025 10:57:29 +0800 Subject: [PATCH 017/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E6=B5=81=E7=A8=8B=E7=9B=91=E5=90=AC=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E8=8A=82=E7=82=B9=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/event/ProcessCreateTaskEvent.java | 12 +++++++- .../core/domain/event/ProcessEvent.java | 17 ++++++++++- .../handler/FlowProcessEventHandler.java | 28 +++++++++++++++---- .../listener/WorkflowGlobalListener.java | 18 ++++++------ .../service/impl/FlwTaskServiceImpl.java | 3 +- 5 files changed, 60 insertions(+), 18 deletions(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessCreateTaskEvent.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessCreateTaskEvent.java index 05047ab84..d603884d1 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessCreateTaskEvent.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessCreateTaskEvent.java @@ -27,10 +27,20 @@ public class ProcessCreateTaskEvent implements Serializable { private String flowCode; /** - * 审批节点编码 + * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + */ + private Integer nodeType; + + /** + * 流程节点编码 */ private String nodeCode; + /** + * 流程节点名称 + */ + private String nodeName; + /** * 任务id */ diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java index 6329b9c01..bc7b78065 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java @@ -33,7 +33,22 @@ public class ProcessEvent implements Serializable { private String businessId; /** - * 状态 + * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + */ + private Integer nodeType; + + /** + * 流程节点编码 + */ + private String nodeCode; + + /** + * 流程节点名称 + */ + private String nodeName; + + /** + * 流程状态 */ private String status; diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java index 3efc52da8..eec27369d 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java @@ -27,16 +27,27 @@ public class FlowProcessEventHandler { * * @param flowCode 流程定义编码 * @param businessId 业务id - * @param status 状态 + * @param nodeType 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + * @param nodeCode 流程节点编码 + * @param nodeName 流程节点名称 + * @param status 流程状态 + * @param params 办理参数 * @param submit 当为true时为申请人节点办理 */ - public void processHandler(String flowCode, String businessId, String status, Map params, boolean submit) { + public void processHandler(String flowCode, String businessId, Integer nodeType, String nodeCode, String nodeName, + String status, Map params, boolean submit) { + String tenantId = TenantHelper.getTenantId(); - log.info("发布流程事件,租户ID: {}, 流程状态: {}, 流程编码: {}, 业务ID: {}, 是否申请人节点办理: {}", tenantId, status, flowCode, businessId, submit); + log.info("【流程事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 状态: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 是否申请人节点: {}, 参数: {}", + tenantId, flowCode, businessId, status, nodeType, nodeCode, nodeName, submit, params); + ProcessEvent processEvent = new ProcessEvent(); processEvent.setTenantId(tenantId); processEvent.setFlowCode(flowCode); processEvent.setBusinessId(businessId); + processEvent.setNodeType(nodeType); + processEvent.setNodeCode(nodeCode); + processEvent.setNodeName(nodeName); processEvent.setStatus(status); processEvent.setParams(params); processEvent.setSubmit(submit); @@ -47,17 +58,22 @@ public class FlowProcessEventHandler { * 执行创建任务监听 * * @param flowCode 流程定义编码 - * @param nodeCode 审批节点编码 + * @param nodeType 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + * @param nodeCode 流程节点编码 + * @param nodeName 流程节点名称 * @param taskId 任务id * @param businessId 业务id */ - public void processCreateTaskHandler(String flowCode, String nodeCode, Long taskId, String businessId) { + public void processCreateTaskHandler(String flowCode, Integer nodeType, String nodeCode, String nodeName, Long taskId, String businessId) { String tenantId = TenantHelper.getTenantId(); - log.info("发布流程任务事件, 租户ID: {}, 流程编码: {}, 节点编码: {}, 任务ID: {}, 业务ID: {}", tenantId, flowCode, nodeCode, taskId, businessId); + log.info("发布流程任务事件, 租户ID: {}, 流程编码: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}, 业务ID: {}", + tenantId, flowCode, nodeType, nodeCode, nodeName, taskId, businessId); ProcessCreateTaskEvent processCreateTaskEvent = new ProcessCreateTaskEvent(); processCreateTaskEvent.setTenantId(tenantId); processCreateTaskEvent.setFlowCode(flowCode); + processCreateTaskEvent.setNodeType(nodeType); processCreateTaskEvent.setNodeCode(nodeCode); + processCreateTaskEvent.setNodeName(nodeName); processCreateTaskEvent.setTaskId(taskId); processCreateTaskEvent.setBusinessId(businessId); SpringUtils.context().publishEvent(processCreateTaskEvent); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index 272f9de92..6f435693e 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -52,7 +52,8 @@ public class WorkflowGlobalListener implements GlobalListener { Task task = listenerVariable.getTask(); if (task != null && BusinessStatusEnum.WAITING.getStatus().equals(flowStatus)) { // 判断流程状态(发布审批中事件) - flowProcessEventHandler.processCreateTaskHandler(definition.getFlowCode(), task.getNodeCode(), task.getId(), businessId); + flowProcessEventHandler.processCreateTaskHandler(definition.getFlowCode(), task.getNodeType(), + task.getNodeCode(), task.getNodeName(), task.getId(), businessId); } } @@ -83,8 +84,6 @@ public class WorkflowGlobalListener implements GlobalListener { public void finish(ListenerVariable listenerVariable) { Instance instance = listenerVariable.getInstance(); Definition definition = listenerVariable.getDefinition(); - String businessId = instance.getBusinessId(); - String flowStatus = instance.getFlowStatus(); Map params = new HashMap<>(); FlowParams flowParams = listenerVariable.getFlowParams(); if (ObjectUtil.isNotNull(flowParams)) { @@ -96,20 +95,21 @@ public class WorkflowGlobalListener implements GlobalListener { params.put("message", flowParams.getMessage()); } // 判断流程状态(发布:撤销,退回,作废,终止,已完成事件) - String status = determineFlowStatus(instance, flowStatus); + String status = determineFlowStatus(instance); if (StringUtils.isNotBlank(status)) { - flowProcessEventHandler.processHandler(definition.getFlowCode(), businessId, status, params, false); + flowProcessEventHandler.processHandler(definition.getFlowCode(), instance.getBusinessId(), instance.getNodeType(), + instance.getNodeCode(), instance.getNodeName(), status, params, false); } } /** - * 根据流程实例和当前流程状态确定最终状态 + * 根据流程实例确定最终状态 * - * @param instance 流程实例 - * @param flowStatus 流程实例当前状态 + * @param instance 流程实例 * @return 流程最终状态 */ - private String determineFlowStatus(Instance instance, String flowStatus) { + private String determineFlowStatus(Instance instance) { + String flowStatus = instance.getFlowStatus(); if (StringUtils.isNotBlank(flowStatus) && BusinessStatusEnum.initialState(flowStatus)) { log.info("流程实例当前状态: {}", flowStatus); return flowStatus; diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 990c32383..356af6600 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -162,7 +162,8 @@ public class FlwTaskServiceImpl implements IFlwTaskService { Definition definition = defService.getById(flowTask.getDefinitionId()); // 检查流程状态是否为草稿、已撤销或已退回状态,若是则执行流程提交监听 if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) { - flowProcessEventHandler.processHandler(definition.getFlowCode(), ins.getBusinessId(), ins.getFlowStatus(), null, true); + flowProcessEventHandler.processHandler(definition.getFlowCode(), ins.getBusinessId(), ins.getNodeType(), + ins.getNodeCode(), ins.getNodeName(), ins.getFlowStatus(), null, true); } // 设置弹窗处理人 Map assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap()); From a690ece164db87557b2363f6f74a3be420170dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Wed, 9 Apr 2025 11:53:40 +0800 Subject: [PATCH 018/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E7=9B=91=E5=90=AC=E5=99=A8=E4=BA=8B=E4=BB=B6=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handler/FlowProcessEventHandler.java | 37 ++++++++----------- .../listener/WorkflowGlobalListener.java | 10 ++--- .../service/impl/FlwTaskServiceImpl.java | 3 +- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java index eec27369d..84a322061 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java @@ -6,6 +6,7 @@ import org.dromara.common.core.domain.event.ProcessDeleteEvent; import org.dromara.common.core.domain.event.ProcessEvent; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.warm.flow.core.entity.Instance; import org.dromara.workflow.common.ConditionalOnEnable; import org.springframework.stereotype.Component; @@ -26,28 +27,25 @@ public class FlowProcessEventHandler { * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等) * * @param flowCode 流程定义编码 - * @param businessId 业务id - * @param nodeType 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) - * @param nodeCode 流程节点编码 - * @param nodeName 流程节点名称 + * @param instance 实例数据 * @param status 流程状态 * @param params 办理参数 * @param submit 当为true时为申请人节点办理 */ - public void processHandler(String flowCode, String businessId, Integer nodeType, String nodeCode, String nodeName, + public void processHandler(String flowCode, Instance instance, String status, Map params, boolean submit) { String tenantId = TenantHelper.getTenantId(); log.info("【流程事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 状态: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 是否申请人节点: {}, 参数: {}", - tenantId, flowCode, businessId, status, nodeType, nodeCode, nodeName, submit, params); + tenantId, flowCode, instance.getBusinessId(), status, instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), submit, params); ProcessEvent processEvent = new ProcessEvent(); processEvent.setTenantId(tenantId); processEvent.setFlowCode(flowCode); - processEvent.setBusinessId(businessId); - processEvent.setNodeType(nodeType); - processEvent.setNodeCode(nodeCode); - processEvent.setNodeName(nodeName); + processEvent.setBusinessId(instance.getBusinessId()); + processEvent.setNodeType(instance.getNodeType()); + processEvent.setNodeCode(instance.getNodeCode()); + processEvent.setNodeName(instance.getNodeName()); processEvent.setStatus(status); processEvent.setParams(params); processEvent.setSubmit(submit); @@ -58,24 +56,21 @@ public class FlowProcessEventHandler { * 执行创建任务监听 * * @param flowCode 流程定义编码 - * @param nodeType 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) - * @param nodeCode 流程节点编码 - * @param nodeName 流程节点名称 + * @param instance 实例数据 * @param taskId 任务id - * @param businessId 业务id */ - public void processCreateTaskHandler(String flowCode, Integer nodeType, String nodeCode, String nodeName, Long taskId, String businessId) { + public void processCreateTaskHandler(String flowCode, Instance instance, Long taskId) { String tenantId = TenantHelper.getTenantId(); - log.info("发布流程任务事件, 租户ID: {}, 流程编码: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}, 业务ID: {}", - tenantId, flowCode, nodeType, nodeCode, nodeName, taskId, businessId); + log.info("发布流程任务事件, 租户ID: {}, 流程编码: {}, 业务ID: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}", + tenantId, flowCode, instance.getBusinessId(), instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), taskId); ProcessCreateTaskEvent processCreateTaskEvent = new ProcessCreateTaskEvent(); processCreateTaskEvent.setTenantId(tenantId); processCreateTaskEvent.setFlowCode(flowCode); - processCreateTaskEvent.setNodeType(nodeType); - processCreateTaskEvent.setNodeCode(nodeCode); - processCreateTaskEvent.setNodeName(nodeName); + processCreateTaskEvent.setBusinessId(instance.getBusinessId()); + processCreateTaskEvent.setNodeType(instance.getNodeType()); + processCreateTaskEvent.setNodeCode(instance.getNodeCode()); + processCreateTaskEvent.setNodeName(instance.getNodeName()); processCreateTaskEvent.setTaskId(taskId); - processCreateTaskEvent.setBusinessId(businessId); SpringUtils.context().publishEvent(processCreateTaskEvent); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index 6f435693e..b6bdfa268 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -47,13 +47,10 @@ public class WorkflowGlobalListener implements GlobalListener { public void create(ListenerVariable listenerVariable) { Instance instance = listenerVariable.getInstance(); Definition definition = listenerVariable.getDefinition(); - String businessId = instance.getBusinessId(); - String flowStatus = instance.getFlowStatus(); Task task = listenerVariable.getTask(); - if (task != null && BusinessStatusEnum.WAITING.getStatus().equals(flowStatus)) { + if (task != null && BusinessStatusEnum.WAITING.getStatus().equals(instance.getFlowStatus())) { // 判断流程状态(发布审批中事件) - flowProcessEventHandler.processCreateTaskHandler(definition.getFlowCode(), task.getNodeType(), - task.getNodeCode(), task.getNodeName(), task.getId(), businessId); + flowProcessEventHandler.processCreateTaskHandler(definition.getFlowCode(), instance, task.getId()); } } @@ -97,8 +94,7 @@ public class WorkflowGlobalListener implements GlobalListener { // 判断流程状态(发布:撤销,退回,作废,终止,已完成事件) String status = determineFlowStatus(instance); if (StringUtils.isNotBlank(status)) { - flowProcessEventHandler.processHandler(definition.getFlowCode(), instance.getBusinessId(), instance.getNodeType(), - instance.getNodeCode(), instance.getNodeName(), status, params, false); + flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 356af6600..f34c15a77 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -162,8 +162,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { Definition definition = defService.getById(flowTask.getDefinitionId()); // 检查流程状态是否为草稿、已撤销或已退回状态,若是则执行流程提交监听 if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) { - flowProcessEventHandler.processHandler(definition.getFlowCode(), ins.getBusinessId(), ins.getNodeType(), - ins.getNodeCode(), ins.getNodeName(), ins.getFlowStatus(), null, true); + flowProcessEventHandler.processHandler(definition.getFlowCode(), ins, ins.getFlowStatus(), null, true); } // 设置弹窗处理人 Map assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap()); From 564ab331d7ce0a6eb70e550b542eb392fd0d8129 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Thu, 10 Apr 2025 16:01:29 +0800 Subject: [PATCH 019/121] =?UTF-8?q?update=20=E7=BB=9F=E4=B8=80=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81FlowParams=E6=9E=84=E9=80=A0=E6=96=B9?= =?UTF-8?q?=E5=BC=8F=E4=B8=BA=E5=BB=BA=E9=80=A0=E8=80=85=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?,=E6=8F=90=E5=8D=87=E4=BB=A3=E7=A0=81=E5=8F=AF=E8=AF=BB?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handler/FlowProcessEventHandler.java | 11 ++-- .../service/impl/FlwCommonServiceImpl.java | 12 ++--- .../service/impl/FlwInstanceServiceImpl.java | 10 ++-- .../service/impl/FlwTaskServiceImpl.java | 54 +++++++++---------- 4 files changed, 42 insertions(+), 45 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java index 84a322061..c9e7a9237 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java @@ -32,13 +32,10 @@ public class FlowProcessEventHandler { * @param params 办理参数 * @param submit 当为true时为申请人节点办理 */ - public void processHandler(String flowCode, Instance instance, - String status, Map params, boolean submit) { - + public void processHandler(String flowCode, Instance instance, String status, Map params, boolean submit) { String tenantId = TenantHelper.getTenantId(); - log.info("【流程事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 状态: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 是否申请人节点: {}, 参数: {}", + log.info("【流程事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 流程状态: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 是否申请人节点: {}, 参数: {}", tenantId, flowCode, instance.getBusinessId(), status, instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), submit, params); - ProcessEvent processEvent = new ProcessEvent(); processEvent.setTenantId(tenantId); processEvent.setFlowCode(flowCode); @@ -61,7 +58,7 @@ public class FlowProcessEventHandler { */ public void processCreateTaskHandler(String flowCode, Instance instance, Long taskId) { String tenantId = TenantHelper.getTenantId(); - log.info("发布流程任务事件, 租户ID: {}, 流程编码: {}, 业务ID: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}", + log.info("【流程任务事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}", tenantId, flowCode, instance.getBusinessId(), instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), taskId); ProcessCreateTaskEvent processCreateTaskEvent = new ProcessCreateTaskEvent(); processCreateTaskEvent.setTenantId(tenantId); @@ -82,7 +79,7 @@ public class FlowProcessEventHandler { */ public void processDeleteHandler(String flowCode, String businessId) { String tenantId = TenantHelper.getTenantId(); - log.info("发布删除流程事件, 租户ID: {}, 流程编码: {}, 业务ID: {}", tenantId, flowCode, businessId); + log.info("【流程删除事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}", tenantId, flowCode, businessId); ProcessDeleteEvent processDeleteEvent = new ProcessDeleteEvent(); processDeleteEvent.setTenantId(tenantId); processDeleteEvent.setFlowCode(flowCode); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index bbc5734c9..9f92a154c 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -198,12 +198,12 @@ public class FlwCommonServiceImpl implements IFlwCommonService { } for (FlowTask task : list) { List userList = flwTaskService.currentTaskAllUser(task.getId()); - FlowParams flowParams = FlowParams.build(); - flowParams.nodeCode(targetNodeCode); - flowParams.message(message); - flowParams.skipType(SkipType.PASS.getKey()); - flowParams.flowStatus(flowStatus).hisStatus(flowHisStatus); - flowParams.ignore(true); + FlowParams flowParams = FlowParams.build() + .nodeCode(targetNodeCode) + .message(message) + .skipType(SkipType.PASS.getKey()) + .flowStatus(flowStatus).hisStatus(flowHisStatus) + .ignore(true); //解决会签没权限问题 if (CollUtil.isNotEmpty(userList)) { flowParams.handler(userList.get(0).getUserId().toString()); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index 3c0b59541..5518cd4ba 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -437,11 +437,11 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { } List flowTaskList = flwTaskService.selectByInstId(bo.getId()); for (FlowTask flowTask : flowTaskList) { - FlowParams flowParams = new FlowParams(); - flowParams.message(bo.getComment()); - flowParams.flowStatus(BusinessStatusEnum.INVALID.getStatus()) - .hisStatus(TaskStatusEnum.INVALID.getStatus()); - flowParams.ignore(true); + FlowParams flowParams = FlowParams.build() + .message(bo.getComment()) + .flowStatus(BusinessStatusEnum.INVALID.getStatus()) + .hisStatus(TaskStatusEnum.INVALID.getStatus()) + .ignore(true); taskService.termination(flowTask.getId(), flowParams); } return true; diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index f34c15a77..71f2a53f2 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -117,10 +117,10 @@ public class FlwTaskServiceImpl implements IFlwTaskService { dto.setTaskId(taskList.get(0).getId()); return dto; } - FlowParams flowParams = new FlowParams(); - flowParams.flowCode(startProcessBo.getFlowCode()); - flowParams.variable(startProcessBo.getVariables()); - flowParams.flowStatus(BusinessStatusEnum.DRAFT.getStatus()); + FlowParams flowParams = FlowParams.build() + .flowCode(startProcessBo.getFlowCode()) + .variable(startProcessBo.getVariables()) + .flowStatus(BusinessStatusEnum.DRAFT.getStatus()); Instance instance; try { instance = insService.start(businessId, flowParams); @@ -170,13 +170,13 @@ public class FlwTaskServiceImpl implements IFlwTaskService { completeTaskBo.getVariables().putAll(assigneeMap); } // 构建流程参数,包括变量、跳转类型、消息、处理人、权限等信息 - FlowParams flowParams = new FlowParams(); - flowParams.variable(completeTaskBo.getVariables()); - flowParams.skipType(SkipType.PASS.getKey()); - flowParams.message(completeTaskBo.getMessage()); - flowParams.flowStatus(BusinessStatusEnum.WAITING.getStatus()).hisStatus(TaskStatusEnum.PASS.getStatus()); - - flowParams.hisTaskExt(completeTaskBo.getFileId()); + FlowParams flowParams = FlowParams.build() + .variable(completeTaskBo.getVariables()) + .skipType(SkipType.PASS.getKey()) + .message(completeTaskBo.getMessage()) + .flowStatus(BusinessStatusEnum.WAITING.getStatus()) + .hisStatus(TaskStatusEnum.PASS.getStatus()) + .hisTaskExt(completeTaskBo.getFileId()); // 执行任务跳转,并根据返回的处理人设置下一步处理人 Instance instance = taskService.skip(taskId, flowParams); this.setHandler(instance, flowTask, flowCopyList); @@ -310,10 +310,10 @@ public class FlwTaskServiceImpl implements IFlwTaskService { task.setId(taskId); task.setNodeName("【抄送】" + task.getNodeName()); Date updateTime = new Date(flowHisTask.getUpdateTime().getTime() - 1000); - FlowParams flowParams = FlowParams.build(); - flowParams.skipType(SkipType.NONE.getKey()); - flowParams.hisStatus(TaskStatusEnum.COPY.getStatus()); - flowParams.message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getUserName)); + FlowParams flowParams = FlowParams.build() + .skipType(SkipType.NONE.getKey()) + .hisStatus(TaskStatusEnum.COPY.getStatus()) + .message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getUserName)); HisTask hisTask = hisTaskService.setSkipHisTask(task, flowNode, flowParams); hisTask.setCreateTime(updateTime); hisTask.setUpdateTime(updateTime); @@ -456,13 +456,13 @@ public class FlwTaskServiceImpl implements IFlwTaskService { Long definitionId = task.getDefinitionId(); Definition definition = defService.getById(definitionId); String applyNodeCode = flwCommonService.applyNodeCode(definitionId); - FlowParams flowParams = FlowParams.build(); - flowParams.nodeCode(bo.getNodeCode()); - flowParams.message(message); - flowParams.skipType(SkipType.REJECT.getKey()); - flowParams.flowStatus(applyNodeCode.equals(bo.getNodeCode()) ? TaskStatusEnum.BACK.getStatus() : TaskStatusEnum.WAITING.getStatus()) - .hisStatus(TaskStatusEnum.BACK.getStatus()); - flowParams.hisTaskExt(bo.getFileId()); + FlowParams flowParams = FlowParams.build() + .nodeCode(bo.getNodeCode()) + .message(message) + .skipType(SkipType.REJECT.getKey()) + .flowStatus(applyNodeCode.equals(bo.getNodeCode()) ? TaskStatusEnum.BACK.getStatus() : TaskStatusEnum.WAITING.getStatus()) + .hisStatus(TaskStatusEnum.BACK.getStatus()) + .hisTaskExt(bo.getFileId()); taskService.skip(task.getId(), flowParams); Instance instance = insService.getById(inst.getId()); @@ -519,9 +519,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { if (ObjectUtil.isNotNull(instance)) { BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus()); } - FlowParams flowParams = new FlowParams(); - flowParams.message(bo.getComment()); - flowParams.flowStatus(BusinessStatusEnum.TERMINATION.getStatus()) + FlowParams flowParams = FlowParams.build() + .message(bo.getComment()) + .flowStatus(BusinessStatusEnum.TERMINATION.getStatus()) .hisStatus(TaskStatusEnum.TERMINATION.getStatus()); taskService.termination(taskId, flowParams); return true; @@ -662,8 +662,8 @@ public class FlwTaskServiceImpl implements IFlwTaskService { @Override @Transactional(rollbackFor = Exception.class) public boolean taskOperation(TaskOperationBo bo, String taskOperation) { - FlowParams flowParams = new FlowParams(); - flowParams.message(bo.getMessage()); + FlowParams flowParams = FlowParams.build() + .message(bo.getMessage()); if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { flowParams.ignore(true); } From 53cf1b20137dd33d9779c97ae9190ad130673bde Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Thu, 10 Apr 2025 16:44:51 +0800 Subject: [PATCH 020/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E8=8E=B7=E5=8F=96=E6=B5=81=E7=A8=8B=E5=8F=98?= =?UTF-8?q?=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflow/domain/vo/FlowVariableVo.java | 28 ------------------- .../service/impl/FlwInstanceServiceImpl.java | 20 ++++--------- 2 files changed, 5 insertions(+), 43 deletions(-) delete mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowVariableVo.java diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowVariableVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowVariableVo.java deleted file mode 100644 index b4de76e93..000000000 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowVariableVo.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.dromara.workflow.domain.vo; - -import lombok.Data; - -import java.io.Serial; -import java.io.Serializable; - -/** - * 流程变量 - * - * @author may - */ -@Data -public class FlowVariableVo implements Serializable { - - @Serial - private static final long serialVersionUID = 1L; - - /** - * 变量key - */ - private String key; - - /** - * 变量值 - */ - private String value; -} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index 5518cd4ba..2aa95484f 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -42,7 +42,6 @@ import org.dromara.workflow.domain.bo.FlowInstanceBo; import org.dromara.workflow.domain.bo.FlowInvalidBo; import org.dromara.workflow.domain.vo.FlowHisTaskVo; import org.dromara.workflow.domain.vo.FlowInstanceVo; -import org.dromara.workflow.domain.vo.FlowVariableVo; import org.dromara.workflow.handler.FlowProcessEventHandler; import org.dromara.workflow.mapper.FlwCategoryMapper; import org.dromara.workflow.mapper.FlwInstanceMapper; @@ -346,21 +345,12 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { */ @Override public Map instanceVariable(Long instanceId) { - Map map = new HashMap<>(); FlowInstance flowInstance = flowInstanceMapper.selectById(instanceId); - Map variableMap = flowInstance.getVariableMap(); - List list = new ArrayList<>(); - if (CollUtil.isNotEmpty(variableMap)) { - for (Map.Entry entry : variableMap.entrySet()) { - FlowVariableVo flowVariableVo = new FlowVariableVo(); - flowVariableVo.setKey(entry.getKey()); - flowVariableVo.setValue(entry.getValue().toString()); - list.add(flowVariableVo); - } - } - map.put("variableList", list); - map.put("variable", flowInstance.getVariable()); - return map; + Map variableMap = Optional.ofNullable(flowInstance.getVariableMap()).orElse(Collections.emptyMap()); + List> variableList = variableMap.entrySet().stream() + .map(entry -> Map.of("key", entry.getKey(), "value", entry.getValue())) + .toList(); + return Map.of("variableList", variableList, "variable", flowInstance.getVariable()); } /** From d89f147c54ff02d6956cb609042127dbc14b48f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Thu, 10 Apr 2025 17:22:34 +0800 Subject: [PATCH 021/121] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20=E4=B8=B4?= =?UTF-8?q?=E6=97=B6=E8=A7=A3=E5=86=B3sa-token=E4=BD=BF=E7=94=A8=E7=A7=92?= =?UTF-8?q?=20redis=E6=98=AF=E6=AF=AB=E7=A7=92=E5=AF=BC=E8=87=B41=E7=A7=92?= =?UTF-8?q?=E7=9A=84=E7=B2=BE=E5=BA=A6=E9=97=AE=E9=A2=98=20=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E8=A1=A5=E5=81=BF(=E7=AD=89satoken=E5=AE=98=E6=96=B9?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/common/satoken/core/dao/PlusSaTokenDao.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java index a2a152003..3a5430327 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java @@ -85,7 +85,8 @@ public class PlusSaTokenDao implements SaTokenDao { @Override public long getTimeout(String key) { long timeout = RedisUtils.getTimeToLive(key); - return timeout < 0 ? timeout : timeout / 1000; + // 加1的目的 解决sa-token使用秒 redis是毫秒导致1秒的精度问题 手动补偿 + return timeout < 0 ? timeout : timeout / 1000 + 1; } /** @@ -152,7 +153,8 @@ public class PlusSaTokenDao implements SaTokenDao { @Override public long getObjectTimeout(String key) { long timeout = RedisUtils.getTimeToLive(key); - return timeout < 0 ? timeout : timeout / 1000; + // 加1的目的 解决sa-token使用秒 redis是毫秒导致1秒的精度问题 手动补偿 + return timeout < 0 ? timeout : timeout / 1000 + 1; } /** From a2c238d466c37bc9906a4d36fa2ee2a1e5bbc1d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Thu, 10 Apr 2025 17:29:44 +0800 Subject: [PATCH 022/121] update bouncycastle 1.76 => 1.80 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f1362f2e3..28dbfe61c 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ 1.4.6 0.2.0 1.18.36 - 1.76 + 1.80 1.16.7 2.7.0 From 00003b2c57cd8fce8b0cf7c08f48e9d134fefb76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 11 Apr 2025 10:18:05 +0800 Subject: [PATCH 023/121] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20sqlserver=20?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E5=86=85=E7=9A=84=E5=A4=9A=E4=BD=99=E7=AC=A6?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/sql/sqlserver/sqlserver_ry_vue_5.X.sql | 10 +++++----- script/sql/update/sqlserver/update_5.0-5.1.sql | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql index 0567f31d9..c5df3e8d2 100644 --- a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql +++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql @@ -1265,15 +1265,15 @@ INSERT sys_dict_data VALUES (32, N'000000', 0, N'邮件认证', N'email', N'sys_ GO INSERT sys_dict_data VALUES (33, N'000000', 0, N'小程序认证', N'xcx', N'sys_grant_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'小程序认证') GO -INSERT sys_dict_data VALUES (34, N'000000', 0, N'三方登录认证', N'`social`', N'sys_grant_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'三方登录认证') +INSERT sys_dict_data VALUES (34, N'000000', 0, N'三方登录认证', N'social', N'sys_grant_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'三方登录认证') GO -INSERT sys_dict_data VALUES (35, N'000000', 0, N'PC', N'`pc`', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'PC') +INSERT sys_dict_data VALUES (35, N'000000', 0, N'PC', N'pc', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'PC') GO -INSERT sys_dict_data VALUES (36, N'000000', 0, N'安卓', N'`android`', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'安卓') +INSERT sys_dict_data VALUES (36, N'000000', 0, N'安卓', N'android', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'安卓') GO -INSERT sys_dict_data VALUES (37, N'000000', 0, N'iOS', N'`ios`', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'iOS') +INSERT sys_dict_data VALUES (37, N'000000', 0, N'iOS', N'ios', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'iOS') GO -INSERT sys_dict_data VALUES (38, N'000000', 0, N'小程序', N'`xcx`', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'小程序') +INSERT sys_dict_data VALUES (38, N'000000', 0, N'小程序', N'xcx', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'小程序') GO CREATE TABLE sys_dict_type diff --git a/script/sql/update/sqlserver/update_5.0-5.1.sql b/script/sql/update/sqlserver/update_5.0-5.1.sql index bde3813ac..6ea54a55a 100644 --- a/script/sql/update/sqlserver/update_5.0-5.1.sql +++ b/script/sql/update/sqlserver/update_5.0-5.1.sql @@ -363,7 +363,7 @@ INSERT sys_dict_data VALUES (32, N'000000', 0, N'邮件认证', N'email', N'sys_ GO INSERT sys_dict_data VALUES (33, N'000000', 0, N'小程序认证', N'xcx', N'sys_grant_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'小程序认证') GO -INSERT sys_dict_data VALUES (34, N'000000', 0, N'三方登录认证', N'`social`', N'sys_grant_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'三方登录认证') +INSERT sys_dict_data VALUES (34, N'000000', 0, N'三方登录认证', N'social', N'sys_grant_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'三方登录认证') GO INSERT sys_dict_data VALUES (35, N'000000', 0, N'PC', N'`pc`', N'sys_device_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'PC') GO From 538aa8d90875400c44d5a3e4e4b3acf0c6181b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 11 Apr 2025 15:07:18 +0800 Subject: [PATCH 024/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E6=B5=81=E7=A8=8Bdemo=20=E6=9D=83=E9=99=90=E4=BA=BA?= =?UTF-8?q?=E5=88=86=E9=9A=94=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/leave/leave1.json | 4 ++-- script/leave/leave2.json | 4 ++-- script/leave/leave3.json | 4 ++-- script/leave/leave4.json | 4 ++-- script/leave/leave5.json | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/script/leave/leave1.json b/script/leave/leave1.json index 36fc32602..0ffdeeb89 100644 --- a/script/leave/leave1.json +++ b/script/leave/leave1.json @@ -52,7 +52,7 @@ "nodeType" : 1, "nodeCode" : "a8abf15f-b83e-428a-86cc-033555ea9bbe", "nodeName" : "部门主管", - "permissionFlag" : "role:3,role:4", + "permissionFlag" : "role:3@@role:4", "nodeRatio" : 0.000, "coordinate" : "720,200|720,200", "formCustom" : "N", @@ -72,4 +72,4 @@ "formCustom" : "N", "ext" : "[]" } ] -} \ No newline at end of file +} diff --git a/script/leave/leave2.json b/script/leave/leave2.json index bb196a314..7bbdbaab2 100644 --- a/script/leave/leave2.json +++ b/script/leave/leave2.json @@ -58,7 +58,7 @@ "nodeType" : 1, "nodeCode" : "b3528155-dcb7-4445-bbdf-3d00e3499e86", "nodeName" : "组长", - "permissionFlag" : "3,4", + "permissionFlag" : "3@@4", "nodeRatio" : 0.000, "coordinate" : "720,320|720,320", "formCustom" : "N", @@ -108,4 +108,4 @@ "coordinate" : "770,160;860,160;860,200" } ] } ] -} \ No newline at end of file +} diff --git a/script/leave/leave3.json b/script/leave/leave3.json index d289c787a..bb22d42c9 100644 --- a/script/leave/leave3.json +++ b/script/leave/leave3.json @@ -106,7 +106,7 @@ "nodeType" : 1, "nodeCode" : "762cb975-37d8-4276-b6db-79a4c3606394", "nodeName" : "综合部", - "permissionFlag" : "role:3,role:4", + "permissionFlag" : "role:3@@role:4", "nodeRatio" : 0.000, "coordinate" : "800,300|800,300", "formCustom" : "N", @@ -118,4 +118,4 @@ "coordinate" : "850,300;920,300;920,245" } ] } ] -} \ No newline at end of file +} diff --git a/script/leave/leave4.json b/script/leave/leave4.json index c085de2e5..50968f8dd 100644 --- a/script/leave/leave4.json +++ b/script/leave/leave4.json @@ -52,7 +52,7 @@ "nodeType" : 1, "nodeCode" : "2f9f2e21-9bcf-42a3-a07c-13037aad22d1", "nodeName" : "全部审批通过", - "permissionFlag" : "role:1,role:3", + "permissionFlag" : "role:1@@role:3", "nodeRatio" : 100.000, "coordinate" : "820,240|820,240", "formCustom" : "N", @@ -87,4 +87,4 @@ "formCustom" : "N", "ext" : "[]" } ] -} \ No newline at end of file +} diff --git a/script/leave/leave5.json b/script/leave/leave5.json index 76d10f180..a27b1de4e 100644 --- a/script/leave/leave5.json +++ b/script/leave/leave5.json @@ -55,7 +55,7 @@ "nodeType" : 1, "nodeCode" : "c80f273e-1f17-4bd8-9ad1-04a4a94ea862", "nodeName" : "会签", - "permissionFlag" : "role:1,role:3", + "permissionFlag" : "role:1@@role:3", "nodeRatio" : 100.000, "coordinate" : "700,320|700,320", "formCustom" : "N", @@ -118,4 +118,4 @@ "coordinate" : "750,120;860,120;860,195" } ] } ] -} \ No newline at end of file +} From 31502dccc7bc160fe36d012ceee93d31b02800a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 11 Apr 2025 15:36:38 +0800 Subject: [PATCH 025/121] =?UTF-8?q?update=20satoken=201.40.0=20=3D>=201.42?= =?UTF-8?q?.0=20=E9=80=82=E9=85=8D=E6=89=80=E6=9C=89=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E9=A1=B9(=E6=94=B9=E5=8A=A8=E8=BE=83=E5=A4=9A)=20SaLoginModel?= =?UTF-8?q?=20->=20SaLoginParameter=20device=20->=20deviceType=20satoken?= =?UTF-8?q?=20BCrypt=20->=20hutool=20BCrypt(satoken=E4=B8=8D=E7=BB=B4?= =?UTF-8?q?=E6=8A=A4=E4=BA=86)=20SaTokenDao=20->=20SaTokenDaoBySessionFoll?= =?UTF-8?q?owObject(satoken=E5=81=9A=E4=BA=86=E9=87=8D=E6=9E=84=E5=B0=81?= =?UTF-8?q?=E8=A3=85)=20sse=20=E9=80=82=E9=85=8D=E6=96=B0satoken=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E6=8B=A6=E6=88=AA=E5=99=A8=E5=8F=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../web/listener/UserActionListener.java | 20 +++++++++---------- .../web/service/SysRegisterService.java | 2 +- .../web/service/impl/EmailAuthStrategy.java | 6 +++--- .../service/impl/PasswordAuthStrategy.java | 8 ++++---- .../web/service/impl/SmsAuthStrategy.java | 6 +++--- .../web/service/impl/SocialAuthStrategy.java | 6 +++--- .../web/service/impl/XcxAuthStrategy.java | 6 +++--- .../satoken/core/dao/PlusSaTokenDao.java | 20 ++++++++++++++++--- .../common/satoken/utils/LoginHelper.java | 6 +++--- .../security/config/SecurityConfig.java | 17 ++++++---------- .../common/sse/controller/SseController.java | 1 + .../common/tenant/core/TenantSaTokenDao.java | 12 ++++++++++- .../system/SysProfileController.java | 2 +- .../controller/system/SysUserController.java | 2 +- .../service/impl/SysTenantServiceImpl.java | 2 +- 16 files changed, 69 insertions(+), 49 deletions(-) diff --git a/pom.xml b/pom.xml index 28dbfe61c..884d73092 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 0.15.0 4.0.3 2.3 - 1.40.0 + 1.42.0 3.5.11 3.9.1 5.8.35 diff --git a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java index 86d26edf7..554c64b32 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java @@ -1,8 +1,8 @@ package org.dromara.web.listener; import cn.dev33.satoken.listener.SaTokenListener; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.convert.Convert; import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; @@ -40,7 +40,7 @@ public class UserActionListener implements SaTokenListener { * 每次登录时触发 */ @Override - public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { + public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) { UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); String ip = ServletUtils.getClientIP(); UserOnlineDTO dto = new UserOnlineDTO(); @@ -50,17 +50,17 @@ public class UserActionListener implements SaTokenListener { dto.setOs(userAgent.getOs().getName()); dto.setLoginTime(System.currentTimeMillis()); dto.setTokenId(tokenValue); - String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY); - String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY); + String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY); + String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY); dto.setUserName(username); - dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY)); - dto.setDeviceType(loginModel.getDevice()); - dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY)); + dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY)); + dto.setDeviceType(loginParameter.getDeviceType()); + dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY)); TenantHelper.dynamic(tenantId, () -> { - if(loginModel.getTimeout() == -1) { + if(loginParameter.getTimeout() == -1) { RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); } else { - RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginModel.getTimeout())); + RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout())); } }); // 记录登录日志 @@ -72,7 +72,7 @@ public class UserActionListener implements SaTokenListener { logininforEvent.setRequest(ServletUtils.getRequest()); SpringUtils.context().publishEvent(logininforEvent); // 更新登录信息 - loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip); + loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip); log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java index 9ec08132e..567906e42 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java @@ -1,6 +1,6 @@ package org.dromara.web.service; -import cn.dev33.satoken.secure.BCrypt; +import cn.hutool.crypto.digest.BCrypt; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.Constants; diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java index 1bed4f3e5..e4315dc59 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java @@ -1,7 +1,7 @@ package org.dromara.web.service.impl; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; @@ -58,8 +58,8 @@ public class EmailAuthStrategy implements IAuthStrategy { }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); - SaLoginModel model = new SaLoginModel(); - model.setDevice(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 例如: 后台用户30分钟过期 app用户1天过期 model.setTimeout(client.getTimeout()); diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java index e8e60e1c4..e579f9969 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java @@ -1,9 +1,9 @@ package org.dromara.web.service.impl; -import cn.dev33.satoken.secure.BCrypt; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.digest.BCrypt; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -70,8 +70,8 @@ public class PasswordAuthStrategy implements IAuthStrategy { }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); - SaLoginModel model = new SaLoginModel(); - model.setDevice(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 例如: 后台用户30分钟过期 app用户1天过期 model.setTimeout(client.getTimeout()); diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java index 2ffda353e..597a6013b 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java @@ -1,7 +1,7 @@ package org.dromara.web.service.impl; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; @@ -58,8 +58,8 @@ public class SmsAuthStrategy implements IAuthStrategy { }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); - SaLoginModel model = new SaLoginModel(); - model.setDevice(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 例如: 后台用户30分钟过期 app用户1天过期 model.setTimeout(client.getTimeout()); diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java index 419dbd6ba..ffe95e0b6 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java @@ -1,7 +1,7 @@ package org.dromara.web.service.impl; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; @@ -99,8 +99,8 @@ public class SocialAuthStrategy implements IAuthStrategy { }); loginUser.setClientKey(client.getClientKey()); loginUser.setDeviceType(client.getDeviceType()); - SaLoginModel model = new SaLoginModel(); - model.setDevice(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 例如: 后台用户30分钟过期 app用户1天过期 model.setTimeout(client.getTimeout()); diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java index fa9b61819..f223dd88f 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java @@ -1,7 +1,7 @@ package org.dromara.web.service.impl; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -76,8 +76,8 @@ public class XcxAuthStrategy implements IAuthStrategy { loginUser.setDeviceType(client.getDeviceType()); loginUser.setOpenid(openid); - SaLoginModel model = new SaLoginModel(); - model.setDevice(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 例如: 后台用户30分钟过期 app用户1天过期 model.setTimeout(client.getTimeout()); diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java index 3a5430327..46c61c4c8 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java @@ -1,6 +1,6 @@ package org.dromara.common.satoken.core.dao; -import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.dao.auto.SaTokenDaoBySessionFollowObject; import cn.dev33.satoken.util.SaFoxUtil; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; @@ -16,10 +16,12 @@ import java.util.concurrent.TimeUnit; * Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一) *

* 采用 caffeine + redis 多级缓存 优化并发查询效率 + *

+ * SaTokenDaoBySessionFollowObject 是 SaTokenDao 子集简化了session方法处理 * * @author Lion Li */ -public class PlusSaTokenDao implements SaTokenDao { +public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { private static final Cache CAFFEINE = Caffeine.newBuilder() // 设置最后一次写入或访问后经过固定时间过期 @@ -107,6 +109,19 @@ public class PlusSaTokenDao implements SaTokenDao { return o; } + /** + * 获取 Object (指定反序列化类型),如无返空 + * + * @param key 键名称 + * @return object + */ + @SuppressWarnings("unchecked cast") + @Override + public T getObject(String key, Class classType) { + Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key)); + return (T) o; + } + /** * 写入Object,并设定存活时间 (单位: 秒) */ @@ -165,7 +180,6 @@ public class PlusSaTokenDao implements SaTokenDao { RedisUtils.expire(key, Duration.ofSeconds(timeout)); } - /** * 搜索数据 */ diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java index a5729387d..7a2b9fcf6 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java @@ -1,8 +1,8 @@ package org.dromara.common.satoken.utils; import cn.dev33.satoken.session.SaSession; -import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; @@ -47,8 +47,8 @@ public class LoginHelper { * @param loginUser 登录用户信息 * @param model 配置参数 */ - public static void login(LoginUser loginUser, SaLoginModel model) { - model = ObjectUtil.defaultIfNull(model, new SaLoginModel()); + public static void login(LoginUser loginUser, SaLoginParameter model) { + model = ObjectUtil.defaultIfNull(model, new SaLoginParameter()); StpUtil.login(loginUser.getLoginId(), model.setExtra(TENANT_KEY, loginUser.getTenantId()) .setExtra(USER_KEY, loginUser.getUserId()) diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java index a4e921f23..21f2c113c 100644 --- a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java +++ b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java @@ -11,13 +11,13 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.HttpStatus; -import org.dromara.common.core.exception.SseException; import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.security.config.properties.SecurityProperties; import org.dromara.common.security.handler.AllUrlHandler; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -37,6 +37,8 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; public class SecurityConfig implements WebMvcConfigurer { private final SecurityProperties securityProperties; + @Value("${sse.path}") + private String ssePath; /** * 注册sa-token的拦截器 @@ -54,15 +56,7 @@ public class SecurityConfig implements WebMvcConfigurer { .check(() -> { HttpServletRequest request = ServletUtils.getRequest(); // 检查是否登录 是否有token - try { - StpUtil.checkLogin(); - } catch (NotLoginException e) { - if (request.getRequestURI().contains("sse")) { - throw new SseException(e.getMessage(), e.getCode()); - } else { - throw e; - } - } + StpUtil.checkLogin(); // 检查 header 与 param 里的 clientid 与 token 里的是否一致 String headerCid = request.getHeader(LoginHelper.CLIENT_KEY); @@ -84,7 +78,8 @@ public class SecurityConfig implements WebMvcConfigurer { }); })).addPathPatterns("/**") // 排除不需要拦截的路径 - .excludePathPatterns(securityProperties.getExcludes()); + .excludePathPatterns(securityProperties.getExcludes()) + .excludePathPatterns(ssePath); } /** diff --git a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java index e5331e419..412834cfb 100644 --- a/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java +++ b/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java @@ -33,6 +33,7 @@ public class SseController implements DisposableBean { */ @GetMapping(value = "${sse.path}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter connect() { + StpUtil.checkLogin(); String tokenValue = StpUtil.getTokenValue(); Long userId = LoginHelper.getUserId(); return sseEmitterManager.connect(userId, tokenValue); diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java index b8da28ef1..9aaa753ec 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java @@ -81,6 +81,17 @@ public class TenantSaTokenDao extends PlusSaTokenDao { return super.getObject(GlobalConstants.GLOBAL_REDIS_KEY + key); } + /** + * 获取 Object (指定反序列化类型),如无返空 + * + * @param key 键名称 + * @return object + */ + @Override + public T getObject(String key, Class classType) { + return super.getObject(GlobalConstants.GLOBAL_REDIS_KEY + key, classType); + } + /** * 写入Object,并设定存活时间 (单位: 秒) */ @@ -137,7 +148,6 @@ public class TenantSaTokenDao extends PlusSaTokenDao { RedisUtils.expire(GlobalConstants.GLOBAL_REDIS_KEY + key, Duration.ofSeconds(timeout)); } - /** * 搜索数据 */ diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java index 7a6bc2c1d..8903a7406 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java @@ -1,8 +1,8 @@ package org.dromara.system.controller.system; -import cn.dev33.satoken.secure.BCrypt; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.io.FileUtil; +import cn.hutool.crypto.digest.BCrypt; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; import org.dromara.common.core.utils.StringUtils; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java index e1e868a6e..774f26a3e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java @@ -1,10 +1,10 @@ package org.dromara.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; -import cn.dev33.satoken.secure.BCrypt; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.digest.BCrypt; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java index a73415c64..ca088607b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java @@ -1,11 +1,11 @@ package org.dromara.system.service.impl; -import cn.dev33.satoken.secure.BCrypt; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; +import cn.hutool.crypto.digest.BCrypt; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; From 5c9721cfac181b669ce184917d03f512efa31780 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Fri, 11 Apr 2025 16:01:03 +0800 Subject: [PATCH 026/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E6=9D=83=E9=99=90=E6=8C=89=E9=92=AE=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=EF=BC=8C=E8=8B=A5=E9=9C=80=E8=A6=81=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E6=9B=B4=E5=A4=9A=E6=8C=89=E9=92=AE=E6=9D=83=E9=99=90=EF=BC=8C?= =?UTF-8?q?=E5=8F=AA=E9=9C=80=E5=9C=A8=20sources=20=E4=B8=AD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=AF=B9=E5=BA=94=E7=9A=84=E6=9E=9A=E4=B8=BE=E7=B1=BB?= =?UTF-8?q?=E6=88=96=E5=AD=97=E5=85=B8=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ermission.java => ButtonPermissionVo.java} | 15 +++- .../workflow/domain/vo/FlowTaskVo.java | 34 +------- .../workflow/service/IFlwNodeExtService.java | 22 +++++ .../impl/FlwDefinitionServiceImpl.java | 2 +- .../service/impl/FlwNodeExtServiceImpl.java | 82 ++++++++++++++++++- .../service/impl/FlwTaskServiceImpl.java | 8 +- 6 files changed, 123 insertions(+), 40 deletions(-) rename ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/{ButtonPermission.java => ButtonPermissionVo.java} (61%) create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermission.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java similarity index 61% rename from ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermission.java rename to ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java index 51f320d2d..eb26a4502 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermission.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java @@ -12,18 +12,18 @@ import java.io.Serializable; * @date 2025-02-28 */ @Data -public class ButtonPermission implements Serializable { +public class ButtonPermissionVo implements Serializable { @Serial private static final long serialVersionUID = 1L; /** - * 枚举路径 + * 唯一编码 */ private String code; /** - * 按钮编码 + * 选项值 */ private String value; @@ -31,4 +31,13 @@ public class ButtonPermission implements Serializable { * 是否显示 */ private boolean show; + + public ButtonPermissionVo() { + } + + public ButtonPermissionVo(String code, boolean show) { + this.code = code; + this.show = show; + } + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java index 785e866c8..e3b37ed86 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java @@ -1,20 +1,16 @@ package org.dromara.workflow.domain.vo; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.json.JSONUtil; import lombok.Data; -import org.dromara.common.core.utils.StringUtils; import org.dromara.common.translation.annotation.Translation; import org.dromara.common.translation.constant.TransConstant; import org.dromara.warm.flow.core.entity.User; import org.dromara.workflow.common.constant.FlowConstant; -import org.dromara.workflow.common.enums.ButtonPermissionEnum; import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; -import java.util.*; -import java.util.stream.Collectors; +import java.util.Date; +import java.util.List; /** * 任务视图 @@ -186,30 +182,6 @@ public class FlowTaskVo implements Serializable { /** * 按钮权限 */ - private List buttonList; + private List buttonList; - public List getButtonList(String ext) { - List buttonPermissions = Arrays.stream(ButtonPermissionEnum.values()) - .map(value -> { - ButtonPermission buttonPermission = new ButtonPermission(); - buttonPermission.setCode(value.getValue()); - buttonPermission.setShow(false); - return buttonPermission; - }) - .collect(Collectors.toList()); - if (StringUtils.isNotBlank(ext)) { - List buttonCodeList = JSONUtil.toList(JSONUtil.parseArray(ext), ButtonPermission.class); - if (CollUtil.isNotEmpty(buttonCodeList)) { - Optional firstPermission = buttonCodeList.stream().findFirst(); - firstPermission.ifPresent(permission -> { - Set codeSet = Arrays.stream(permission.getValue().split(",")) - .map(String::trim) - .filter(code -> !code.isEmpty()) - .collect(Collectors.toSet()); - buttonPermissions.forEach(bp -> bp.setShow(codeSet.contains(bp.getCode()))); - }); - } - } - return buttonPermissions; - } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java new file mode 100644 index 000000000..959516535 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java @@ -0,0 +1,22 @@ +package org.dromara.workflow.service; + +import org.dromara.workflow.domain.vo.ButtonPermissionVo; + +import java.util.List; + +/** + * 流程节点扩展属性 服务层 + * + * @author AprilWind + */ +public interface IFlwNodeExtService { + + /** + * 从扩展属性构建按钮权限列表:根据 ext 中记录的权限值,标记每个按钮是否勾选 + * + * @param ext 扩展属性 JSON 字符串 + * @return 按钮权限 VO 列表 + */ + List buildButtonPermissionsFromExt(String ext); + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java index 7f0f67ec6..7936210d0 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java @@ -191,7 +191,7 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { List flowDefinitions = flowDefinitionMapper.selectByIds(StreamUtils.toList(flowHisTasks, FlowHisTask::getDefinitionId)); if (CollUtil.isNotEmpty(flowDefinitions)) { String join = StreamUtils.join(flowDefinitions, FlowDefinition::getFlowCode); - log.error("流程定义【{}】已被使用不可被删除!", join); + log.info("流程定义【{}】已被使用不可被删除!", join); throw new ServiceException("流程定义【" + join + "】已被使用不可被删除!"); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java index a6af73988..fca21a367 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java @@ -2,18 +2,25 @@ package org.dromara.workflow.service.impl; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.dto.DictTypeDTO; import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; import org.dromara.warm.flow.ui.service.NodeExtService; import org.dromara.warm.flow.ui.vo.NodeExt; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.enums.ButtonPermissionEnum; import org.dromara.workflow.common.enums.NodeExtEnum; +import org.dromara.workflow.domain.vo.ButtonPermissionVo; +import org.dromara.workflow.service.IFlwNodeExtService; import org.springframework.stereotype.Service; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * 流程设计器-节点扩展属性 @@ -24,14 +31,15 @@ import java.util.*; @Slf4j @RequiredArgsConstructor @Service -public class FlwNodeExtServiceImpl implements NodeExtService { +public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService { /** * 存储不同 dictType 对应的配置信息 */ private static final Map CHILD_NODE_MAP = new HashMap<>(); - record ButtonPermission(String label, Integer type, Boolean must, Boolean multiple) {} + record ButtonPermission(String label, Integer type, Boolean must, Boolean multiple) { + } static { CHILD_NODE_MAP.put(ButtonPermissionEnum.class.getSimpleName(), @@ -162,4 +170,74 @@ public class FlwNodeExtServiceImpl implements NodeExtService { return childNode; } + /** + * 从扩展属性构建按钮权限列表:根据 ext 中记录的权限值,标记每个按钮是否勾选 + * + * @param ext 扩展属性 JSON 字符串 + * @return 按钮权限 VO 列表 + */ + @Override + public List buildButtonPermissionsFromExt(String ext) { + // 解析 ext 为 Map>,用于标记权限 + Map> permissionMap = JsonUtils.parseArray(ext, ButtonPermissionVo.class) + .stream() + .collect(Collectors.toMap( + ButtonPermissionVo::getCode, + item -> StringUtils.splitList(item.getValue()).stream() + .map(String::trim) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()), + (a, b) -> b, + HashMap::new + )); + + // 构建按钮权限列表,标记哪些按钮在 permissionMap 中出现(表示已勾选) + return buildPermissionsFromSources(permissionMap, List.of(ButtonPermissionEnum.class)); + } + + /** + * 将权限映射与按钮权限来源(枚举类或字典类型)进行匹配,生成权限视图列表 + *

+ * 使用说明: + * - sources 支持传入多个来源类型,支持 NodeExtEnum 枚举类 或 字典类型字符串(dictType) + * - 若需要扩展更多按钮权限,只需在 sources 中新增对应的枚举类或字典类型 + *

+ * 示例: + * buildPermissionsFromSources(permissionMap, List.of(ButtonPermissionEnum.class, "custom_button_dict")); + * + * @param permissionMap 权限映射 + * @param sources 枚举类或字典类型列表 + * @return 按钮权限视图对象列表 + */ + @SuppressWarnings("unchecked cast") + private List buildPermissionsFromSources(Map> permissionMap, List sources) { + return sources.stream() + .flatMap(source -> { + if (source instanceof Class clazz && NodeExtEnum.class.isAssignableFrom(clazz)) { + Set selectedSet = permissionMap.getOrDefault(clazz.getSimpleName(), Collections.emptySet()); + return extractDictItems(this.buildChildNode((Class) clazz), selectedSet).stream(); + } else if (source instanceof String dictType) { + Set selectedSet = permissionMap.getOrDefault(dictType, Collections.emptySet()); + return extractDictItems(this.buildChildNode(dictType), selectedSet).stream(); + } + return Stream.empty(); + }).toList(); + } + + /** + * 从节点子项中提取字典项,并构建按钮权限视图对象列表 + * + * @param childNode 子节点 + * @param selectedSet 已选中的值集 + * @return 按钮权限视图对象列表 + */ + private List extractDictItems(NodeExt.ChildNode childNode, Set selectedSet) { + return Optional.ofNullable(childNode) + .map(NodeExt.ChildNode::getDict) + .orElse(List.of()) + .stream() + .map(dict -> new ButtonPermissionVo(dict.getValue(), selectedSet.contains(dict.getValue()))) + .toList(); + } + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 71f2a53f2..90c96a4f4 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -48,6 +48,7 @@ import org.dromara.workflow.handler.WorkflowPermissionHandler; import org.dromara.workflow.mapper.FlwCategoryMapper; import org.dromara.workflow.mapper.FlwTaskMapper; import org.dromara.workflow.service.IFlwCommonService; +import org.dromara.workflow.service.IFlwNodeExtService; import org.dromara.workflow.service.IFlwTaskAssigneeService; import org.dromara.workflow.service.IFlwTaskService; import org.springframework.stereotype.Service; @@ -86,6 +87,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { private final FlowNodeMapper flowNodeMapper; private final IFlwTaskAssigneeService flwTaskAssigneeService; private final IFlwCommonService flwCommonService; + private final IFlwNodeExtService flwNodeExtService; /** * 启动任务 @@ -561,12 +563,12 @@ public class FlwTaskServiceImpl implements IFlwTaskService { flowTaskVo.setFlowCode(definition.getFlowCode()); flowTaskVo.setFlowName(definition.getFlowName()); flowTaskVo.setBusinessId(instance.getBusinessId()); - //设置按钮权限 - FlowNode flowNode = getByNodeCode(flowTaskVo.getNodeCode(), instance.getDefinitionId()); + FlowNode flowNode = this.getByNodeCode(flowTaskVo.getNodeCode(), instance.getDefinitionId()); if (ObjectUtil.isNull(flowNode)) { throw new NullPointerException("当前【" + flowTaskVo.getNodeCode() + "】节点编码不存在"); } - flowTaskVo.setButtonList(flowTaskVo.getButtonList(flowNode.getExt())); + //设置按钮权限 + flowTaskVo.setButtonList(flwNodeExtService.buildButtonPermissionsFromExt(flowNode.getExt())); flowTaskVo.setNodeRatio(flowNode.getNodeRatio()); flowTaskVo.setApplyNode(flowNode.getNodeCode().equals(flwCommonService.applyNodeCode(task.getDefinitionId()))); return flowTaskVo; From 878cd7e9f03e1268e198942849c436efbf042d99 Mon Sep 17 00:00:00 2001 From: gssong <1742057357@qq.com> Date: Sat, 12 Apr 2025 17:22:44 +0800 Subject: [PATCH 027/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E7=94=A8=E6=88=B7=E6=9F=A5=E8=AF=A2=E6=9E=84?= =?UTF-8?q?=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/FlwCommonServiceImpl.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index 9f92a154c..df4893d9b 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -36,10 +36,7 @@ import org.dromara.workflow.service.IFlwTaskAssigneeService; import org.dromara.workflow.service.IFlwTaskService; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; @@ -83,16 +80,19 @@ public class FlwCommonServiceImpl implements IFlwCommonService { Set list = new HashSet<>(); Set processedBySet = new HashSet<>(); IFlwTaskAssigneeService taskAssigneeService = SpringUtils.getBean(IFlwTaskAssigneeService.class); - for (User user : userList) { + Map> userListMap = StreamUtils.groupByKey(userList, User::getType); + for (Map.Entry> entry : userListMap.entrySet()) { + List entryValue = entry.getValue(); + String processedBys = StreamUtils.join(entryValue, User::getProcessedBy); // 根据 processedBy 前缀判断处理人类型,分别获取用户列表 - List users = taskAssigneeService.fetchUsersByStorageId(user.getProcessedBy()); + List users = taskAssigneeService.fetchUsersByStorageId(processedBys); // 转换为 FlowUser 并添加到结果集合 if (CollUtil.isNotEmpty(users)) { users.forEach(dto -> { String processedBy = String.valueOf(dto.getUserId()); if (!processedBySet.contains(processedBy)) { FlowUser flowUser = new FlowUser(); - flowUser.setType(user.getType()); + flowUser.setType(entry.getKey()); flowUser.setProcessedBy(processedBy); flowUser.setAssociated(taskId); list.add(flowUser); From 9e78fcccf7a685b420a737fe040a3ba4452aef25 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Sun, 13 Apr 2025 12:46:24 +0800 Subject: [PATCH 028/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E8=BF=94?= =?UTF-8?q?=E5=9B=9Evo=E7=9A=84boolean=E7=B1=BB=E5=9E=8B=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=8C=85=E8=A3=85=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/common/core/domain/event/ProcessEvent.java | 2 +- .../src/main/java/org/dromara/system/domain/vo/MetaVo.java | 6 +++--- .../main/java/org/dromara/system/domain/vo/RouterVo.java | 2 +- .../main/java/org/dromara/system/domain/vo/SysRoleVo.java | 4 ++-- .../org/dromara/workflow/domain/vo/ButtonPermissionVo.java | 4 ++-- .../java/org/dromara/workflow/domain/vo/FlowTaskVo.java | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java index bc7b78065..7b15b85ae 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java @@ -60,6 +60,6 @@ public class ProcessEvent implements Serializable { /** * 当为true时为申请人节点办理 */ - private boolean submit; + private Boolean submit; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java index f720cd74f..b2c11485c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java @@ -25,7 +25,7 @@ public class MetaVo { /** * 设置为true,则不会被 缓存 */ - private boolean noCache; + private Boolean noCache; /** * 内链地址(http(s)://开头) @@ -37,7 +37,7 @@ public class MetaVo { this.icon = icon; } - public MetaVo(String title, String icon, boolean noCache) { + public MetaVo(String title, String icon, Boolean noCache) { this.title = title; this.icon = icon; this.noCache = noCache; @@ -49,7 +49,7 @@ public class MetaVo { this.link = link; } - public MetaVo(String title, String icon, boolean noCache, String link) { + public MetaVo(String title, String icon, Boolean noCache, String link) { this.title = title; this.icon = icon; this.noCache = noCache; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java index 0d576ef15..d56e09da3 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java @@ -27,7 +27,7 @@ public class RouterVo { /** * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 */ - private boolean hidden; + private Boolean hidden; /** * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java index 1913170ed..74f5ac721 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java @@ -91,9 +91,9 @@ public class SysRoleVo implements Serializable { /** * 用户是否存在此角色标识 默认不存在 */ - private boolean flag = false; + private Boolean flag = false; - public boolean isSuperAdmin() { + public Boolean isSuperAdmin() { return SystemConstants.SUPER_ADMIN_ID.equals(this.roleId); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java index eb26a4502..7175e5e09 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java @@ -30,12 +30,12 @@ public class ButtonPermissionVo implements Serializable { /** * 是否显示 */ - private boolean show; + private Boolean show; public ButtonPermissionVo() { } - public ButtonPermissionVo(String code, boolean show) { + public ButtonPermissionVo(String code, Boolean show) { this.code = code; this.show = show; } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java index e3b37ed86..07a22c4f3 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java @@ -177,7 +177,7 @@ public class FlowTaskVo implements Serializable { /** * 是否为申请人节点 */ - private boolean applyNode; + private Boolean applyNode; /** * 按钮权限 From d456ff64f11204ca7db7e43f4acfb4467f96ea5c Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Sun, 13 Apr 2025 12:49:03 +0800 Subject: [PATCH 029/121] =?UTF-8?q?Revert=20"update=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=BF=94=E5=9B=9Evo=E7=9A=84boolean=E7=B1=BB=E5=9E=8B=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E4=BD=BF=E7=94=A8=E5=8C=85=E8=A3=85=E7=B1=BB"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 9e78fcccf7a685b420a737fe040a3ba4452aef25. --- .../org/dromara/common/core/domain/event/ProcessEvent.java | 2 +- .../src/main/java/org/dromara/system/domain/vo/MetaVo.java | 6 +++--- .../main/java/org/dromara/system/domain/vo/RouterVo.java | 2 +- .../main/java/org/dromara/system/domain/vo/SysRoleVo.java | 4 ++-- .../org/dromara/workflow/domain/vo/ButtonPermissionVo.java | 4 ++-- .../java/org/dromara/workflow/domain/vo/FlowTaskVo.java | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java index 7b15b85ae..bc7b78065 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java @@ -60,6 +60,6 @@ public class ProcessEvent implements Serializable { /** * 当为true时为申请人节点办理 */ - private Boolean submit; + private boolean submit; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java index b2c11485c..f720cd74f 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java @@ -25,7 +25,7 @@ public class MetaVo { /** * 设置为true,则不会被 缓存 */ - private Boolean noCache; + private boolean noCache; /** * 内链地址(http(s)://开头) @@ -37,7 +37,7 @@ public class MetaVo { this.icon = icon; } - public MetaVo(String title, String icon, Boolean noCache) { + public MetaVo(String title, String icon, boolean noCache) { this.title = title; this.icon = icon; this.noCache = noCache; @@ -49,7 +49,7 @@ public class MetaVo { this.link = link; } - public MetaVo(String title, String icon, Boolean noCache, String link) { + public MetaVo(String title, String icon, boolean noCache, String link) { this.title = title; this.icon = icon; this.noCache = noCache; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java index d56e09da3..0d576ef15 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java @@ -27,7 +27,7 @@ public class RouterVo { /** * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 */ - private Boolean hidden; + private boolean hidden; /** * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java index 74f5ac721..1913170ed 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java @@ -91,9 +91,9 @@ public class SysRoleVo implements Serializable { /** * 用户是否存在此角色标识 默认不存在 */ - private Boolean flag = false; + private boolean flag = false; - public Boolean isSuperAdmin() { + public boolean isSuperAdmin() { return SystemConstants.SUPER_ADMIN_ID.equals(this.roleId); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java index 7175e5e09..eb26a4502 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java @@ -30,12 +30,12 @@ public class ButtonPermissionVo implements Serializable { /** * 是否显示 */ - private Boolean show; + private boolean show; public ButtonPermissionVo() { } - public ButtonPermissionVo(String code, Boolean show) { + public ButtonPermissionVo(String code, boolean show) { this.code = code; this.show = show; } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java index 07a22c4f3..e3b37ed86 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java @@ -177,7 +177,7 @@ public class FlowTaskVo implements Serializable { /** * 是否为申请人节点 */ - private Boolean applyNode; + private boolean applyNode; /** * 按钮权限 From 71dddee1460a98986560b5c05f5a3ba36d21bf2d Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Sun, 13 Apr 2025 13:28:40 +0800 Subject: [PATCH 030/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96Mybatis?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/mybatis/handler/MybatisExceptionHandler.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java index 518d52d23..9a572b56b 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java @@ -1,5 +1,6 @@ package org.dromara.common.mybatis.handler; +import cn.hutool.http.HttpStatus; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.R; @@ -25,7 +26,7 @@ public class MybatisExceptionHandler { public R handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',数据库中已存在记录'{}'", requestURI, e.getMessage()); - return R.fail("数据库中已存在该记录,请联系管理员确认"); + return R.fail(HttpStatus.HTTP_CONFLICT, "数据库中已存在该记录,请联系管理员确认"); } /** @@ -35,12 +36,12 @@ public class MybatisExceptionHandler { public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); String message = e.getMessage(); - if (StringUtils.contains("CannotFindDataSourceException", message)) { + if (StringUtils.contains(message, "CannotFindDataSourceException")) { log.error("请求地址'{}', 未找到数据源", requestURI); - return R.fail("未找到数据源,请联系管理员确认"); + return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "未找到数据源,请联系管理员确认"); } log.error("请求地址'{}', Mybatis系统异常", requestURI, e); - return R.fail(message); + return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, message); } } From c171817d6aa70d9f40e0b2b1dbfe4f4ffc560e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Sun, 13 Apr 2025 19:16:57 +0800 Subject: [PATCH 031/121] =?UTF-8?q?add=20=E6=96=B0=E5=A2=9E=20=E4=B8=80?= =?UTF-8?q?=E5=A4=A7=E5=A0=86snailjob=E7=9A=84demo=E6=A1=88=E4=BE=8B(?= =?UTF-8?q?=E6=84=9F=E8=B0=A2=20=E8=80=81=E9=A9=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/dromara/job/entity/BillDto.java | 30 ++++++++++ .../dromara/job/snailjob/AlipayBillTask.java | 42 +++++++++++++ .../dromara/job/snailjob/SummaryBillTask.java | 45 ++++++++++++++ .../job/snailjob/TestAnnoJobExecutor.java | 6 +- .../job/snailjob/TestBroadcastJob.java | 37 ++++++++++++ .../job/snailjob/TestMapJobAnnotation.java | 53 ++++++++++++++++ .../snailjob/TestMapReduceAnnotation1.java | 60 +++++++++++++++++++ .../job/snailjob/TestStaticShardingJob.java | 36 +++++++++++ .../dromara/job/snailjob/WechatBillTask.java | 43 +++++++++++++ 9 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/entity/BillDto.java create mode 100644 ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java create mode 100644 ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java create mode 100644 ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestBroadcastJob.java create mode 100644 ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java create mode 100644 ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java create mode 100644 ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestStaticShardingJob.java create mode 100644 ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/entity/BillDto.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/entity/BillDto.java new file mode 100644 index 000000000..2661e3465 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/entity/BillDto.java @@ -0,0 +1,30 @@ +package org.dromara.job.entity; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class BillDto { + + /** + * 账单ID + */ + private Long billId; + + /** + * 账单渠道 + */ + private String billChannel; + + /** + * 账单日期 + */ + private String billDate; + + /** + * 账单金额 + */ + private BigDecimal billAmount; + +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java new file mode 100644 index 000000000..b8ad8cc39 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java @@ -0,0 +1,42 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.job.entity.BillDto; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/** + * DAG工作流任务-模拟支付宝账单任务 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "alipayBillTask") +public class AlipayBillTask { + + public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException { + BillDto billDto = new BillDto(); + billDto.setBillId(23456789L); + billDto.setBillChannel("alipay"); + // 设置清算日期 + String settlementDate = (String) jobArgs.getWfContext().get("settlementDate"); + if (StrUtil.equals(settlementDate, "sysdate")) { + settlementDate = DateUtil.today(); + } + billDto.setBillDate(settlementDate); + billDto.setBillAmount(new BigDecimal("2345.67")); + // 把billDto对象放入上下文进行传递 + jobArgs.appendContext("alipay", JsonUtils.toJsonString(billDto)); + SnailJobLog.REMOTE.info("上下文: {}", jobArgs.getWfContext()); + return ExecuteResult.success(billDto); + } + +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java new file mode 100644 index 000000000..bff15f97e --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java @@ -0,0 +1,45 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.util.StrUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.job.entity.BillDto; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/** + * DAG工作流任务-模拟汇总账单任务 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "summaryBillTask") +public class SummaryBillTask { + + public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException { + // 获得微信账单 + BigDecimal wechatAmount = BigDecimal.valueOf(0); + String wechat = (String) jobArgs.getWfContext("wechat"); + if (StrUtil.isNotBlank(wechat)) { + BillDto wechatBillDto = JsonUtils.parseObject(wechat, BillDto.class); + wechatAmount = wechatBillDto.getBillAmount(); + } + // 获得支付宝账单 + BigDecimal alipayAmount = BigDecimal.valueOf(0); + String alipay = (String) jobArgs.getWfContext("alipay"); + if (StrUtil.isNotBlank(alipay)) { + BillDto alipayBillDto = JsonUtils.parseObject(alipay, BillDto.class); + alipayAmount = alipayBillDto.getBillAmount(); + } + // 汇总账单 + BigDecimal totalAmount = wechatAmount.add(alipayAmount); + SnailJobLog.REMOTE.info("总金额: {}", totalAmount); + return ExecuteResult.success(totalAmount); + } + +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java index 5bea9daf3..e5339f5b4 100644 --- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java @@ -8,8 +8,10 @@ import com.aizuda.snailjob.common.log.SnailJobLog; import org.springframework.stereotype.Component; /** - * @author opensnail - * @date 2024-05-17 + * 正常任务 + * + * + * @author 老马 */ @Component @JobExecutor(name = "testJobExecutor") diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestBroadcastJob.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestBroadcastJob.java new file mode 100644 index 000000000..d77e72e6c --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestBroadcastJob.java @@ -0,0 +1,37 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.util.RandomUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 广播任务 + * + * + * @author 老马 + */ +@Slf4j +@Component +@JobExecutor(name = "testBroadcastJob") +public class TestBroadcastJob { + + @Value("${snail-job.port}") + private int clientPort; + + public ExecuteResult jobExecute(JobArgs jobArgs) { + int randomInt = RandomUtil.randomInt(100); + log.info("随机数: {}", randomInt); + SnailJobLog.REMOTE.info("随机数: {},客户端端口:{}", randomInt, clientPort); + if (randomInt < 50) { + throw new RuntimeException("随机数小于50,收集日志任务执行失败"); + } + // 获得jobArgs 中传入的相加的两个数 + return ExecuteResult.success("随机数大于50,收集日志任务执行成功"); + } + +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java new file mode 100644 index 000000000..6589ed1c5 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java @@ -0,0 +1,53 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.aizuda.snailjob.client.job.core.MapHandler; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.annotation.MapExecutor; +import com.aizuda.snailjob.client.job.core.dto.MapArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Map任务 动态分配 只分片不关注结果 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "testMapJobAnnotation") +public class TestMapJobAnnotation { + + @MapExecutor + public ExecuteResult doJobMapExecute(MapArgs mapArgs, MapHandler mapHandler) { + // 生成1~200数值并分片 + int partitionSize = 50; + List> partition = IntStream.rangeClosed(1, 200) + .boxed() + .collect(Collectors.groupingBy(i -> (i - 1) / partitionSize)) + .values() + .stream() + .toList(); + SnailJobLog.REMOTE.info("端口:{}完成分配任务", SpringUtil.getProperty("server.port")); + return mapHandler.doMap(partition, "doCalc"); + } + + @MapExecutor(taskName = "doCalc") + public ExecuteResult doCalc(MapArgs mapArgs) { + List sourceList = (List) mapArgs.getMapResult(); + // 遍历sourceList的每一个元素,计算出一个累加值partitionTotal + int partitionTotal = sourceList.stream().mapToInt(i -> i).sum(); + // 打印日志到服务器 + ThreadUtil.sleep(3, TimeUnit.SECONDS); + SnailJobLog.REMOTE.info("端口:{},partitionTotal:{}", SpringUtil.getProperty("server.port"), partitionTotal); + return ExecuteResult.success(partitionTotal); + } + +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java new file mode 100644 index 000000000..4ae2fa80a --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java @@ -0,0 +1,60 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.aizuda.snailjob.client.job.core.MapHandler; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.annotation.MapExecutor; +import com.aizuda.snailjob.client.job.core.annotation.ReduceExecutor; +import com.aizuda.snailjob.client.job.core.dto.MapArgs; +import com.aizuda.snailjob.client.job.core.dto.ReduceArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * MapReduce任务 动态分配 分片后合并结果 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "testMapReduceAnnotation1") +public class TestMapReduceAnnotation1 { + + @MapExecutor + public ExecuteResult rootMapExecute(MapArgs mapArgs, MapHandler mapHandler) { + int partitionSize = 50; + List> partition = IntStream.rangeClosed(1, 200) + .boxed() + .collect(Collectors.groupingBy(i -> (i - 1) / partitionSize)) + .values() + .stream() + .toList(); + SnailJobLog.REMOTE.info("端口:{}完成分配任务", SpringUtil.getProperty("server.port")); + return mapHandler.doMap(partition, "doCalc"); + } + + @MapExecutor(taskName = "doCalc") + public ExecuteResult doCalc(MapArgs mapArgs) { + List sourceList = (List) mapArgs.getMapResult(); + // 遍历sourceList的每一个元素,计算出一个累加值partitionTotal + int partitionTotal = sourceList.stream().mapToInt(i -> i).sum(); + // 打印日志到服务器 + ThreadUtil.sleep(3, TimeUnit.SECONDS); + SnailJobLog.REMOTE.info("端口:{},partitionTotal:{}", SpringUtil.getProperty("server.port"), partitionTotal); + return ExecuteResult.success(partitionTotal); + } + + @ReduceExecutor + public ExecuteResult reduceExecute(ReduceArgs reduceArgs) { + int reduceTotal = reduceArgs.getMapResult().stream().mapToInt(i -> Integer.parseInt((String) i)).sum(); + SnailJobLog.REMOTE.info("端口:{},reduceTotal:{}", SpringUtil.getProperty("server.port"), reduceTotal); + return ExecuteResult.success(reduceTotal); + } +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestStaticShardingJob.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestStaticShardingJob.java new file mode 100644 index 000000000..07a1bc566 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestStaticShardingJob.java @@ -0,0 +1,36 @@ +package org.dromara.job.snailjob; + +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.stereotype.Component; + +/** + * 静态分片 根据服务端任务参数分片 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "testStaticShardingJob") +public class TestStaticShardingJob { + + public ExecuteResult jobExecute(JobArgs jobArgs) { + String jobParams = String.valueOf(jobArgs.getJobParams()); + SnailJobLog.LOCAL.info("开始执行分片任务,参数:{}", jobParams); + // 获得jobArgs 中传入的开始id和结束id + String[] split = jobParams.split(","); + Long fromId = Long.parseLong(split[0]); + Long toId = Long.parseLong(split[1]); + // 模拟数据库操作,对范围id,进行加密处理 + try { + SnailJobLog.REMOTE.info("开始对id范围:{}进行加密处理", fromId + "-" + toId); + Thread.sleep(3000); + SnailJobLog.REMOTE.info("对id范围:{}进行加密处理完成", fromId + "-" + toId); + } catch (InterruptedException e) { + return ExecuteResult.failure("任务执行失败"); + } + return ExecuteResult.success("执行分片任务完成"); + } +} diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java new file mode 100644 index 000000000..d8caf1af0 --- /dev/null +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java @@ -0,0 +1,43 @@ +package org.dromara.job.snailjob; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.job.entity.BillDto; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/** + * DAG工作流任务-模拟微信账单任务 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "wechatBillTask") +public class WechatBillTask { + + public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException { + BillDto billDto = new BillDto(); + billDto.setBillId(123456789L); + billDto.setBillChannel("wechat"); + // 从上下文中获得清算日期并设置,如果上下文中清算日期 + // 是sysdate设置为当前日期;否则取管理页面设置的值 + String settlementDate = (String) jobArgs.getWfContext().get("settlementDate"); + if (StrUtil.equals(settlementDate, "sysdate")) { + settlementDate = DateUtil.today(); + } + billDto.setBillDate(settlementDate); + billDto.setBillAmount(new BigDecimal("1234.56")); + // 把billDto对象放入上下文进行传递 + jobArgs.appendContext("wechat", JsonUtils.toJsonString(billDto)); + SnailJobLog.REMOTE.info("上下文: {}", jobArgs.getWfContext()); + return ExecuteResult.success(billDto); + } + +} From ee6c0388dafce13a67a76fcbb89cace61707fd0d Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Mon, 14 Apr 2025 09:05:15 +0800 Subject: [PATCH 032/121] update warm-flow 1.6.8 => 1.6.10 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 884d73092..a474f8b66 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ 8.7.2-20250101 - 1.6.8 + 1.6.10 3.2.2 From 142fb33d81cf329209b957896895de40a646c687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 14 Apr 2025 09:33:36 +0800 Subject: [PATCH 033/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=AE=9E?= =?UTF-8?q?=E4=BD=93=E7=B1=BB=E7=BB=9F=E4=B8=80=E4=BD=BF=E7=94=A8=E5=8C=85?= =?UTF-8?q?=E8=A3=85=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/common/core/domain/event/ProcessEvent.java | 2 +- .../src/main/java/org/dromara/system/domain/vo/MetaVo.java | 6 +++--- .../main/java/org/dromara/system/domain/vo/RouterVo.java | 2 +- .../org/dromara/workflow/domain/vo/ButtonPermissionVo.java | 4 ++-- .../java/org/dromara/workflow/domain/vo/FlowTaskVo.java | 2 +- .../dromara/workflow/service/impl/TestLeaveServiceImpl.java | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java index bc7b78065..7b15b85ae 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java @@ -60,6 +60,6 @@ public class ProcessEvent implements Serializable { /** * 当为true时为申请人节点办理 */ - private boolean submit; + private Boolean submit; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java index f720cd74f..b2c11485c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java @@ -25,7 +25,7 @@ public class MetaVo { /** * 设置为true,则不会被 缓存 */ - private boolean noCache; + private Boolean noCache; /** * 内链地址(http(s)://开头) @@ -37,7 +37,7 @@ public class MetaVo { this.icon = icon; } - public MetaVo(String title, String icon, boolean noCache) { + public MetaVo(String title, String icon, Boolean noCache) { this.title = title; this.icon = icon; this.noCache = noCache; @@ -49,7 +49,7 @@ public class MetaVo { this.link = link; } - public MetaVo(String title, String icon, boolean noCache, String link) { + public MetaVo(String title, String icon, Boolean noCache, String link) { this.title = title; this.icon = icon; this.noCache = noCache; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java index 0d576ef15..d56e09da3 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java @@ -27,7 +27,7 @@ public class RouterVo { /** * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 */ - private boolean hidden; + private Boolean hidden; /** * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java index eb26a4502..7175e5e09 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java @@ -30,12 +30,12 @@ public class ButtonPermissionVo implements Serializable { /** * 是否显示 */ - private boolean show; + private Boolean show; public ButtonPermissionVo() { } - public ButtonPermissionVo(String code, boolean show) { + public ButtonPermissionVo(String code, Boolean show) { this.code = code; this.show = show; } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java index e3b37ed86..07a22c4f3 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java @@ -177,7 +177,7 @@ public class FlowTaskVo implements Serializable { /** * 是否为申请人节点 */ - private boolean applyNode; + private Boolean applyNode; /** * 按钮权限 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java index 9ecd867ac..4ed393e58 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java @@ -157,7 +157,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { // 办理意见 String message = Convert.toStr(params.get("message")); } - if (processEvent.isSubmit()) { + if (processEvent.getSubmit()) { testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus()); } baseMapper.updateById(testLeave); From 33e1d34ce50a1c8f4a6b04a70c0f05e92c6a42aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 14 Apr 2025 10:09:03 +0800 Subject: [PATCH 034/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E6=B3=A8=E8=A7=A3=E6=94=AF=E6=8C=81=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/core/constant/CacheNames.java | 5 ++-- .../redis/manager/PlusSpringCacheManager.java | 26 +++++++++++++------ .../demo/controller/RedisCacheController.java | 2 +- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java index 519034cf2..c38f39b47 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java @@ -3,13 +3,14 @@ package org.dromara.common.core.constant; /** * 缓存组名称常量 *

- * key 格式为 cacheNames#ttl#maxIdleTime#maxSize + * key 格式为 cacheNames#ttl#maxIdleTime#maxSize#local *

* ttl 过期时间 如果设置为0则不过期 默认为0 * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 + * local 默认开启本地缓存为1 关闭本地缓存为0 *

- * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500 + * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500、test#1h#0#500#0 * * @author Lion Li */ diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java index 740e2a13b..8428ef725 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java @@ -145,18 +145,25 @@ public class PlusSpringCacheManager implements CacheManager { if (array.length > 3) { config.setMaxSize(Integer.parseInt(array[3])); } - - if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) { - return createMap(name, config); + int local = 1; + if (array.length > 4) { + local = Integer.parseInt(array[4]); } - return createMapCache(name, config); + if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) { + return createMap(name, config, local); + } + + return createMapCache(name, config, local); } - private Cache createMap(String name, CacheConfig config) { + private Cache createMap(String name, CacheConfig config, int local) { RMap map = RedisUtils.getClient().getMap(name); - Cache cache = new CaffeineCacheDecorator(name, new RedissonCache(map, allowNullValues)); + Cache cache = new RedissonCache(map, allowNullValues); + if (local == 1) { + cache = new CaffeineCacheDecorator(name, cache); + } if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } @@ -167,10 +174,13 @@ public class PlusSpringCacheManager implements CacheManager { return cache; } - private Cache createMapCache(String name, CacheConfig config) { + private Cache createMapCache(String name, CacheConfig config, int local) { RMapCache map = RedisUtils.getClient().getMapCache(name); - Cache cache = new CaffeineCacheDecorator(name, new RedissonCache(map, config, allowNullValues)); + Cache cache = new RedissonCache(map, config, allowNullValues); + if (local == 1) { + cache = new CaffeineCacheDecorator(name, cache); + } if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java index 303cf885d..2335da4cd 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java @@ -40,7 +40,7 @@ public class RedisCacheController { *

* cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 */ - @Cacheable(cacheNames = "demo:cache#60s#10m#20", key = "#key", condition = "#key != null") + @Cacheable(cacheNames = "demo:cache#60s#10m#20#1", key = "#key", condition = "#key != null") @GetMapping("/test1") public R test1(String key, String value) { return R.ok("操作成功", value); From ea254745290429c032902e7edd149f9e3ca9a955 Mon Sep 17 00:00:00 2001 From: Q&Q <13153393+qlth@user.noreply.gitee.com> Date: Wed, 16 Apr 2025 06:22:07 +0000 Subject: [PATCH 035/121] =?UTF-8?q?!670=20fix=20=E4=BF=AE=E5=A4=8D=20sqlse?= =?UTF-8?q?rver=20=E8=84=9A=E6=9C=AC=E5=86=85=E7=9A=84=E7=BC=BA=E5=A4=B1?= =?UTF-8?q?=E7=AC=A6=E5=8F=B7=20*=20fix=20=E4=BF=AE=E5=A4=8D=20sqlserver?= =?UTF-8?q?=20=E8=84=9A=E6=9C=AC=E5=86=85=E7=9A=84=E7=BC=BA=E5=A4=B1?= =?UTF-8?q?=E7=AC=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/sql/sqlserver/sqlserver_ry_vue_5.X.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql index c5df3e8d2..f64d8f804 100644 --- a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql +++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql @@ -1239,7 +1239,7 @@ INSERT sys_dict_data VALUES (18, N'000000', 1, N'新增', N'1', N'sys_oper_type' GO INSERT sys_dict_data VALUES (19, N'000000', 2, N'修改', N'2', N'sys_oper_type', N'', N'info', N'N', 103, 1, getdate(), NULL, NULL, N'修改操作') GO -INSERT sys_dict_data VALUES (20, N'000000', 3, N'删除', N3, N'sys_oper_type', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'删除操作') +INSERT sys_dict_data VALUES (20, N'000000', 3, N'删除', N'3', N'sys_oper_type', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'删除操作') GO INSERT sys_dict_data VALUES (21, N'000000', 4, N'授权', N'4', N'sys_oper_type', N'', N'primary', N'N', 103, 1, getdate(), NULL, NULL, N'授权操作') GO @@ -3180,7 +3180,7 @@ INSERT INTO sys_oss_config VALUES (N'1', N'000000', N'minio', N'ruoyi', GO INSERT INTO sys_oss_config VALUES (N'2', N'000000', N'qiniu', N'XXXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi', N'', N's3-cn-north-1.qiniucs.com', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) GO -INSERT INTO sys_oss_config VALUES (N3, N'000000', N'aliyun', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi', N'', N'oss-cn-beijing.aliyuncs.com', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) +INSERT INTO sys_oss_config VALUES (N'3', N'000000', N'aliyun', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi', N'', N'oss-cn-beijing.aliyuncs.com', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) GO INSERT INTO sys_oss_config VALUES (N'4', N'000000', N'qcloud', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi-1250000000', N'', N'cos.ap-beijing.myqcloud.com', N'',N'N', N'ap-beijing', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) GO From 7c2efb1aefdd004ae45672896138e3fdb97886c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Thu, 17 Apr 2025 14:00:13 +0800 Subject: [PATCH 036/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20getNextNo?= =?UTF-8?q?deList=20=E5=8F=AA=E8=8E=B7=E5=8F=96=E4=B8=AD=E9=97=B4=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=94=A8=E4=BA=8E=E5=AE=A1=E6=89=B9=20=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E5=85=B6=E4=BB=96=E6=97=A0=E7=94=A8=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/workflow/service/impl/FlwTaskServiceImpl.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 90c96a4f4..5685ac3dc 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -587,13 +587,15 @@ public class FlwTaskServiceImpl implements IFlwTaskService { Instance instance = insService.getById(task.getInstanceId()); Definition definition = defService.getById(task.getDefinitionId()); Map mergeVariable = MapUtil.mergeAll(instance.getVariableMap(), variables); - //获取下一节点列表 + // 获取下一节点列表 List nextNodeList = nodeService.getNextNodeList(task.getDefinitionId(), task.getNodeCode(), null, SkipType.PASS.getKey(), mergeVariable); List nextFlowNodes = BeanUtil.copyToList(nextNodeList, FlowNode.class); + // 只获取中间节点 + nextFlowNodes = StreamUtils.filter(nextFlowNodes, node -> NodeType.BETWEEN.getKey().equals(node.getNodeType())); if (CollUtil.isNotEmpty(nextNodeList)) { - //构建以下节点数据 + // 构建以下节点数据 List buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, null)); - //办理人变量替换 + // 办理人变量替换 ExpressionUtil.evalVariable(buildNextTaskList, mergeVariable); for (FlowNode flowNode : nextFlowNodes) { buildNextTaskList.stream().filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())).findFirst().ifPresent(t -> { From 3b46f8c8cf4ef4e6b4a6f28c4f9ca408882bd384 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Fri, 18 Apr 2025 11:48:31 +0800 Subject: [PATCH 037/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E4=BB=BB=E5=8A=A1=E6=8C=87=E6=B4=BE=E7=9A=84=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E5=A2=9E=E5=8A=A0=E6=97=B6=E9=97=B4=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/system/service/impl/SysDeptServiceImpl.java | 4 ++++ .../org/dromara/system/service/impl/SysPostServiceImpl.java | 4 ++++ .../system/service/impl/SysTaskAssigneeServiceImpl.java | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java index 98aad216b..5e4904d05 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java @@ -39,6 +39,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; /** * 部门管理 服务实现 @@ -92,6 +93,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { } private LambdaQueryWrapper buildQueryWrapper(SysDeptBo bo) { + Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(SysDept::getDelFlag, SystemConstants.NORMAL); lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), SysDept::getDeptId, bo.getDeptId()); @@ -99,6 +101,8 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName()); lqw.like(StringUtils.isNotBlank(bo.getDeptCategory()), SysDept::getDeptCategory, bo.getDeptCategory()); lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus()); + lqw.between(params.get("beginTime") != null && params.get("endTime") != null, + SysDept::getCreateTime, params.get("beginTime"), params.get("endTime")); lqw.orderByAsc(SysDept::getAncestors); lqw.orderByAsc(SysDept::getParentId); lqw.orderByAsc(SysDept::getOrderNum); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java index 894f6acc8..122492e0c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java @@ -27,6 +27,7 @@ import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.List; +import java.util.Map; /** * 岗位信息 服务层处理 @@ -76,11 +77,14 @@ public class SysPostServiceImpl implements ISysPostService, PostService { * @return 构建好的查询包装器 */ private LambdaQueryWrapper buildQueryWrapper(SysPostBo bo) { + Map params = bo.getParams(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(bo.getPostCode()), SysPost::getPostCode, bo.getPostCode()) .like(StringUtils.isNotBlank(bo.getPostCategory()), SysPost::getPostCategory, bo.getPostCategory()) .like(StringUtils.isNotBlank(bo.getPostName()), SysPost::getPostName, bo.getPostName()) .eq(StringUtils.isNotBlank(bo.getStatus()), SysPost::getStatus, bo.getStatus()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysPost::getCreateTime, params.get("beginTime"), params.get("endTime")) .orderByAsc(SysPost::getPostSort); if (ObjectUtil.isNotNull(bo.getDeptId())) { //优先单部门搜索 diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java index d4f2c0d4e..31e9be902 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java @@ -49,8 +49,8 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService { public TaskAssigneeDTO selectRolesByTaskAssigneeList(TaskAssigneeBody taskQuery) { PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); SysRoleBo bo = new SysRoleBo(); - bo.setRoleName(taskQuery.getHandlerCode()); - bo.setRoleKey(taskQuery.getHandlerName()); + bo.setRoleKey(taskQuery.getHandlerCode()); + bo.setRoleName(taskQuery.getHandlerName()); Map params = bo.getParams(); params.put("beginTime", taskQuery.getBeginTime()); params.put("endTime", taskQuery.getEndTime()); From 2a34c3ebb283dff31f53533457e02c2339ba4952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E9=93=83=E8=96=AF=E5=A4=B4?= <5601833+xlsea@user.noreply.gitee.com> Date: Fri, 18 Apr 2025 05:30:12 +0000 Subject: [PATCH 038/121] =?UTF-8?q?!672=20fix:=20=E4=BF=AE=E5=A4=8D=20exce?= =?UTF-8?q?l=20=E5=90=88=E5=B9=B6=E5=8D=95=E5=85=83=E6=A0=BC=E5=9C=A8?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=9C=A8=E6=9C=80=E5=90=8E=E4=B8=80=E8=A1=8C?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E5=90=88=E5=B9=B6=E6=97=B6=EF=BC=8C=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E7=9A=84=E6=95=B0=E6=8D=AE=E5=90=88=E5=B9=B6=E5=A4=B1?= =?UTF-8?q?=E6=95=88=E9=97=AE=E9=A2=98=20*=20fix:=20=E4=BF=AE=E5=A4=8D=20e?= =?UTF-8?q?xcel=20=E5=90=88=E5=B9=B6=E5=8D=95=E5=85=83=E6=A0=BC=E5=9C=A8?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=9C=A8=E6=9C=80=E5=90=8E=E4=B8=80=E8=A1=8C?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E5=90=88=E5=B9=B6=E6=97=B6=EF=BC=8C=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E7=9A=84=E6=95=B0=E6=8D=AE=E5=90=88=E5=B9=B6=E5=A4=B1?= =?UTF-8?q?=E6=95=88=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/common/excel/core/CellMergeStrategy.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java index 7c7721c60..1c7d97fc4 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java @@ -112,7 +112,13 @@ public class CellMergeStrategy extends AbstractMergeStrategy implements Workbook } map.put(field, new RepeatCell(val, i)); } else if (i == list.size() - 1) { - if (i > repeatCell.getCurrent() && isMerge(list, i, field)) { + if (!isMerge(list, i, field)) { + // 如果最后一行不能合并,检查之前的数据是否需要合并 + if (i - repeatCell.getCurrent() > 1) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); + } + } else if (i > repeatCell.getCurrent()) { + // 如果最后一行可以合并,则直接合并到最后 cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); } } else if (!isMerge(list, i, field)) { From ae65985fbcd1d41b3e51a3cc2dacd315e90398a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BF=99=E5=A4=8F=E5=A4=A9=E4=BE=9D=E7=84=B6=E5=B9=B3?= =?UTF-8?q?=E5=87=A1?= <1822213252@qq.com> Date: Fri, 18 Apr 2025 09:30:33 +0000 Subject: [PATCH 039/121] =?UTF-8?q?!668=20update=20EasyExcel=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E5=8E=9F=E4=BD=9C=E8=80=85FastExcel=20*=20remove=20?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B9=8B=E5=89=8D=E6=9A=82=E6=97=B6=E6=80=A7?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E6=BC=8F=E6=B4=9E=E4=BE=9D=E8=B5=96=20*=20up?= =?UTF-8?q?date=20EasyExcel=E5=8D=87=E7=BA=A71.2.0=E5=B9=B6=E5=8D=87?= =?UTF-8?q?=E7=BA=A7commons-io=E5=88=B0=E6=9C=80=E6=96=B0=E7=89=88?= =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=AF=BC=E5=87=BA=E6=8A=A5=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E6=89=BE=E4=B8=8D=E5=88=B0=E5=BC=82=E5=B8=B8=20*=20update=20Ea?= =?UTF-8?q?syExcel=E5=8D=87=E7=BA=A7=E5=8E=9F=E4=BD=9C=E8=80=85FastExcel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 14 +++----- ruoyi-common/ruoyi-common-excel/pom.xml | 4 +-- .../excel/convert/ExcelBigNumberConvert.java | 12 +++---- .../excel/convert/ExcelDictConvert.java | 12 +++---- .../excel/convert/ExcelEnumConvert.java | 12 +++---- .../common/excel/core/CellMergeStrategy.java | 10 +++--- .../excel/core/DefaultExcelListener.java | 8 ++--- .../common/excel/core/ExcelDownHandler.java | 12 +++---- .../common/excel/core/ExcelListener.java | 2 +- .../excel/handler/DataWriteHandler.java | 18 +++++----- .../dromara/common/excel/utils/ExcelUtil.java | 34 +++++++++---------- .../demo/domain/bo/TestDemoImportVo.java | 2 +- .../dromara/demo/domain/vo/ExportDemoVo.java | 4 +-- .../dromara/demo/domain/vo/TestDemoVo.java | 4 +-- .../dromara/demo/domain/vo/TestTreeVo.java | 4 +-- .../demo/listener/ExportDemoListener.java | 2 +- .../src/main/resources/vm/java/vo.java.vm | 4 +-- .../dromara/system/domain/vo/SysClientVo.java | 4 +-- .../dromara/system/domain/vo/SysConfigVo.java | 4 +-- .../dromara/system/domain/vo/SysDeptVo.java | 4 +-- .../system/domain/vo/SysDictDataVo.java | 4 +-- .../system/domain/vo/SysDictTypeVo.java | 4 +-- .../system/domain/vo/SysLogininforVo.java | 4 +-- .../system/domain/vo/SysOperLogVo.java | 4 +-- .../system/domain/vo/SysOssConfigVo.java | 2 +- .../dromara/system/domain/vo/SysPostVo.java | 4 +-- .../dromara/system/domain/vo/SysRoleVo.java | 4 +-- .../system/domain/vo/SysTenantPackageVo.java | 4 +-- .../dromara/system/domain/vo/SysTenantVo.java | 4 +-- .../system/domain/vo/SysUserExportVo.java | 2 +- .../system/domain/vo/SysUserImportVo.java | 2 +- .../listener/SysUserImportListener.java | 4 +-- .../workflow/domain/vo/FlowCategoryVo.java | 4 +-- .../workflow/domain/vo/TestLeaveVo.java | 4 +-- 34 files changed, 107 insertions(+), 113 deletions(-) diff --git a/pom.xml b/pom.xml index a474f8b66..726b6727c 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 3.5.16 2.8.5 0.15.0 - 4.0.3 + 1.2.0 2.3 1.42.0 3.5.11 @@ -166,9 +166,9 @@ - com.alibaba - easyexcel - ${easyexcel.version} + cn.idev.excel + fastexcel + ${fastexcel.version} @@ -321,12 +321,6 @@ ${ip2region.version} - - commons-io - commons-io - 2.15.0 - - com.alibaba fastjson diff --git a/ruoyi-common/ruoyi-common-excel/pom.xml b/ruoyi-common/ruoyi-common-excel/pom.xml index dd4a5eebe..47ba5284b 100644 --- a/ruoyi-common/ruoyi-common-excel/pom.xml +++ b/ruoyi-common/ruoyi-common-excel/pom.xml @@ -22,8 +22,8 @@ - com.alibaba - easyexcel + cn.idev.excel + fastexcel diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java index 07cc4c4e7..c6beb5537 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java @@ -2,12 +2,12 @@ package org.dromara.common.excel.convert; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.GlobalConfiguration; -import com.alibaba.excel.metadata.data.ReadCellData; -import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.metadata.property.ExcelContentProperty; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; import lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java index 61eeabfd7..c54816f76 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java @@ -3,12 +3,12 @@ package org.dromara.common.excel.convert; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.GlobalConfiguration; -import com.alibaba.excel.metadata.data.ReadCellData; -import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.metadata.property.ExcelContentProperty; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.core.service.DictService; import org.dromara.common.core.utils.SpringUtils; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java index b948ea7ee..5723e61e8 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java @@ -3,12 +3,12 @@ package org.dromara.common.excel.convert; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.GlobalConfiguration; -import com.alibaba.excel.metadata.data.ReadCellData; -import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.metadata.property.ExcelContentProperty; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.excel.annotation.ExcelEnumFormat; import lombok.extern.slf4j.Slf4j; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java index 1c7d97fc4..515f68e1b 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java @@ -3,11 +3,11 @@ package org.dromara.common.excel.core; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; -import com.alibaba.excel.annotation.ExcelProperty; -import com.alibaba.excel.metadata.Head; -import com.alibaba.excel.write.handler.WorkbookWriteHandler; -import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext; -import com.alibaba.excel.write.merge.AbstractMergeStrategy; +import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.metadata.Head; +import cn.idev.excel.write.handler.WorkbookWriteHandler; +import cn.idev.excel.write.handler.context.WorkbookWriteHandlerContext; +import cn.idev.excel.write.merge.AbstractMergeStrategy; import lombok.AllArgsConstructor; import lombok.Data; import lombok.SneakyThrows; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java index b6fa0b434..e715c5fc3 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java @@ -1,10 +1,10 @@ package org.dromara.common.excel.core; import cn.hutool.core.util.StrUtil; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; -import com.alibaba.excel.exception.ExcelAnalysisException; -import com.alibaba.excel.exception.ExcelDataConvertException; +import cn.idev.excel.context.AnalysisContext; +import cn.idev.excel.event.AnalysisEventListener; +import cn.idev.excel.exception.ExcelAnalysisException; +import cn.idev.excel.exception.ExcelDataConvertException; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.json.utils.JsonUtils; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java index d10ec70e6..4cda8587f 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java @@ -5,12 +5,12 @@ import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import com.alibaba.excel.metadata.FieldCache; -import com.alibaba.excel.metadata.FieldWrapper; -import com.alibaba.excel.util.ClassUtils; -import com.alibaba.excel.write.handler.SheetWriteHandler; -import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; -import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import cn.idev.excel.metadata.FieldCache; +import cn.idev.excel.metadata.FieldWrapper; +import cn.idev.excel.util.ClassUtils; +import cn.idev.excel.write.handler.SheetWriteHandler; +import cn.idev.excel.write.metadata.holder.WriteSheetHolder; +import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddressList; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelListener.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelListener.java index 2d0340f25..957b30755 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelListener.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelListener.java @@ -1,6 +1,6 @@ package org.dromara.common.excel.core; -import com.alibaba.excel.read.listener.ReadListener; +import cn.idev.excel.read.listener.ReadListener; /** * Excel 导入监听 diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java index a2aa4951b..259f8225b 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java @@ -1,15 +1,15 @@ package org.dromara.common.excel.handler; import cn.hutool.core.collection.CollUtil; -import com.alibaba.excel.metadata.data.DataFormatData; -import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.util.StyleUtil; -import com.alibaba.excel.write.handler.CellWriteHandler; -import com.alibaba.excel.write.handler.SheetWriteHandler; -import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; -import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; -import com.alibaba.excel.write.metadata.style.WriteCellStyle; -import com.alibaba.excel.write.metadata.style.WriteFont; +import cn.idev.excel.metadata.data.DataFormatData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.util.StyleUtil; +import cn.idev.excel.write.handler.CellWriteHandler; +import cn.idev.excel.write.handler.SheetWriteHandler; +import cn.idev.excel.write.handler.context.CellWriteHandlerContext; +import cn.idev.excel.write.metadata.holder.WriteSheetHolder; +import cn.idev.excel.write.metadata.style.WriteCellStyle; +import cn.idev.excel.write.metadata.style.WriteFont; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFRichTextString; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java index 70ab31d9c..1b35e559a 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java @@ -3,13 +3,13 @@ package org.dromara.common.excel.utils; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.resource.ClassPathResource; import cn.hutool.core.util.IdUtil; -import com.alibaba.excel.EasyExcel; -import com.alibaba.excel.ExcelWriter; -import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder; -import com.alibaba.excel.write.metadata.WriteSheet; -import com.alibaba.excel.write.metadata.fill.FillConfig; -import com.alibaba.excel.write.metadata.fill.FillWrapper; -import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import cn.idev.excel.FastExcel; +import cn.idev.excel.ExcelWriter; +import cn.idev.excel.write.builder.ExcelWriterSheetBuilder; +import cn.idev.excel.write.metadata.WriteSheet; +import cn.idev.excel.write.metadata.fill.FillConfig; +import cn.idev.excel.write.metadata.fill.FillWrapper; +import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.AccessLevel; @@ -43,7 +43,7 @@ public class ExcelUtil { * @return 转换后集合 */ public static List importExcel(InputStream is, Class clazz) { - return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); + return FastExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); } @@ -57,7 +57,7 @@ public class ExcelUtil { */ public static ExcelResult importExcel(InputStream is, Class clazz, boolean isValidate) { DefaultExcelListener listener = new DefaultExcelListener<>(isValidate); - EasyExcel.read(is, clazz, listener).sheet().doRead(); + FastExcel.read(is, clazz, listener).sheet().doRead(); return listener.getExcelResult(); } @@ -70,7 +70,7 @@ public class ExcelUtil { * @return 转换后集合 */ public static ExcelResult importExcel(InputStream is, Class clazz, ExcelListener listener) { - EasyExcel.read(is, clazz, listener).sheet().doRead(); + FastExcel.read(is, clazz, listener).sheet().doRead(); return listener.getExcelResult(); } @@ -186,7 +186,7 @@ public class ExcelUtil { */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, OutputStream os, List options) { - ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) + ExcelWriterSheetBuilder builder = FastExcel.write(os, clazz) .autoCloseStream(false) // 自动适配 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) @@ -237,14 +237,14 @@ public class ExcelUtil { */ public static void exportTemplate(List data, String templatePath, OutputStream os) { ClassPathResource templateResource = new ClassPathResource(templatePath); - ExcelWriter excelWriter = EasyExcel.write(os) + ExcelWriter excelWriter = FastExcel.write(os) .withTemplate(templateResource.getStream()) .autoCloseStream(false) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) .registerWriteHandler(new DataWriteHandler(data.get(0).getClass())) .build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); + WriteSheet writeSheet = FastExcel.writerSheet().build(); FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); // 单表多数据导出 模板格式为 {.属性} for (T d : data) { @@ -310,13 +310,13 @@ public class ExcelUtil { */ public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { ClassPathResource templateResource = new ClassPathResource(templatePath); - ExcelWriter excelWriter = EasyExcel.write(os) + ExcelWriter excelWriter = FastExcel.write(os) .withTemplate(templateResource.getStream()) .autoCloseStream(false) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) .build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); + WriteSheet writeSheet = FastExcel.writerSheet().build(); for (Map.Entry map : data.entrySet()) { // 设置列表后续还有数据 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); @@ -341,14 +341,14 @@ public class ExcelUtil { */ public static void exportTemplateMultiSheet(List> data, String templatePath, OutputStream os) { ClassPathResource templateResource = new ClassPathResource(templatePath); - ExcelWriter excelWriter = EasyExcel.write(os) + ExcelWriter excelWriter = FastExcel.write(os) .withTemplate(templateResource.getStream()) .autoCloseStream(false) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) .build(); for (int i = 0; i < data.size(); i++) { - WriteSheet writeSheet = EasyExcel.writerSheet(i).build(); + WriteSheet writeSheet = FastExcel.writerSheet(i).build(); for (Map.Entry map : data.get(i).entrySet()) { // 设置列表后续还有数据 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java index c0661185e..dc8b35f49 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java @@ -1,6 +1,6 @@ package org.dromara.demo.domain.bo; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelProperty; import lombok.Data; import jakarta.validation.constraints.NotBlank; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java index ef9058ed5..b42ce7679 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java @@ -1,7 +1,7 @@ package org.dromara.demo.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java index e7ea8075c..c6595b0e2 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java @@ -1,7 +1,7 @@ package org.dromara.demo.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelNotation; import org.dromara.common.excel.annotation.ExcelRequired; import org.dromara.common.translation.annotation.Translation; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java index 58b4bdbc0..ee2336ac5 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java @@ -1,7 +1,7 @@ package org.dromara.demo.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.demo.domain.TestTree; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/listener/ExportDemoListener.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/listener/ExportDemoListener.java index 7bd4e1eea..de927609f 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/listener/ExportDemoListener.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/listener/ExportDemoListener.java @@ -1,7 +1,7 @@ package org.dromara.demo.listener; import cn.hutool.core.util.NumberUtil; -import com.alibaba.excel.context.AnalysisContext; +import cn.idev.excel.context.AnalysisContext; import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm index 5591f9778..480394c72 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm @@ -4,8 +4,8 @@ package ${packageName}.domain.vo; import ${import}; #end import ${packageName}.domain.${ClassName}; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import io.github.linpeilie.annotations.AutoMapper; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java index 34f24eb36..82b0f46ca 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java @@ -1,8 +1,8 @@ package org.dromara.system.domain.vo; import org.dromara.system.domain.SysClient; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import io.github.linpeilie.annotations.AutoMapper; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java index f896000a3..a35e1323c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysConfig; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java index 1f182a248..c9f5a1faf 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.common.excel.annotation.ExcelDictFormat; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java index 83ea619af..d97c00d1b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysDictData; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java index e6a184fcf..4b62226e2 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysDictType; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java index de19aeabd..3086aa74a 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java @@ -1,8 +1,8 @@ package org.dromara.system.domain.vo; import java.util.Date; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysLogininfor; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java index d9eb71d40..00b334432 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysOperLog; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java index e7cfde4fe..a030722d5 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java @@ -1,6 +1,6 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; import org.dromara.system.domain.SysOssConfig; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java index 69be547a0..50140b6f1 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.common.excel.annotation.ExcelDictFormat; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java index 1913170ed..1a205cc74 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.common.core.constant.SystemConstants; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java index 070334b41..ead9229b9 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java @@ -1,7 +1,7 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysTenantPackage; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java index 6a453150e..021c66488 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java @@ -1,8 +1,8 @@ package org.dromara.system.domain.vo; import java.util.Date; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.system.domain.SysTenant; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java index 37ec6b709..913ab2001 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java @@ -1,6 +1,6 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import io.github.linpeilie.annotations.AutoMapper; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java index c34a23cd9..d4e575c14 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java @@ -1,6 +1,6 @@ package org.dromara.system.domain.vo; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelProperty; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; import lombok.Data; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java index 25b62a9d9..66db5f578 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java @@ -4,8 +4,8 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.crypto.digest.BCrypt; import cn.hutool.http.HtmlUtil; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; +import cn.idev.excel.context.AnalysisContext; +import cn.idev.excel.event.AnalysisEventListener; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; import org.dromara.common.core.exception.ServiceException; diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java index f1c4caad5..db236c2df 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java @@ -1,7 +1,7 @@ package org.dromara.workflow.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.workflow.domain.FlowCategory; diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java index 47886d721..741949f79 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java @@ -1,7 +1,7 @@ package org.dromara.workflow.domain.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.workflow.domain.TestLeave; From 7ecf4bbf1ca85aeb1d6673870439d0052122e1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 18 Apr 2025 17:47:05 +0800 Subject: [PATCH 040/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=85=B3?= =?UTF-8?q?=E4=BA=8Eexcel=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../java/org/dromara/common/excel/core/ExcelDownHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a37615025..64384d3e1 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11
| 邮件 | 采用 mail-api 通用协议支持大部分邮件厂商 | 不支持 | | 接口文档 | 采用 SpringDoc、javadoc 无注解零入侵基于java注释
只需把注释写好 无需再写一大堆的文档注解了 | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成 | | 校验框架 | 采用 Validation 支持注解与工具类校验 注解支持国际化 | 仅支持注解 且注解不支持国际化 | -| Excel框架 | 采用 Alibaba EasyExcel 基于插件化
框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 | +| Excel框架 | 采用 FastExcel(原Alibaba EasyExcel) 基于插件化
框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 | | 工作流支持 | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能 | 无 | | 工具类框架 | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码 | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等 | | 监控框架 | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制
实时监控服务状态 框架还为其扩展了在线日志查看监控 | 无 | diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java index 4cda8587f..f3b641545 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java @@ -175,7 +175,7 @@ public class ExcelDownHandler implements SheetWriteHandler { List firstOptions = options.getOptions(); Map> secoundOptionsMap = options.getNextOptions(); - // 采用按行填充数据的方式,避免EasyExcel出现数据无法写入的问题 + // 采用按行填充数据的方式,避免出现数据无法写入的问题 // Attempting to write a row in the range that is already written to disk // 使用ArrayList记载数据,防止乱序 From 3f680385a9874d19a4a78e3dc5ed389e53e80745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Tue, 22 Apr 2025 16:08:38 +0800 Subject: [PATCH 041/121] =?UTF-8?q?update=20=E6=9B=B4=E6=96=B0=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64384d3e1..ec44c16d9 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus) [![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus) [![Star](https://gitcode.com/dromara/RuoYi-Vue-Plus/star/badge.svg)](https://gitcode.com/dromara/RuoYi-Vue-Plus) -[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.3.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) From d7d7dcbcf7d59afadc042e32e2e1d5ff78983e11 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Wed, 23 Apr 2025 12:30:07 +0800 Subject: [PATCH 042/121] =?UTF-8?q?docs=20=E4=BC=98=E5=8C=96=E6=9E=9A?= =?UTF-8?q?=E4=B8=BE=E7=B1=BB=E5=9E=8B=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/common/core/enums/DeviceType.java | 6 ++++-- .../java/org/dromara/common/core/enums/UserType.java | 12 +++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java index dbadfc2de..1667ac7bb 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java @@ -5,7 +5,6 @@ import lombok.Getter; /** * 设备类型 - * 针对一套 用户体系 * * @author Lion Li */ @@ -29,9 +28,12 @@ public enum DeviceType { XCX("xcx"), /** - * social第三方端 + * 第三方社交登录平台 */ SOCIAL("social"); + /** + * 设备标识 + */ private final String device; } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java index 69e4753cf..636988f2f 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java @@ -1,12 +1,11 @@ package org.dromara.common.core.enums; -import org.dromara.common.core.utils.StringUtils; import lombok.AllArgsConstructor; import lombok.Getter; +import org.dromara.common.core.utils.StringUtils; /** - * 设备类型 - * 针对多套 用户体系 + * 用户类型 * * @author Lion Li */ @@ -15,15 +14,18 @@ import lombok.Getter; public enum UserType { /** - * pc端 + * 后台系统用户 */ SYS_USER("sys_user"), /** - * app端 + * 移动客户端用户 */ APP_USER("app_user"); + /** + * 用户类型标识(用于 token、权限识别等) + */ private final String userType; public static UserType getUserType(String str) { From 21deab4bf1689493ed3153ed5863fb2c4c49345c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 25 Apr 2025 09:51:55 +0800 Subject: [PATCH 043/121] =?UTF-8?q?update=20=E5=88=A0=E9=99=A4=E5=B7=B2?= =?UTF-8?q?=E7=BB=8F=E4=B8=8D=E5=AD=98=E5=9C=A8=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index ec44c16d9..cc50af628 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,6 @@ Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11
| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 | | 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 | | 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 | -| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | 支持 | 支持 | | 使用案例 | 系统的一些功能案例 | 支持 | 不支持 | ## 参考文档 From e4f1da30fca3140e675d0139d5901ab585f44393 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Fri, 25 Apr 2025 13:38:41 +0800 Subject: [PATCH 044/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E6=97=A5?= =?UTF-8?q?=E6=9C=9F=E4=B8=8E=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/common/core/utils/DateUtils.java | 25 ++++++++++++++----- .../common/core/utils/StringUtils.java | 23 +++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java index 41d0f6c25..b52d95e16 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java @@ -175,14 +175,27 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils { } /** - * 计算两个日期之间的天数差(以毫秒为单位) + * 计算两个时间之间的时间差,并以指定单位返回(绝对值) * - * @param date1 第一个日期 - * @param date2 第二个日期 - * @return 两个日期之间的天数差的绝对值 + * @param start 起始时间 + * @param end 结束时间 + * @param unit 所需返回的时间单位(DAYS、HOURS、MINUTES、SECONDS、MILLISECONDS、MICROSECONDS、NANOSECONDS) + * @return 时间差的绝对值,以指定单位表示 */ - public static int differentDaysByMillisecond(Date date1, Date date2) { - return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + public static long difference(Date start, Date end, TimeUnit unit) { + // 计算时间差,单位为毫秒,取绝对值避免负数 + long diffInMillis = Math.abs(end.getTime() - start.getTime()); + + // 根据目标单位转换时间差 + return switch (unit) { + case DAYS -> diffInMillis / TimeUnit.DAYS.toMillis(1); + case HOURS -> diffInMillis / TimeUnit.HOURS.toMillis(1); + case MINUTES -> diffInMillis / TimeUnit.MINUTES.toMillis(1); + case SECONDS -> diffInMillis / TimeUnit.SECONDS.toMillis(1); + case MILLISECONDS -> diffInMillis; + case MICROSECONDS -> TimeUnit.MILLISECONDS.toMicros(diffInMillis); + case NANOSECONDS -> TimeUnit.MILLISECONDS.toNanos(diffInMillis); + }; } /** diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java index 0363ad4b5..716573431 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java @@ -6,6 +6,7 @@ import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; import org.springframework.util.AntPathMatcher; +import java.nio.charset.Charset; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -339,4 +340,26 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils { return false; } + /** + * 将字符串从源字符集转换为目标字符集 + * + * @param input 原始字符串 + * @param fromCharset 源字符集 + * @param toCharset 目标字符集 + * @return 转换后的字符串 + */ + public static String convert(String input, Charset fromCharset, Charset toCharset) { + if (isBlank(input)) { + return input; + } + try { + // 从源字符集获取字节 + byte[] bytes = input.getBytes(fromCharset); + // 使用目标字符集解码 + return new String(bytes, toCharset); + } catch (Exception e) { + return input; + } + } + } From 9fbe3cf3993cc5c58093b5cec7067d76a35c3943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 25 Apr 2025 16:40:13 +0800 Subject: [PATCH 045/121] update springboot 3.4.4 => 3.4.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 726b6727c..ebec03645 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ 5.3.1 - 3.4.4 + 3.4.5 UTF-8 UTF-8 17 From f29e0223a7485d797c3715ffe8b17ea8c1c7e39f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E8=BE=9E=E6=9C=AA=E5=AF=92?= <545073804@qq.com> Date: Sat, 26 Apr 2025 00:11:58 +0800 Subject: [PATCH 046/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=9C=B0=E5=9D=80=E6=94=AF=E6=8C=81IPv6=E5=88=A4?= =?UTF-8?q?=E6=96=AD=E8=80=8C=E4=B8=8D=E6=98=AF=E6=8A=9B=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/common/core/utils/NetUtils.java | 84 +++++++++++++++++++ .../common/core/utils/ip/AddressUtils.java | 13 +-- 2 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/NetUtils.java diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/NetUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/NetUtils.java new file mode 100644 index 000000000..72fdf4033 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/NetUtils.java @@ -0,0 +1,84 @@ +package org.dromara.common.core.utils; + +import cn.hutool.core.lang.PatternPool; +import cn.hutool.core.net.NetUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.regex.RegexUtils; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 增强网络相关工具类 + * + * @author 秋辞未寒 + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class NetUtils extends NetUtil { + + /** + * 判断是否为IPv6地址 + * + * @param ip IP地址 + * @return 是否为IPv6地址 + */ + public static boolean isIPv6(String ip) { + try { + // 判断是否为IPv6地址 + return InetAddress.getByName(ip) instanceof Inet6Address; + } catch (UnknownHostException e) { + return false; + } + } + + /** + * 判断IPv6地址是否为内网地址 + *

+ * 以下地址将归类为本地地址,如有业务场景有需要,请根据需求自行处理: + *

+     * 通配符地址 0:0:0:0:0:0:0:0
+     * 链路本地地址 fe80::/10
+     * 唯一本地地址 fec0::/10
+     * 环回地址 ::1
+     * 
+ * + * @param ip IP地址 + * @return 是否为内网地址 + */ + public static boolean isInnerIPv6(String ip) { + try { + // 判断是否为IPv6地址 + if (InetAddress.getByName(ip) instanceof Inet6Address inet6Address) { + // isAnyLocalAddress 判断是否为通配符地址,通常不会将其视为内网地址,根据业务场景自行处理判断 + // isLinkLocalAddress 判断是否为链路本地地址,通常不算内网地址,是否划分归属于内网需要根据业务场景自行处理判断 + // isLoopbackAddress 判断是否为环回地址,与IPv4的 127.0.0.1 同理,用于表示本机 + // isSiteLocalAddress 判断是否为本地站点地址,IPv6唯一本地地址(Unique Local Addresses,简称ULA) + if (inet6Address.isAnyLocalAddress() + || inet6Address.isLinkLocalAddress() + || inet6Address.isLoopbackAddress() + || inet6Address.isSiteLocalAddress()) { + return true; + } + } + } catch (UnknownHostException e) { + // 注意,isInnerIPv6方法和isIPv6方法的适用范围不同,所以此处不能忽略其异常信息。 + throw new IllegalArgumentException("Invalid IPv6 address!", e); + } + return false; + } + + /** + * 判断是否为IPv4地址 + * + * @param ip IP地址 + * @return 是否为IPv4地址 + */ + public static boolean isIPv4(String ip) { + return RegexUtils.isMatch(PatternPool.IPV4, ip); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java index 3f7cd57a9..2878078f1 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java @@ -1,11 +1,11 @@ package org.dromara.common.core.utils.ip; -import cn.hutool.core.net.NetUtil; import cn.hutool.http.HtmlUtil; -import org.dromara.common.core.utils.StringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.NetUtils; +import org.dromara.common.core.utils.StringUtils; /** * 获取地址类 @@ -20,14 +20,17 @@ public class AddressUtils { public static final String UNKNOWN = "XX XX"; public static String getRealAddressByIP(String ip) { - if (StringUtils.isBlank(ip)) { + // 处理空串并过滤HTML标签 + ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,"")); + // 判断是否为IPv4或IPv6,如果不是则返回未知地址 + if (!NetUtils.isIPv4(ip) && !NetUtils.isIPv6(ip)) { return UNKNOWN; } // 内网不查询 - ip = StringUtils.contains(ip, "0:0:0:0:0:0:0:1") ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); - if (NetUtil.isInnerIP(ip)) { + if (NetUtils.isInnerIPv6(ip) || NetUtils.isInnerIP(ip)) { return "内网IP"; } return RegionUtils.getCityInfo(ip); } + } From 1c5ae2f16806d1be3e771831e5a03e27b05ba778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E8=BE=9E=E6=9C=AA=E5=AF=92?= <545073804@qq.com> Date: Sat, 26 Apr 2025 00:20:34 +0800 Subject: [PATCH 047/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=9C=B0=E5=9D=80=E6=94=AF=E6=8C=81IPv6=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/common/core/utils/ip/AddressUtils.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java index 2878078f1..104734465 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java @@ -22,14 +22,21 @@ public class AddressUtils { public static String getRealAddressByIP(String ip) { // 处理空串并过滤HTML标签 ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,"")); + boolean isIPv6 = NetUtils.isIPv6(ip); // 判断是否为IPv4或IPv6,如果不是则返回未知地址 - if (!NetUtils.isIPv4(ip) && !NetUtils.isIPv6(ip)) { + if (!NetUtils.isIPv4(ip) && !isIPv6) { return UNKNOWN; } // 内网不查询 if (NetUtils.isInnerIPv6(ip) || NetUtils.isInnerIP(ip)) { return "内网IP"; } + // 不支持IPv6,不再进行没有必要的IP地址信息的解析,直接返回 + if (isIPv6) { + log.warn("ip2region不支持IPV6地址解析:{}", ip); + // 如有需要,可自行实现IPv6地址信息解析逻辑,并在这里返回 + return "未知"; + } return RegionUtils.getCityInfo(ip); } From 15905b702270b39b3196f9ac103fc3afc78cc1ae Mon Sep 17 00:00:00 2001 From: gssong <1742057357@qq.com> Date: Sat, 26 Apr 2025 12:52:36 +0800 Subject: [PATCH 048/121] =?UTF-8?q?update=20=E6=94=BE=E5=BC=80=E7=94=B3?= =?UTF-8?q?=E8=AF=B7=E4=BA=BA=E9=99=84=E4=BB=B6=E4=B8=8E=E6=8A=84=E9=80=81?= =?UTF-8?q?=E9=99=90=E5=88=B6=20=E9=99=84=E4=BB=B6=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E6=9D=83=E9=99=90=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflow/common/enums/ButtonPermissionEnum.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java index 7a224051d..598cd05ce 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java @@ -50,7 +50,12 @@ public enum ButtonPermissionEnum implements NodeExtEnum { /** * 是否能终止 */ - TERMINATION("是否能终止", "termination", true); + TERMINATION("是否能终止", "termination", true), + + /** + * 是否能上传附件 + */ + FILE("是否能上传附件", "file", true); private final String label; private final String value; From e1e3843ec0bc9c262587c5b03537986636f68b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Sun, 27 Apr 2025 22:09:14 +0800 Subject: [PATCH 049/121] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20snailjob=20h?= =?UTF-8?q?ttp=20basic=E9=AA=8C=E8=AF=81=E5=88=A4=E6=96=AD=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snailjob/server/starter/filter/ActuatorAuthFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java b/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java index e3a6892fa..00f6eee13 100644 --- a/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java +++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java @@ -44,7 +44,7 @@ public class ActuatorAuthFilter implements Filter { return; } // 验证用户名和密码 - if (!username.equals(split[0]) && password.equals(split[1])) { + if (!username.equals(split[0]) || !password.equals(split[1])) { response.setHeader("WWW-Authenticate", "Basic realm=\"realm\""); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); return; From 6d44069364cf634788d11316eba7998d84d8769d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E9=93=83=E8=96=AF=E5=A4=B4?= <5601833+xlsea@user.noreply.gitee.com> Date: Tue, 29 Apr 2025 08:29:46 +0000 Subject: [PATCH 050/121] =?UTF-8?q?!675=20update=20=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E8=8F=9C=E5=8D=95=E5=88=97=E8=A1=A8=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E8=8F=9C=E5=8D=95=E7=B1=BB=E5=9E=8B=E4=B8=8E=E7=88=B6?= =?UTF-8?q?=E7=BA=A7ID=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6=20*=20update=20?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E7=B3=BB=E7=BB=9F=E8=8F=9C=E5=8D=95=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=96=B0=E5=A2=9E=E8=8F=9C=E5=8D=95=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=B8=8E=E7=88=B6=E7=BA=A7ID=E6=9F=A5=E8=AF=A2=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/system/service/impl/SysMenuServiceImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java index 40643e13d..19048abc4 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java @@ -71,6 +71,8 @@ public class SysMenuServiceImpl implements ISysMenuService { .like(StringUtils.isNotBlank(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName()) .eq(StringUtils.isNotBlank(menu.getVisible()), SysMenu::getVisible, menu.getVisible()) .eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus()) + .eq(StringUtils.isNotBlank(menu.getMenuType()), SysMenu::getMenuType, menu.getMenuType()) + .eq(ObjectUtil.isNotNull(menu.getParentId()), SysMenu::getParentId, menu.getParentId()) .orderByAsc(SysMenu::getParentId) .orderByAsc(SysMenu::getOrderNum)); } else { @@ -79,6 +81,8 @@ public class SysMenuServiceImpl implements ISysMenuService { .like(StringUtils.isNotBlank(menu.getMenuName()), "m.menu_name", menu.getMenuName()) .eq(StringUtils.isNotBlank(menu.getVisible()), "m.visible", menu.getVisible()) .eq(StringUtils.isNotBlank(menu.getStatus()), "m.status", menu.getStatus()) + .eq(StringUtils.isNotBlank(menu.getMenuType()), "m.menu_type", menu.getMenuType()) + .eq(ObjectUtil.isNotNull(menu.getParentId()), "m.parent_id", menu.getParentId()) .orderByAsc("m.parent_id") .orderByAsc("m.order_num"); List list = baseMapper.selectMenuListByUserId(wrapper); From 5e510773475b47fe4375db143583da1e6b08c1ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E8=BE=9E=E6=9C=AA=E5=AF=92?= <545073804@qq.com> Date: Tue, 29 Apr 2025 19:01:45 +0800 Subject: [PATCH 051/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E4=BB=8EClassPath=E5=8A=A0=E8=BD=BDip2region=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/core/utils/ip/RegionUtils.java | 44 ++++++------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java index 6e2a44e00..c9e867899 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java @@ -1,15 +1,12 @@ package org.dromara.common.core.utils.ip; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.io.resource.ClassPathResource; -import cn.hutool.core.util.ObjectUtil; -import org.dromara.common.core.exception.ServiceException; -import org.dromara.common.core.utils.file.FileUtils; +import cn.hutool.core.io.resource.NoResourceException; +import cn.hutool.core.io.resource.ResourceUtil; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; import org.lionsoul.ip2region.xdb.Searcher; -import java.io.File; - /** * 根据ip地址定位工具类,离线方式 * 参考地址:集成 ip2region 实现离线IP地址定位库 @@ -19,31 +16,19 @@ import java.io.File; @Slf4j public class RegionUtils { + // IP地址库文件名称 + public static final String IP_XDB_FILENAME = "ip2region.xdb"; + private static final Searcher SEARCHER; static { - String fileName = "/ip2region.xdb"; - File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName); - if (!FileUtils.exist(existFile)) { - ClassPathResource fileStream = new ClassPathResource(fileName); - if (ObjectUtil.isEmpty(fileStream.getStream())) { - throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!"); - } - FileUtils.writeFromStream(fileStream.getStream(), existFile); - } - - String dbPath = existFile.getPath(); - - // 1、从 dbPath 加载整个 xdb 到内存。 - byte[] cBuff; try { - cBuff = Searcher.loadContentFromFile(dbPath); - } catch (Exception e) { - throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage()); - } - // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。 - try { - SEARCHER = Searcher.newWithBuffer(cBuff); + // 1、将 ip2region 数据库文件 xdb 从 ClassPath 加载到内存。 + // 2、基于加载到内存的 xdb 数据创建一个 Searcher 查询对象。 + SEARCHER = Searcher.newWithBuffer(ResourceUtil.readBytes(IP_XDB_FILENAME)); + log.info("RegionUtils初始化成功,加载IP地址库数据成功!"); + } catch (NoResourceException e) { + throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!"); } catch (Exception e) { throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage()); } @@ -54,9 +39,8 @@ public class RegionUtils { */ public static String getCityInfo(String ip) { try { - ip = ip.trim(); // 3、执行查询 - String region = SEARCHER.search(ip); + String region = SEARCHER.search(StringUtils.trim(ip)); return region.replace("0|", "").replace("|0", ""); } catch (Exception e) { log.error("IP地址离线获取城市异常 {}", ip); From 53827228679935ecc41525a494c131594d62f1c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Tue, 6 May 2025 13:29:57 +0800 Subject: [PATCH 052/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=A4=9A?= =?UTF-8?q?=E7=A7=9F=E6=88=B7=E5=BF=BD=E7=95=A5=E8=A1=A8=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=BF=BD=E7=95=A5=E5=A4=A7=E5=B0=8F=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/common/tenant/handle/PlusTenantLineHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java index 6c93ee581..d518becbc 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java @@ -48,7 +48,7 @@ public class PlusTenantLineHandler implements TenantLineHandler { "gen_table_column" ); tables.addAll(excludes); - return tables.contains(tableName); + return StringUtils.containsAnyIgnoreCase(tableName, tables.toArray(new String[0])); } return true; } From 053dc50c4da344df987c95dba5c0b253e0d17c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Thu, 8 May 2025 22:14:07 +0800 Subject: [PATCH 053/121] =?UTF-8?q?update=20=E6=9B=B4=E6=96=B0=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cc50af628..fb0a97c76 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ > 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system) -> 官方前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
+> 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)
> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5) > 文档地址: [plus-doc](https://plus-doc.dromara.org) From a09414110e3531bd7a9e3861c6a19b1eec8bcc11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Thu, 8 May 2025 23:14:07 +0800 Subject: [PATCH 054/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20compose?= =?UTF-8?q?=E7=BC=96=E6=8E=92=E5=A2=9E=E5=8A=A0snailjob=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E9=98=B2=E6=AD=A2=E9=9B=86=E7=BE=A4=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/docker/docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml index fb35cae6f..3814a2d89 100644 --- a/script/docker/docker-compose.yml +++ b/script/docker/docker-compose.yml @@ -104,6 +104,7 @@ services: # 时区上海 TZ: Asia/Shanghai SERVER_PORT: 8080 + SNAIL_PORT: 28080 volumes: # 配置文件 - /docker/server1/logs/:/ruoyi/server/logs/ @@ -119,6 +120,7 @@ services: # 时区上海 TZ: Asia/Shanghai SERVER_PORT: 8081 + SNAIL_PORT: 28081 volumes: # 配置文件 - /docker/server2/logs/:/ruoyi/server/logs/ From b709bc02147f6f2c379d77416d2106369bcdfb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 9 May 2025 15:00:29 +0800 Subject: [PATCH 055/121] =?UTF-8?q?update=20=E6=9B=B4=E6=96=B0=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fb0a97c76..2e897b59f 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ > 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)
> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5) -> 文档地址: [plus-doc](https://plus-doc.dromara.org) +> 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网) ## 赞助商 From 3749e7e7243a1245b9150c3bafac8c0169f28d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 9 May 2025 16:04:05 +0800 Subject: [PATCH 056/121] =?UTF-8?q?update=20=E6=9B=B4=E6=96=B0=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e897b59f..5b132c662 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.3.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) +[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.3.1-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.4-blue.svg)]() [![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() [![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]() From eac7f1b4e22125bcbd94dc2ca4bb9c886eba2c99 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Sat, 10 May 2025 14:48:34 +0800 Subject: [PATCH 057/121] =?UTF-8?q?docs=20=E4=BC=98=E5=8C=96EncryptUtils?= =?UTF-8?q?=E5=8A=A0=E8=A7=A3=E5=AF=86=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/common/encrypt/utils/EncryptUtils.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java index 2a096eea7..ff0fbc812 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java @@ -108,7 +108,7 @@ public class EncryptUtils { } /** - * sm4加密 + * SM4加密(Base64编码) * * @param data 待加密数据 * @param password 秘钥字符串 @@ -127,11 +127,11 @@ public class EncryptUtils { } /** - * sm4加密 + * SM4加密(Hex编码) * * @param data 待加密数据 * @param password 秘钥字符串 - * @return 加密后字符串, 采用Base64编码 + * @return 加密后字符串, 采用Hex编码 */ public static String encryptBySm4Hex(String data, String password) { if (StrUtil.isBlank(password)) { @@ -148,7 +148,7 @@ public class EncryptUtils { /** * sm4解密 * - * @param data 待解密数据 + * @param data 待解密数据(可以是Base64或Hex编码) * @param password 秘钥字符串 * @return 解密后字符串 */ From 5a8dc8e1cfc1999ef677b75da3417d48d96eaa87 Mon Sep 17 00:00:00 2001 From: gssong <1742057357@qq.com> Date: Sat, 10 May 2025 15:51:17 +0800 Subject: [PATCH 058/121] =?UTF-8?q?update=20=E5=8D=87=E7=BA=A7warm-flow1.7?= =?UTF-8?q?.0=20update=20=E8=B0=83=E6=95=B4=E6=B5=81=E7=A8=8B=E6=92=A4?= =?UTF-8?q?=E9=94=80=20remove=20=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../workflow/service/IFlwCommonService.java | 20 ++----- .../service/impl/FlwCommonServiceImpl.java | 56 +++---------------- .../service/impl/FlwInstanceServiceImpl.java | 27 ++++----- .../service/impl/FlwTaskServiceImpl.java | 6 +- 5 files changed, 31 insertions(+), 80 deletions(-) diff --git a/pom.xml b/pom.xml index ebec03645..a9b5296e8 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ 8.7.2-20250101 - 1.6.10 + 1.7.0 3.2.2 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java index 73201f43f..4d3540b91 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java @@ -1,9 +1,11 @@ package org.dromara.workflow.service; +import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.User; import org.dromara.warm.flow.core.service.UserService; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -48,17 +50,6 @@ public interface IFlwCommonService { */ void sendMessage(String flowName, Long instId, List messageType, String message); - /** - * 驳回 - * - * @param message 审批意见 - * @param instanceId 流程实例id - * @param targetNodeCode 目标节点 - * @param flowStatus 流程状态 - * @param flowHisStatus 节点操作状态 - */ - void backTask(String message, Long instanceId, String targetNodeCode, String flowStatus, String flowHisStatus); - /** * 申请人节点编码 * @@ -68,9 +59,10 @@ public interface IFlwCommonService { String applyNodeCode(Long definitionId); /** - * 删除运行中的任务 + * 合并变量 * - * @param taskIds 任务id + * @param instance 流程实例 + * @param variable 变量 */ - void deleteRunTask(List taskIds); + void mergeVariable(Instance instance, Map variable); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index df4893d9b..5c77d2c08 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -12,8 +12,9 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mail.utils.MailUtils; import org.dromara.common.sse.dto.SseMessageDto; import org.dromara.common.sse.utils.SseMessageUtils; +import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.constant.ExceptionCons; -import org.dromara.warm.flow.core.dto.FlowParams; +import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Node; import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.core.entity.User; @@ -23,6 +24,7 @@ import org.dromara.warm.flow.core.service.NodeService; import org.dromara.warm.flow.core.service.TaskService; import org.dromara.warm.flow.core.service.UserService; import org.dromara.warm.flow.core.utils.AssertUtil; +import org.dromara.warm.flow.core.utils.MapUtil; import org.dromara.warm.flow.orm.entity.FlowNode; import org.dromara.warm.flow.orm.entity.FlowTask; import org.dromara.warm.flow.orm.entity.FlowUser; @@ -177,42 +179,6 @@ public class FlwCommonServiceImpl implements IFlwCommonService { } } - /** - * 驳回 - * - * @param message 审批意见 - * @param instanceId 流程实例id - * @param targetNodeCode 目标节点 - * @param flowStatus 流程状态 - * @param flowHisStatus 节点操作状态 - */ - @Override - public void backTask(String message, Long instanceId, String targetNodeCode, String flowStatus, String flowHisStatus) { - IFlwTaskService flwTaskService = SpringUtils.getBean(IFlwTaskService.class); - List list = flwTaskService.selectByInstId(instanceId); - if (CollUtil.isNotEmpty(list)) { - List tasks = StreamUtils.filter(list, e -> e.getNodeCode().equals(targetNodeCode)); - if (list.size() == tasks.size()) { - return; - } - } - for (FlowTask task : list) { - List userList = flwTaskService.currentTaskAllUser(task.getId()); - FlowParams flowParams = FlowParams.build() - .nodeCode(targetNodeCode) - .message(message) - .skipType(SkipType.PASS.getKey()) - .flowStatus(flowStatus).hisStatus(flowHisStatus) - .ignore(true); - //解决会签没权限问题 - if (CollUtil.isNotEmpty(userList)) { - flowParams.handler(userList.get(0).getUserId().toString()); - } - taskService.skip(task.getId(), flowParams); - } - //解决会签多人审批问题 - backTask(message, instanceId, targetNodeCode, flowStatus, flowHisStatus); - } /** * 申请人节点编码 @@ -231,17 +197,13 @@ public class FlwCommonServiceImpl implements IFlwCommonService { return nextNode.getNodeCode(); } - /** - * 删除运行中的任务 - * - * @param taskIds 任务id - */ @Override - public void deleteRunTask(List taskIds) { - if (CollUtil.isEmpty(taskIds)) { - return; + public void mergeVariable(Instance instance, Map variable) { + if (MapUtil.isNotEmpty(variable)) { + String variableStr = instance.getVariable(); + Map deserialize = FlowEngine.jsonConvert.strToMap(variableStr); + deserialize.putAll(variable); + instance.setVariable(FlowEngine.jsonConvert.objToStr(deserialize)); } - userService.deleteByTaskIds(taskIds); - flowTaskMapper.deleteByIds(taskIds); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index 2aa95484f..e50744005 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -19,13 +19,13 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; -import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.constant.ExceptionCons; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.entity.Definition; import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.core.enums.NodeType; +import org.dromara.warm.flow.core.enums.SkipType; import org.dromara.warm.flow.core.service.ChartService; import org.dromara.warm.flow.core.service.DefService; import org.dromara.warm.flow.core.service.InsService; @@ -185,7 +185,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { @Override @Transactional(rollbackFor = Exception.class) public boolean deleteByBusinessIds(List businessIds) { - List flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper().in(FlowInstance::getBusinessId, StreamUtils.toList(businessIds,Convert::toStr))); + List flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper().in(FlowInstance::getBusinessId, StreamUtils.toList(businessIds, Convert::toStr))); if (CollUtil.isEmpty(flowInstances)) { log.warn("未找到对应的流程实例信息,无法执行删除操作。"); return false; @@ -244,19 +244,16 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { throw new ServiceException(ExceptionCons.NOT_FOUNT_DEF); } String message = bo.getMessage(); + String userIdStr = LoginHelper.getUserIdStr(); BusinessStatusEnum.checkCancelStatus(instance.getFlowStatus()); - String applyNodeCode = flwCommonService.applyNodeCode(definition.getId()); - //撤销 - flwCommonService.backTask(message, instance.getId(), applyNodeCode, BusinessStatusEnum.CANCEL.getStatus(), BusinessStatusEnum.CANCEL.getStatus()); - //判断或签节点是否有多个,只保留一个 - List currentTaskList = taskService.list(FlowEngine.newTask().setInstanceId(instance.getId())); - if (CollUtil.isNotEmpty(currentTaskList)) { - if (currentTaskList.size() > 1) { - currentTaskList.remove(0); - flwCommonService.deleteRunTask(StreamUtils.toList(currentTaskList, Task::getId)); - } - } - + FlowParams flowParams = FlowParams.build() + .message(message) + .skipType(SkipType.PASS.getKey()) + .flowStatus(BusinessStatusEnum.CANCEL.getStatus()) + .hisStatus(BusinessStatusEnum.CANCEL.getStatus()) + .handler(userIdStr) + .ignore(true); + taskService.revoke(instance.getId(), flowParams); } catch (Exception e) { log.error("撤销失败: {}", e.getMessage(), e); throw new ServiceException(e.getMessage()); @@ -363,7 +360,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { public void setVariable(Long instanceId, Map variable) { Instance instance = insService.getById(instanceId); if (instance != null) { - taskService.mergeVariable(instance, variable); + flwCommonService.mergeVariable(instance, variable); insService.updateById(instance); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 5685ac3dc..77c7fcecf 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -112,7 +112,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { if (ObjectUtil.isNotNull(flowInstance)) { BusinessStatusEnum.checkStartStatus(flowInstance.getFlowStatus()); List taskList = taskService.list(new FlowTask().setInstanceId(flowInstance.getId())); - taskService.mergeVariable(flowInstance, variables); + flwCommonService.mergeVariable(flowInstance, variables); insService.updateById(flowInstance); StartProcessReturnDTO dto = new StartProcessReturnDTO(); dto.setProcessInstanceId(taskList.get(0).getInstanceId()); @@ -217,7 +217,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { variableMap.remove(task.getNodeCode()); } } - taskService.mergeVariable(inst, variableMap); + flwCommonService.mergeVariable(inst, variableMap); } /** @@ -594,7 +594,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { nextFlowNodes = StreamUtils.filter(nextFlowNodes, node -> NodeType.BETWEEN.getKey().equals(node.getNodeType())); if (CollUtil.isNotEmpty(nextNodeList)) { // 构建以下节点数据 - List buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, null)); + List buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, FlowParams.build())); // 办理人变量替换 ExpressionUtil.evalVariable(buildNextTaskList, mergeVariable); for (FlowNode flowNode : nextFlowNodes) { From d6758dc47b51ae14b93ac15c1a5a436cd1958779 Mon Sep 17 00:00:00 2001 From: gssong <1742057357@qq.com> Date: Sat, 10 May 2025 16:41:50 +0800 Subject: [PATCH 059/121] =?UTF-8?q?update=20=E8=B0=83=E6=95=B4=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E7=94=B3=E8=AF=B7=E4=BA=BA=E8=8A=82=E7=82=B9=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/FlwCommonServiceImpl.java | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index 5c77d2c08..086ae6870 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -2,7 +2,6 @@ package org.dromara.workflow.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.dto.UserDTO; @@ -13,23 +12,16 @@ import org.dromara.common.mail.utils.MailUtils; import org.dromara.common.sse.dto.SseMessageDto; import org.dromara.common.sse.utils.SseMessageUtils; import org.dromara.warm.flow.core.FlowEngine; -import org.dromara.warm.flow.core.constant.ExceptionCons; import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Node; import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.core.entity.User; -import org.dromara.warm.flow.core.enums.NodeType; import org.dromara.warm.flow.core.enums.SkipType; import org.dromara.warm.flow.core.service.NodeService; -import org.dromara.warm.flow.core.service.TaskService; import org.dromara.warm.flow.core.service.UserService; -import org.dromara.warm.flow.core.utils.AssertUtil; import org.dromara.warm.flow.core.utils.MapUtil; -import org.dromara.warm.flow.orm.entity.FlowNode; import org.dromara.warm.flow.orm.entity.FlowTask; import org.dromara.warm.flow.orm.entity.FlowUser; -import org.dromara.warm.flow.orm.mapper.FlowNodeMapper; -import org.dromara.warm.flow.orm.mapper.FlowTaskMapper; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.enums.MessageTypeEnum; import org.dromara.workflow.common.enums.TaskAssigneeType; @@ -52,11 +44,7 @@ import java.util.stream.Collectors; @RequiredArgsConstructor @Service public class FlwCommonServiceImpl implements IFlwCommonService { - - private final FlowNodeMapper flowNodeMapper; - private final FlowTaskMapper flowTaskMapper; private final UserService userService; - private final TaskService taskService; private final NodeService nodeService; /** @@ -188,11 +176,7 @@ public class FlwCommonServiceImpl implements IFlwCommonService { */ @Override public String applyNodeCode(Long definitionId) { - //获取已发布的流程节点 - List flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper().eq(FlowNode::getDefinitionId, definitionId)); - AssertUtil.isTrue(CollUtil.isEmpty(flowNodes), ExceptionCons.NOT_PUBLISH_NODE); - Node startNode = flowNodes.stream().filter(t -> NodeType.isStart(t.getNodeType())).findFirst().orElse(null); - AssertUtil.isNull(startNode, ExceptionCons.LOST_START_NODE); + Node startNode = nodeService.getStartNode(definitionId); Node nextNode = nodeService.getNextNode(definitionId, startNode.getNodeCode(), null, SkipType.PASS.getKey()); return nextNode.getNodeCode(); } From 9df837f0473ba3162164077cbd8565710846f550 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Sat, 10 May 2025 16:51:56 +0800 Subject: [PATCH 060/121] =?UTF-8?q?update=20=E9=87=8D=E6=9E=84=E5=8A=9E?= =?UTF-8?q?=E7=90=86=E4=BA=BA=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../common/core/service/UserService.java | 33 +++++ .../service/impl/SysUserServiceImpl.java | 82 ++++++++++- .../service/IFlwTaskAssigneeService.java | 15 +- .../impl/FlwTaskAssigneeServiceImpl.java | 136 ++++++++++++++---- script/sql/oracle/oracle_ry_workflow.sql | 30 ++-- script/sql/postgres/postgres_ry_workflow.sql | 10 +- script/sql/ry_workflow.sql | 13 +- .../sql/sqlserver/sqlserver_ry_workflow.sql | 15 +- script/sql/update/update_5.3.1-5.3.2.sql | 8 ++ 10 files changed, 292 insertions(+), 52 deletions(-) create mode 100644 script/sql/update/update_5.3.1-5.3.2.sql diff --git a/pom.xml b/pom.xml index a9b5296e8..311e17011 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ 1.2.83 8.7.2-20250101 - + 1.7.0 diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java index 67cd54fba..4903c3860 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java @@ -3,6 +3,7 @@ package org.dromara.common.core.service; import org.dromara.common.core.domain.dto.UserDTO; import java.util.List; +import java.util.Map; /** * 通用 用户服务 @@ -91,4 +92,36 @@ public interface UserService { */ List selectUsersByPostIds(List postIds); + /** + * 根据用户 ID 列表查询用户名称映射关系 + * + * @param userIds 用户 ID 列表 + * @return Map,其中 key 为用户 ID,value 为对应的用户名称 + */ + Map selectUserNamesByIds(List userIds); + + /** + * 根据角色 ID 列表查询角色名称映射关系 + * + * @param roleIds 角色 ID 列表 + * @return Map,其中 key 为角色 ID,value 为对应的角色名称 + */ + Map selectRoleNamesByIds(List roleIds); + + /** + * 根据部门 ID 列表查询部门名称映射关系 + * + * @param deptIds 部门 ID 列表 + * @return Map,其中 key 为部门 ID,value 为对应的部门名称 + */ + Map selectDeptNamesByIds(List deptIds); + + /** + * 根据岗位 ID 列表查询岗位名称映射关系 + * + * @param postIds 岗位 ID 列表 + * @return Map,其中 key 为岗位 ID,value 为对应的岗位名称 + */ + Map selectPostNamesByIds(List postIds); + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java index cecd7f56f..e493e426c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java @@ -35,10 +35,8 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.stream.Collectors; /** * 用户 业务层处理 @@ -720,4 +718,80 @@ public class SysUserServiceImpl implements ISysUserService, UserService { return selectListByIds(new ArrayList<>(userIds)); } + /** + * 根据用户 ID 列表查询用户名称映射关系 + * + * @param userIds 用户 ID 列表 + * @return Map,其中 key 为用户 ID,value 为对应的用户名称 + */ + @Override + public Map selectUserNamesByIds(List userIds) { + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyMap(); + } + return baseMapper.selectList( + new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getNickName) + .in(SysUser::getUserId, userIds) + ).stream() + .collect(Collectors.toMap(SysUser::getUserId, SysUser::getNickName)); + } + + /** + * 根据角色 ID 列表查询角色名称映射关系 + * + * @param roleIds 角色 ID 列表 + * @return Map,其中 key 为角色 ID,value 为对应的角色名称 + */ + @Override + public Map selectRoleNamesByIds(List roleIds) { + if (CollUtil.isEmpty(roleIds)) { + return Collections.emptyMap(); + } + return roleMapper.selectList( + new LambdaQueryWrapper() + .select(SysRole::getRoleId, SysRole::getRoleName) + .in(SysRole::getRoleId, roleIds) + ).stream() + .collect(Collectors.toMap(SysRole::getRoleId, SysRole::getRoleName)); + } + + /** + * 根据部门 ID 列表查询部门名称映射关系 + * + * @param deptIds 部门 ID 列表 + * @return Map,其中 key 为部门 ID,value 为对应的部门名称 + */ + @Override + public Map selectDeptNamesByIds(List deptIds) { + if (CollUtil.isEmpty(deptIds)) { + return Collections.emptyMap(); + } + return deptMapper.selectList( + new LambdaQueryWrapper() + .select(SysDept::getDeptId, SysDept::getDeptName) + .in(SysDept::getDeptId, deptIds) + ).stream() + .collect(Collectors.toMap(SysDept::getDeptId, SysDept::getDeptName)); + } + + /** + * 根据岗位 ID 列表查询岗位名称映射关系 + * + * @param postIds 岗位 ID 列表 + * @return Map,其中 key 为岗位 ID,value 为对应的岗位名称 + */ + @Override + public Map selectPostNamesByIds(List postIds) { + if (CollUtil.isEmpty(postIds)) { + return Collections.emptyMap(); + } + return postMapper.selectList( + new LambdaQueryWrapper() + .select(SysPost::getPostId, SysPost::getPostName) + .in(SysPost::getPostId, postIds) + ).stream() + .collect(Collectors.toMap(SysPost::getPostId, SysPost::getPostName)); + } + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java index 116cb74f3..90d87e37f 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java @@ -13,10 +13,21 @@ public interface IFlwTaskAssigneeService { /** * 根据存储标识符(storageId)解析分配类型和ID,并获取对应的用户列表 + * 支持单个标识(例如 "user:123" 或 "456"),格式非法将返回空列表 * - * @param storageId 包含分配类型和ID的字符串(例如 "user:123" 或 "role:456") - * @return 与分配类型和ID匹配的用户列表,如果格式无效则返回空列表 + * @param storageId 包含分配类型和ID的字符串 + * @return 匹配的用户列表,格式非法返回空列表 */ List fetchUsersByStorageId(String storageId); + /** + * 批量解析多个存储标识符(storageIds),按类型分类并合并查询用户列表 + * 输入格式支持多个以逗号分隔的标识(如 "user:123,role:456,789") + * 会自动去重返回结果,非法格式的标识将被忽略 + * + * @param storageIds 多个存储标识符字符串(逗号分隔) + * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 + */ + List fetchUsersByStorageIds(String storageIds); + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java index 1e0d17278..67c58b001 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java @@ -2,6 +2,7 @@ package org.dromara.workflow.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Pair; import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -10,7 +11,6 @@ import org.dromara.common.core.domain.dto.TaskAssigneeDTO; import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.domain.model.TaskAssigneeBody; import org.dromara.common.core.enums.FormatsType; -import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.DeptService; import org.dromara.common.core.service.TaskAssigneeService; import org.dromara.common.core.service.UserService; @@ -20,6 +20,7 @@ import org.dromara.warm.flow.ui.dto.HandlerFunDto; import org.dromara.warm.flow.ui.dto.HandlerQuery; import org.dromara.warm.flow.ui.dto.TreeFunDto; import org.dromara.warm.flow.ui.service.HandlerSelectService; +import org.dromara.warm.flow.ui.vo.HandlerFeedBackVo; import org.dromara.warm.flow.ui.vo.HandlerSelectVo; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.enums.TaskAssigneeEnum; @@ -74,6 +75,49 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand return getHandlerSelectVo(buildHandlerData(dto, type), buildDeptTree(depts)); } + /** + * 办理人权限名称回显 + * + * @param storageIds 入库主键集合 + * @return 结果 + */ + @Override + public List handlerFeedback(List storageIds) { + if (CollUtil.isEmpty(storageIds)) { + return Collections.emptyList(); + } + // 解析并归类 ID,同时记录原始顺序和对应解析结果 + Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); + Map> parsedMap = new LinkedHashMap<>(); + for (String storageId : storageIds) { + Pair parsed = this.parseStorageId(storageId); + parsedMap.put(storageId, parsed); + if (parsed != null) { + typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); + } + } + + // 查询所有类型对应的 ID 名称映射 + Map> nameMap = new EnumMap<>(TaskAssigneeEnum.class); + typeIdMap.forEach((type, ids) -> nameMap.put(type, this.getNamesByType(type, ids))); + + // 组装返回结果,保持原始顺序 + return parsedMap.entrySet().stream() + .map(entry -> { + String storageId = entry.getKey(); + Pair parsed = entry.getValue(); + String handlerName = "格式错误"; + if (parsed != null) { + Map nameMapping = nameMap.getOrDefault(parsed.getKey(), Collections.emptyMap()); + handlerName = nameMapping.getOrDefault(parsed.getValue(), "未知名称"); + } + HandlerFeedBackVo backVo = new HandlerFeedBackVo(); + backVo.setStorageId(storageId); + backVo.setHandlerName(handlerName); + return backVo; + }).toList(); + } + /** * 根据任务办理类型查询对应的数据 */ @@ -83,7 +127,6 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand case ROLE -> taskAssigneeService.selectRolesByTaskAssigneeList(taskQuery); case DEPT -> taskAssigneeService.selectDeptsByTaskAssigneeList(taskQuery); case POST -> taskAssigneeService.selectPostsByTaskAssigneeList(taskQuery); - default -> throw new ServiceException("Unsupported handler type"); }; } @@ -124,33 +167,41 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand /** * 根据存储标识符(storageId)解析分配类型和ID,并获取对应的用户列表 + * 支持单个标识(例如 "user:123" 或 "456"),格式非法将返回空列表 * - * @param storageId 包含分配类型和ID的字符串(例如 "user:123" 或 "role:456") - * @return 与分配类型和ID匹配的用户列表,如果格式无效则返回空列表 + * @param storageId 包含分配类型和ID的字符串 + * @return 匹配的用户列表,格式非法返回空列表 */ @Override public List fetchUsersByStorageId(String storageId) { - List list = new ArrayList<>(); - Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); - for (String str : storageId.split(StrUtil.COMMA)) { - String[] parts = str.split(StrUtil.COLON, 2); - TaskAssigneeEnum type; - Long id; - if (parts.length < 2) { - // 无前缀时默认是用户类型 - type = TaskAssigneeEnum.USER; - id = Long.valueOf(parts[0]); - } else { - // 根据前缀解析类型(如 "role:123" -> ROLE 类型) - type = TaskAssigneeEnum.fromCode(parts[0] + StrUtil.COLON); - id = Long.valueOf(parts[1]); - } - typeIdMap.computeIfAbsent(type, k -> new ArrayList<>()).add(id); + Pair parsed = this.parseStorageId(storageId); + if (parsed == null) { + return Collections.emptyList(); } - typeIdMap.entrySet().stream() - .filter(entry -> CollUtil.isNotEmpty(entry.getValue())) - .forEach(entry -> list.addAll(getUsersByType(entry.getKey(), entry.getValue()))); - return list.stream().distinct().toList(); + return this.getUsersByType(parsed.getKey(), Collections.singletonList(parsed.getValue())); + } + + /** + * 批量解析多个存储标识符(storageIds),按类型分类并合并查询用户列表 + * 输入格式支持多个以逗号分隔的标识(如 "user:123,role:456,789") + * 会自动去重返回结果,非法格式的标识将被忽略 + * + * @param storageIds 多个存储标识符字符串(逗号分隔) + * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 + */ + @Override + public List fetchUsersByStorageIds(String storageIds) { + Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); + for (String storageId : storageIds.split(StrUtil.COMMA)) { + Pair parsed = this.parseStorageId(storageId); + if (parsed != null) { + typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); + } + } + return typeIdMap.entrySet().stream() + .flatMap(entry -> this.getUsersByType(entry.getKey(), entry.getValue()).stream()) + .distinct() + .toList(); } /** @@ -172,4 +223,41 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand }; } + /** + * 根据任务分配类型和对应 ID 列表,批量查询名称映射关系 + * + * @param type 分配类型(用户、角色、部门、岗位) + * @param ids ID 列表(如用户ID、角色ID等) + * @return 返回 Map,其中 key 为 ID,value 为对应的名称 + */ + private Map getNamesByType(TaskAssigneeEnum type, List ids) { + return switch (type) { + case USER -> userService.selectUserNamesByIds(ids); + case ROLE -> userService.selectRoleNamesByIds(ids); + case DEPT -> userService.selectDeptNamesByIds(ids); + case POST -> userService.selectPostNamesByIds(ids); + }; + } + + /** + * 解析 storageId 字符串,返回类型和ID的组合 + * + * @param storageId 例如 "user:123" 或 "456" + * @return Pair(TaskAssigneeEnum, Long),如果格式非法返回 null + */ + private Pair parseStorageId(String storageId) { + try { + String[] parts = storageId.split(StrUtil.COLON, 2); + if (parts.length < 2) { + return Pair.of(TaskAssigneeEnum.USER, Long.valueOf(parts[0])); + } else { + TaskAssigneeEnum type = TaskAssigneeEnum.fromCode(parts[0] + StrUtil.COLON); + return Pair.of(type, Long.valueOf(parts[1])); + } + } catch (Exception e) { + log.warn("解析 storageId 失败,格式非法:{},错误信息:{}", storageId, e.getMessage()); + return null; + } + } + } diff --git a/script/sql/oracle/oracle_ry_workflow.sql b/script/sql/oracle/oracle_ry_workflow.sql index d982f03ed..7925fc3e4 100644 --- a/script/sql/oracle/oracle_ry_workflow.sql +++ b/script/sql/oracle/oracle_ry_workflow.sql @@ -1,3 +1,6 @@ +-- ---------------------------- +-- 0、warm-flow-all.sql,地址:https://gitee.com/dromara/warm-flow/blob/master/sql/oracle/oracle-wram-flow-all.sql +-- ---------------------------- create table FLOW_DEFINITION ( ID NUMBER(20) not null, @@ -18,7 +21,8 @@ create table FLOW_DEFINITION TENANT_ID VARCHAR2(40) ); -alter table FLOW_DEFINITION add constraint PK_FLOW_DEFINITION primary key (ID); +alter table FLOW_DEFINITION + add constraint PK_FLOW_DEFINITION primary key (ID); comment on table FLOW_DEFINITION is '流程定义表'; comment on column FLOW_DEFINITION.ID is '主键id'; @@ -63,7 +67,8 @@ create table FLOW_NODE PERMISSION_FLAG VARCHAR2(200) ); -alter table FLOW_NODE add constraint PK_FLOW_NODE primary key (ID); +alter table FLOW_NODE + add constraint PK_FLOW_NODE primary key (ID); comment on table FLOW_NODE is '流程节点表'; comment on column FLOW_NODE.ID is '主键id'; @@ -106,7 +111,8 @@ create table FLOW_SKIP TENANT_ID VARCHAR2(40) ); -alter table FLOW_SKIP add constraint PK_FLOW_SKIP primary key (ID); +alter table FLOW_SKIP + add constraint PK_FLOW_SKIP primary key (ID); comment on table FLOW_SKIP is '节点跳转关联表'; comment on column FLOW_SKIP.ID is '主键id'; @@ -144,7 +150,8 @@ create table FLOW_INSTANCE TENANT_ID VARCHAR2(40) ); -alter table FLOW_INSTANCE add constraint PK_FLOW_INSTANCE primary key (ID); +alter table FLOW_INSTANCE + add constraint PK_FLOW_INSTANCE primary key (ID); comment on table FLOW_INSTANCE is '流程实例表'; comment on column FLOW_INSTANCE.ID is '主键id'; @@ -154,7 +161,7 @@ comment on column FLOW_INSTANCE.NODE_TYPE is '开始节点类型 (0开始节点 comment on column FLOW_INSTANCE.NODE_CODE is '开始节点编码'; comment on column FLOW_INSTANCE.NODE_NAME is '开始节点名称'; comment on column FLOW_INSTANCE.VARIABLE is '任务变量'; -comment on column FLOW_INSTANCE.FLOW_STATUS is '流程状态(0待提交 1审批中 2 审批通过 3自动通过 4终止 5作废 6撤销 7取回 8已完成 9已退回 10失效)'; +comment on column FLOW_INSTANCE.FLOW_STATUS is '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; comment on column FLOW_INSTANCE.ACTIVITY_STATUS is '流程激活状态(0挂起 1激活)'; comment on column FLOW_INSTANCE.DEF_JSON is '流程定义json'; comment on column FLOW_INSTANCE.CREATE_BY is '创建者'; @@ -172,6 +179,7 @@ create table FLOW_TASK NODE_CODE VARCHAR2(100), NODE_NAME VARCHAR2(100), NODE_TYPE NUMBER(1), + FLOW_STATUS VARCHAR2(20), FORM_CUSTOM VARCHAR2(1) default 'N', FORM_PATH VARCHAR2(100), CREATE_TIME DATE, @@ -180,7 +188,8 @@ create table FLOW_TASK TENANT_ID VARCHAR2(40) ); -alter table FLOW_TASK add constraint PK_FLOW_TASK primary key (ID); +alter table FLOW_TASK + add constraint PK_FLOW_TASK primary key (ID); comment on table FLOW_TASK is '待办任务表'; comment on column FLOW_TASK.ID is '主键id'; @@ -189,6 +198,7 @@ comment on column FLOW_TASK.INSTANCE_ID is '对应flow_instance表的id'; comment on column FLOW_TASK.NODE_CODE is '节点编码'; comment on column FLOW_TASK.NODE_NAME is '节点名称'; comment on column FLOW_TASK.NODE_TYPE is '节点类型 (0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +comment on column FLOW_TASK.FLOW_STATUS is '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; comment on column FLOW_TASK.FORM_CUSTOM is '审批表单是否自定义 (Y是 N否)'; comment on column FLOW_TASK.FORM_PATH is '审批表单路径'; comment on column FLOW_TASK.CREATE_TIME is '创建时间'; @@ -224,7 +234,8 @@ create table FLOW_HIS_TASK ); -alter table FLOW_HIS_TASK add constraint PK_FLOW_HIS_TASK primary key (ID); +alter table FLOW_HIS_TASK + add constraint PK_FLOW_HIS_TASK primary key (ID); comment on table FLOW_HIS_TASK is '历史任务记录表'; comment on column FLOW_HIS_TASK.ID is '主键id'; @@ -237,7 +248,7 @@ comment on column FLOW_HIS_TASK.NODE_TYPE is '开始节点类型 (0开始节点 comment on column FLOW_HIS_TASK.TARGET_NODE_CODE is '目标节点编码'; comment on column FLOW_HIS_TASK.TARGET_NODE_NAME is '目标节点名称'; comment on column FLOW_HIS_TASK.SKIP_TYPE is '流转类型(PASS通过 REJECT退回 NONE无动作)'; -comment on column FLOW_HIS_TASK.FLOW_STATUS is '流程状态(1审批中 2 审批通过 9已退回 10失效)'; +comment on column FLOW_HIS_TASK.FLOW_STATUS is '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; comment on column FLOW_HIS_TASK.FORM_CUSTOM is '审批表单是否自定义 (Y是 N否)'; comment on column FLOW_HIS_TASK.FORM_PATH is '审批表单路径'; comment on column FLOW_HIS_TASK.MESSAGE is '审批意见'; @@ -264,7 +275,8 @@ create table FLOW_USER TENANT_ID VARCHAR2(40) ); -alter table FLOW_USER add constraint PK_FLOW_USER primary key (ID); +alter table FLOW_USER + add constraint PK_FLOW_USER primary key (ID); comment on table FLOW_USER is '待办任务表'; comment on column FLOW_USER.ID is '主键id'; diff --git a/script/sql/postgres/postgres_ry_workflow.sql b/script/sql/postgres/postgres_ry_workflow.sql index df3d41313..c943024df 100644 --- a/script/sql/postgres/postgres_ry_workflow.sql +++ b/script/sql/postgres/postgres_ry_workflow.sql @@ -154,7 +154,7 @@ COMMENT ON COLUMN flow_instance.node_type IS '节点类型(0开始节点 1中 COMMENT ON COLUMN flow_instance.node_code IS '流程节点编码'; COMMENT ON COLUMN flow_instance.node_name IS '流程节点名称'; COMMENT ON COLUMN flow_instance.variable IS '任务变量'; -COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2 审批通过 3自动通过 4终止 5作废 6撤销 7取回 8已完成 9已退回 10失效)'; +COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; COMMENT ON COLUMN flow_instance.activity_status IS '流程激活状态(0挂起 1激活)'; COMMENT ON COLUMN flow_instance.def_json IS '流程定义json'; COMMENT ON COLUMN flow_instance.create_by IS '创建者'; @@ -172,6 +172,7 @@ CREATE TABLE flow_task node_code varchar(100) NOT NULL, -- 节点编码 node_name varchar(100) NULL, -- 节点名称 node_type int2 NOT NULL, -- 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + flow_status varchar(20) NOT NULL, -- 流程状态(0待提交 1审批中 2 审批通过 8已完成 9已退回 10失效) form_custom bpchar(1) NULL DEFAULT 'N':: character varying, -- 审批表单是否自定义(Y是 N否) form_path varchar(100) NULL, -- 审批表单路径 create_time timestamp NULL, -- 创建时间 @@ -188,6 +189,7 @@ COMMENT ON COLUMN flow_task.instance_id IS '对应flow_instance表的id'; COMMENT ON COLUMN flow_task.node_code IS '节点编码'; COMMENT ON COLUMN flow_task.node_name IS '节点名称'; COMMENT ON COLUMN flow_task.node_type IS '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +COMMENT ON COLUMN flow_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; COMMENT ON COLUMN flow_task.form_custom IS '审批表单是否自定义(Y是 N否)'; COMMENT ON COLUMN flow_task.form_path IS '审批表单路径'; COMMENT ON COLUMN flow_task.create_time IS '创建时间'; @@ -201,8 +203,8 @@ CREATE TABLE flow_his_task definition_id int8 NOT NULL, -- 对应flow_definition表的id instance_id int8 NOT NULL, -- 对应flow_instance表的id task_id int8 NOT NULL, -- 对应flow_task表的id - node_code varchar(200) NULL, -- 开始节点编码 - node_name varchar(200) NULL, -- 开始节点名称 + node_code varchar(100) NULL, -- 开始节点编码 + node_name varchar(100) NULL, -- 开始节点名称 node_type int2 NULL, -- 开始节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) target_node_code varchar(200) NULL, -- 目标节点编码 target_node_name varchar(200) NULL, -- 结束节点名称 @@ -237,7 +239,7 @@ COMMENT ON COLUMN flow_his_task.approver IS '审批者'; COMMENT ON COLUMN flow_his_task.cooperate_type IS '协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)'; COMMENT ON COLUMN flow_his_task.collaborator IS '协作人'; COMMENT ON COLUMN flow_his_task.skip_type IS '流转类型(PASS通过 REJECT退回 NONE无动作)'; -COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(1审批中 2 审批通过 9已退回 10失效)'; +COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; COMMENT ON COLUMN flow_his_task.form_custom IS '审批表单是否自定义(Y是 N否)'; COMMENT ON COLUMN flow_his_task.form_path IS '审批表单路径'; COMMENT ON COLUMN flow_his_task.message IS '审批意见'; diff --git a/script/sql/ry_workflow.sql b/script/sql/ry_workflow.sql index acc297ec5..a9cddeb24 100644 --- a/script/sql/ry_workflow.sql +++ b/script/sql/ry_workflow.sql @@ -3,7 +3,7 @@ -- ---------------------------- CREATE TABLE `flow_definition` ( - `id` bigint unsigned NOT NULL COMMENT '主键id', + `id` bigint NOT NULL COMMENT '主键id', `flow_code` varchar(40) NOT NULL COMMENT '流程编码', `flow_name` varchar(100) NOT NULL COMMENT '流程名称', `category` varchar(100) DEFAULT NULL COMMENT '流程类别', @@ -50,7 +50,7 @@ CREATE TABLE `flow_node` CREATE TABLE `flow_skip` ( - `id` bigint unsigned NOT NULL COMMENT '主键id', + `id` bigint NOT NULL COMMENT '主键id', `definition_id` bigint NOT NULL COMMENT '流程定义id', `now_node_code` varchar(100) NOT NULL COMMENT '当前流程节点的编码', `now_node_type` tinyint(1) DEFAULT NULL COMMENT '当前节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', @@ -76,7 +76,7 @@ CREATE TABLE `flow_instance` `node_code` varchar(40) NOT NULL COMMENT '流程节点编码', `node_name` varchar(100) DEFAULT NULL COMMENT '流程节点名称', `variable` text COMMENT '任务变量', - `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2 审批通过 3自动通过 4终止 5作废 6撤销 7取回 8已完成 9已退回 10失效)', + `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', `activity_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '流程激活状态(0挂起 1激活)', `def_json` text COMMENT '流程定义json', `create_by` varchar(64) DEFAULT '' COMMENT '创建者', @@ -96,6 +96,7 @@ CREATE TABLE `flow_task` `node_code` varchar(100) NOT NULL COMMENT '节点编码', `node_name` varchar(100) DEFAULT NULL COMMENT '节点名称', `node_type` tinyint(1) NOT NULL COMMENT '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', + `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', `form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义(Y是 N否)', `form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径', `create_time` datetime DEFAULT NULL COMMENT '创建时间', @@ -107,7 +108,7 @@ CREATE TABLE `flow_task` CREATE TABLE `flow_his_task` ( - `id` bigint(20) unsigned NOT NULL COMMENT '主键id', + `id` bigint(20) NOT NULL COMMENT '主键id', `definition_id` bigint(20) NOT NULL COMMENT '对应flow_definition表的id', `instance_id` bigint(20) NOT NULL COMMENT '对应flow_instance表的id', `task_id` bigint(20) NOT NULL COMMENT '对应flow_task表的id', @@ -120,7 +121,7 @@ CREATE TABLE `flow_his_task` `cooperate_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)', `collaborator` varchar(40) DEFAULT NULL COMMENT '协作人', `skip_type` varchar(10) NOT NULL COMMENT '流转类型(PASS通过 REJECT退回 NONE无动作)', - `flow_status` varchar(20) NOT NULL COMMENT '流程状态(1审批中 2 审批通过 9已退回 10失效)', + `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', `form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义(Y是 N否)', `form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径', `message` varchar(500) DEFAULT NULL COMMENT '审批意见', @@ -136,7 +137,7 @@ CREATE TABLE `flow_his_task` CREATE TABLE `flow_user` ( - `id` bigint unsigned NOT NULL COMMENT '主键id', + `id` bigint NOT NULL COMMENT '主键id', `type` char(1) NOT NULL COMMENT '人员类型(1待办任务的审批人权限 2待办任务的转办人权限 3待办任务的委托人权限)', `processed_by` varchar(80) DEFAULT NULL COMMENT '权限人', `associated` bigint NOT NULL COMMENT '任务表id', diff --git a/script/sql/sqlserver/sqlserver_ry_workflow.sql b/script/sql/sqlserver/sqlserver_ry_workflow.sql index 96ea62ad3..46f0bf941 100644 --- a/script/sql/sqlserver/sqlserver_ry_workflow.sql +++ b/script/sql/sqlserver/sqlserver_ry_workflow.sql @@ -1,3 +1,6 @@ +-- ---------------------------- +-- 0、warm-flow-all.sql,地址:https://gitee.com/dromara/warm-flow/blob/master/sql/sqlserver/sqlserver.sql +-- ---------------------------- CREATE TABLE flow_definition ( id bigint NOT NULL, flow_code nvarchar(40) NOT NULL, @@ -523,7 +526,7 @@ EXEC sp_addextendedproperty GO EXEC sp_addextendedproperty -'MS_Description', N'流程状态(0待提交 1审批中 2 审批通过 3自动通过 4终止 5作废 6撤销 7取回 8已完成 9已退回 10失效)', +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', 'SCHEMA', N'dbo', 'TABLE', N'flow_instance', 'COLUMN', N'flow_status' @@ -598,6 +601,7 @@ CREATE TABLE flow_task ( node_code nvarchar(100) NOT NULL, node_name nvarchar(100) NULL, node_type tinyint NOT NULL, + flow_status nvarchar(20) NOT NULL, form_custom nchar(1) DEFAULT('N') NULL, form_path nvarchar(100) NULL, create_time datetime2(7) NULL, @@ -653,6 +657,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'node_type' GO +EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'flow_status' +GO + EXEC sp_addextendedproperty 'MS_Description', N'审批表单是否自定义(Y是 N否)', 'SCHEMA', N'dbo', @@ -824,7 +835,7 @@ EXEC sp_addextendedproperty GO EXEC sp_addextendedproperty -'MS_Description', N'流程状态(1审批中 2 审批通过 9已退回 10失效)', +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', 'SCHEMA', N'dbo', 'TABLE', N'flow_his_task', 'COLUMN', N'flow_status' diff --git a/script/sql/update/update_5.3.1-5.3.2.sql b/script/sql/update/update_5.3.1-5.3.2.sql new file mode 100644 index 000000000..c703a4e72 --- /dev/null +++ b/script/sql/update/update_5.3.1-5.3.2.sql @@ -0,0 +1,8 @@ +ALTER TABLE `flow_task` + ADD COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `node_type`; + +ALTER TABLE `flow_instance` + MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `variable`; + +ALTER TABLE `flow_his_task` + MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `skip_type` From ea74803ccc877e85b70178bf942d6405cb065a79 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Sun, 11 May 2025 17:57:42 +0800 Subject: [PATCH 061/121] =?UTF-8?q?add=20=E6=96=B0=E5=A2=9E=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E4=BD=93=E8=AF=BB=E5=8F=96=E5=BC=82=E5=B8=B8=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/handler/GlobalExceptionHandler.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java index 0a60fbcc7..28aacbcfb 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java @@ -2,6 +2,7 @@ package org.dromara.common.web.handler; import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.HttpStatus; +import com.fasterxml.jackson.core.JsonParseException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.ConstraintViolation; @@ -14,6 +15,7 @@ import org.dromara.common.core.exception.base.BaseException; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.json.utils.JsonUtils; import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.BindException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -180,4 +182,24 @@ public class GlobalExceptionHandler { return R.fail(message); } + /** + * JSON 解析异常(Jackson 在处理 JSON 格式出错时抛出) + * 可能是请求体格式非法,也可能是服务端反序列化失败 + */ + @ExceptionHandler(JsonParseException.class) + public R handleJsonParseException(JsonParseException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}' 发生 JSON 解析异常: {}", requestURI, e.getMessage()); + return R.fail(HttpStatus.HTTP_BAD_REQUEST, "请求数据格式错误(JSON 解析失败):" + e.getMessage()); + } + + /** + * 请求体读取异常(通常是请求参数格式非法、字段类型不匹配等) + */ + @ExceptionHandler(HttpMessageNotReadableException.class) + public R handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request) { + log.error("请求地址'{}', 参数解析失败: {}", request.getRequestURI(), e.getMessage()); + return R.fail(HttpStatus.HTTP_BAD_REQUEST, "请求参数格式错误:" + e.getMostSpecificCause().getMessage()); + } + } From a5c2093c76bbf505a3cd0a9eaf440fd912cf72a8 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Sun, 11 May 2025 18:12:49 +0800 Subject: [PATCH 062/121] =?UTF-8?q?docs=20=E4=BF=AE=E6=AD=A3=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-admin/src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index ee059dc3c..8e3f55acb 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -21,8 +21,8 @@ server: worker: 256 captcha: + # 是否启用验证码校验 enable: true - # 页面 <参数设置> 可开启关闭 验证码校验 # 验证码类型 math 数组计算 char 字符验证 type: MATH # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 From 2dde42168f5fa04af3ae224676b6f04c492510a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 12 May 2025 09:33:23 +0800 Subject: [PATCH 063/121] =?UTF-8?q?update=20=E6=9B=B4=E6=96=B0=20readme=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=B0=E6=88=90=E5=91=98=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5b132c662..f4d4a2873 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ > 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)
> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5) +> 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean) > 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网) From 0c2fe34d929471d37254633797671e9177173421 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Mon, 12 May 2025 02:04:43 +0000 Subject: [PATCH 064/121] =?UTF-8?q?!678=20add=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=20Date=20=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=8F=8D=E5=BA=8F=E5=88=97=E5=8C=96=E5=A4=84=E7=90=86=E5=99=A8?= =?UTF-8?q?=EF=BC=88=E6=94=AF=E6=8C=81=E5=A4=9A=E7=A7=8D=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=EF=BC=89=20*=20add=20=E6=96=B0=E5=A2=9E=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=20Date=20=E7=B1=BB=E5=9E=8B=E5=8F=8D=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E5=8C=96=E5=A4=84=E7=90=86=E5=99=A8=EF=BC=88=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=A4=9A=E7=A7=8D=E6=A0=BC=E5=BC=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/json/config/JacksonConfig.java | 10 ++++-- .../json/handler/CustomDateDeserializer.java | 31 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java index 8f5a45d11..f800463ba 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java @@ -1,11 +1,13 @@ package org.dromara.common.json.config; +import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; -import org.dromara.common.json.handler.BigNumberSerializer; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.json.handler.BigNumberSerializer; +import org.dromara.common.json.handler.CustomDateDeserializer; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; @@ -15,6 +17,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Date; import java.util.TimeZone; /** @@ -38,7 +41,10 @@ public class JacksonConfig { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter)); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); - builder.modules(javaTimeModule); + // 自定义 java.util.Date 的反序列化(支持多格式字符串解析) + SimpleModule dateModule = new SimpleModule(); + dateModule.addDeserializer(Date.class, new CustomDateDeserializer()); + builder.modules(javaTimeModule, dateModule); builder.timeZone(TimeZone.getDefault()); log.info("初始化 jackson 配置"); }; diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java new file mode 100644 index 000000000..069b924f0 --- /dev/null +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java @@ -0,0 +1,31 @@ +package org.dromara.common.json.handler; + +import cn.hutool.core.date.DateUtil; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.util.Date; + +/** + * 自定义 Date 类型反序列化处理器(支持多种格式) + * + * @author AprilWind + */ +public class CustomDateDeserializer extends JsonDeserializer { + + /** + * 反序列化逻辑:将字符串转换为 Date 对象 + * + * @param p JSON 解析器,用于获取字符串值 + * @param ctxt 上下文环境(可用于获取更多配置) + * @return 转换后的 Date 对象,若为空字符串返回 null + * @throws IOException 当字符串格式非法或转换失败时抛出 + */ + @Override + public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return DateUtil.parse(p.getText()); + } + +} From 5a1523564b1db3204490273dfbb8aa0052b45ede Mon Sep 17 00:00:00 2001 From: lcry <1318070+lcry@user.noreply.gitee.com> Date: Mon, 12 May 2025 02:07:46 +0000 Subject: [PATCH 065/121] =?UTF-8?q?!677=20add=20=E6=96=B0=E5=A2=9E=20?= =?UTF-8?q?=E5=AF=B9=E6=8E=A5=20gitea=20=E4=B8=89=E6=96=B9=E5=8D=95?= =?UTF-8?q?=E7=82=B9=E7=99=BB=E5=BD=95=20*=20add=20=E6=96=B0=E5=A2=9E=20?= =?UTF-8?q?=E5=AF=B9=E6=8E=A5=20gitea=20=E4=B8=89=E6=96=B9=E5=8D=95?= =?UTF-8?q?=E7=82=B9=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application-dev.yml | 6 ++ .../src/main/resources/application-prod.yml | 6 ++ .../common/social/gitea/AuthGiteaRequest.java | 92 +++++++++++++++++++ .../common/social/gitea/AuthGiteaSource.java | 50 ++++++++++ .../common/social/utils/SocialUtils.java | 2 + script/sql/oracle/oracle_ry_vue_5.X.sql | 4 +- script/sql/postgres/postgres_ry_vue_5.X.sql | 4 +- script/sql/ry_vue_5.X.sql | 4 +- script/sql/sqlserver/sqlserver_ry_vue_5.X.sql | 4 +- script/sql/update/update_5.3.1-5.3.2.sql | 5 + 10 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaRequest.java create mode 100644 ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaSource.java diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index d2c45eae1..34a62d39b 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -263,3 +263,9 @@ justauth: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: ${justauth.address}/social-callback?source=gitlab + gitea: + # gitea 服务器地址 + server-url: https://demo.gitea.com + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=gitea diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 1c7eae428..8440deece 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -265,3 +265,9 @@ justauth: client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e redirect-uri: ${justauth.address}/social-callback?source=gitlab + gitea: + # gitea 服务器地址 + server-url: https://demo.gitea.com + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=gitea diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaRequest.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaRequest.java new file mode 100644 index 000000000..d3fc7516f --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaRequest.java @@ -0,0 +1,92 @@ +package org.dromara.common.social.gitea; + +import cn.hutool.core.lang.Dict; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import lombok.extern.slf4j.Slf4j; +import me.zhyd.oauth.cache.AuthStateCache; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.exception.AuthException; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthDefaultRequest; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.json.utils.JsonUtils; + +/** + * @author lcry + */ +@Slf4j +public class AuthGiteaRequest extends AuthDefaultRequest { + + public static final String SERVER_URL = SpringUtils.getProperty("justauth.type.gitea.server-url"); + + /** + * 设定归属域 + */ + public AuthGiteaRequest(AuthConfig config) { + super(config, AuthGiteaSource.GITEA); + } + + public AuthGiteaRequest(AuthConfig config, AuthStateCache authStateCache) { + super(config, AuthGiteaSource.GITEA, authStateCache); + } + + @Override + public AuthToken getAccessToken(AuthCallback authCallback) { + String body = doPostAuthorizationCode(authCallback.getCode()); + Dict object = JsonUtils.parseMap(body); + // oauth/token 验证异常 + if (object.containsKey("error")) { + throw new AuthException(object.getStr("error_description")); + } + // user 验证异常 + if (object.containsKey("message")) { + throw new AuthException(object.getStr("message")); + } + return AuthToken.builder() + .accessToken(object.getStr("access_token")) + .refreshToken(object.getStr("refresh_token")) + .idToken(object.getStr("id_token")) + .tokenType(object.getStr("token_type")) + .scope(object.getStr("scope")) + .build(); + } + + @Override + protected String doPostAuthorizationCode(String code) { + HttpRequest request = HttpRequest.post(source.accessToken()) + .form("client_id", config.getClientId()) + .form("client_secret", config.getClientSecret()) + .form("grant_type", "authorization_code") + .form("code", code) + .form("redirect_uri", config.getRedirectUri()); + HttpResponse response = request.execute(); + return response.body(); + } + + @Override + public AuthUser getUserInfo(AuthToken authToken) { + String body = doGetUserInfo(authToken); + Dict object = JsonUtils.parseMap(body); + // oauth/token 验证异常 + if (object.containsKey("error")) { + throw new AuthException(object.getStr("error_description")); + } + // user 验证异常 + if (object.containsKey("message")) { + throw new AuthException(object.getStr("message")); + } + return AuthUser.builder() + .uuid(object.getStr("sub")) + .username(object.getStr("name")) + .nickname(object.getStr("preferred_username")) + .avatar(object.getStr("picture")) + .email(object.getStr("email")) + .token(authToken) + .source(source.toString()) + .build(); + } + +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaSource.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaSource.java new file mode 100644 index 000000000..201b22308 --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaSource.java @@ -0,0 +1,50 @@ +package org.dromara.common.social.gitea; + +import me.zhyd.oauth.config.AuthSource; +import me.zhyd.oauth.request.AuthDefaultRequest; + +/** + * gitea Oauth2 默认接口说明 + * + * @author lcry + */ +public enum AuthGiteaSource implements AuthSource { + + /** + * 自己搭建的 gitea 私服 + */ + GITEA { + /** + * 授权的api + */ + @Override + public String authorize() { + return AuthGiteaRequest.SERVER_URL + "/login/oauth/authorize"; + } + + /** + * 获取accessToken的api + */ + @Override + public String accessToken() { + return AuthGiteaRequest.SERVER_URL + "/login/oauth/access_token"; + } + + /** + * 获取用户信息的api + */ + @Override + public String userInfo() { + return AuthGiteaRequest.SERVER_URL + "/login/oauth/userinfo"; + } + + /** + * 平台对应的 AuthRequest 实现类,必须继承自 {@link AuthDefaultRequest} + */ + @Override + public Class getTargetClass() { + return AuthGiteaRequest.class; + } + + } +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java index db696e515..a73b229f8 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java @@ -10,6 +10,7 @@ import me.zhyd.oauth.request.*; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.social.config.properties.SocialLoginConfigProperties; import org.dromara.common.social.config.properties.SocialProperties; +import org.dromara.common.social.gitea.AuthGiteaRequest; import org.dromara.common.social.maxkey.AuthMaxKeyRequest; import org.dromara.common.social.topiam.AuthTopIamRequest; @@ -66,6 +67,7 @@ public class SocialUtils { case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE); case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE); case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE); + case "gitea" -> new AuthGiteaRequest(builder.build(), STATE_CACHE); default -> throw new AuthException("未获取到有效的Auth配置"); }; } diff --git a/script/sql/oracle/oracle_ry_vue_5.X.sql b/script/sql/oracle/oracle_ry_vue_5.X.sql index 7fff0b443..ce38b069d 100644 --- a/script/sql/oracle/oracle_ry_vue_5.X.sql +++ b/script/sql/oracle/oracle_ry_vue_5.X.sql @@ -13,9 +13,9 @@ create table sys_social nick_name varchar2(30) default '', email varchar2(255) default '', avatar varchar2(500) default '', - access_token varchar2(255) not null, + access_token varchar2(2000) not null, expire_in number(20) default null, - refresh_token varchar2(255) default null, + refresh_token varchar2(2000) default null, access_code varchar2(255) default null, union_id varchar2(255) default null, scope varchar2(255) default null, diff --git a/script/sql/postgres/postgres_ry_vue_5.X.sql b/script/sql/postgres/postgres_ry_vue_5.X.sql index 523cbe266..0f92c839e 100644 --- a/script/sql/postgres/postgres_ry_vue_5.X.sql +++ b/script/sql/postgres/postgres_ry_vue_5.X.sql @@ -13,9 +13,9 @@ create table sys_social nick_name varchar(30) default ''::varchar, email varchar(255) default ''::varchar, avatar varchar(500) default ''::varchar, - access_token varchar(255) not null, + access_token varchar(2000) not null, expire_in int8 default null, - refresh_token varchar(255) default null::varchar, + refresh_token varchar(2000) default null::varchar, access_code varchar(255) default null::varchar, union_id varchar(255) default null::varchar, scope varchar(255) default null::varchar, diff --git a/script/sql/ry_vue_5.X.sql b/script/sql/ry_vue_5.X.sql index c17e4a01a..ffc469e29 100644 --- a/script/sql/ry_vue_5.X.sql +++ b/script/sql/ry_vue_5.X.sql @@ -13,10 +13,10 @@ create table sys_social nick_name varchar(30) default '' comment '用户昵称', email varchar(255) default '' comment '用户邮箱', avatar varchar(500) default '' comment '头像地址', - access_token varchar(255) not null comment '用户的授权令牌', + access_token varchar(2000) not null comment '用户的授权令牌', expire_in int default null comment '用户的授权令牌的有效期,部分平台可能没有', refresh_token varchar(255) default null comment '刷新令牌,部分平台可能没有', - access_code varchar(255) default null comment '平台的授权信息,部分平台可能没有', + access_code varchar(2000) default null comment '平台的授权信息,部分平台可能没有', union_id varchar(255) default null comment '用户的 unionid', scope varchar(255) default null comment '授予的权限,部分平台可能没有', token_type varchar(255) default null comment '个别平台的授权信息,部分平台可能没有', diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql index f64d8f804..c7fed712a 100644 --- a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql +++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql @@ -10,9 +10,9 @@ create table sys_social nick_name nvarchar(30) DEFAULT ('') NULL, email nvarchar(255) DEFAULT ('') NULL, avatar nvarchar(500) DEFAULT ('') NULL, - access_token nvarchar(255) NOT NULL, + access_token nvarchar(2000) NOT NULL, expire_in bigint NULL, - refresh_token nvarchar(255) NULL, + refresh_token nvarchar(2000) NULL, access_code nvarchar(255) NULL, union_id nvarchar(255) NULL, scope nvarchar(255) NULL, diff --git a/script/sql/update/update_5.3.1-5.3.2.sql b/script/sql/update/update_5.3.1-5.3.2.sql index c703a4e72..6feb5c9f7 100644 --- a/script/sql/update/update_5.3.1-5.3.2.sql +++ b/script/sql/update/update_5.3.1-5.3.2.sql @@ -6,3 +6,8 @@ ALTER TABLE `flow_instance` ALTER TABLE `flow_his_task` MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `skip_type` + +ALTER TABLE `sys_social` + MODIFY COLUMN `access_token` varchar(2000) NOT NULL COMMENT '用户的授权令牌' AFTER `avatar`; +ALTER TABLE `sys_social` + MODIFY COLUMN `refresh_token` varchar(2000) NOT NULL COMMENT '刷新令牌,部分平台可能没有' AFTER `expire_in`; From e0672fc75319c0ccfdbfb0c8054225cbbeebd303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 12 May 2025 10:12:20 +0800 Subject: [PATCH 066/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=AF=B9=E6=8E=A5gitea=E5=AF=B9=E6=8E=A5=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E7=9A=84pr=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-admin/src/main/resources/application-dev.yml | 1 + ruoyi-admin/src/main/resources/application-prod.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 34a62d39b..5dd2e00fe 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -264,6 +264,7 @@ justauth: client-secret: 1f7d08**********5b7**********29e redirect-uri: ${justauth.address}/social-callback?source=gitlab gitea: + # 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204 # gitea 服务器地址 server-url: https://demo.gitea.com client-id: 10**********6 diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 8440deece..435a9e856 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -266,6 +266,7 @@ justauth: client-secret: 1f7d08**********5b7**********29e redirect-uri: ${justauth.address}/social-callback?source=gitlab gitea: + # 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204 # gitea 服务器地址 server-url: https://demo.gitea.com client-id: 10**********6 From 748c95b30f0ea78ba58d4fa0ab1a62f536a9b761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 12 May 2025 10:59:12 +0800 Subject: [PATCH 067/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20=E5=85=B6=E4=BB=96=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E5=8D=87=E7=BA=A7sql?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sql/update/oracle/update_5.3.1-5.4.0.sql | 9 +++ .../update/postgres/update_5.3.1-5.4.0.sql | 9 +++ .../update/sqlserver/update_5.3.1-5.4.0.sql | 56 +++++++++++++++++++ ...5.3.1-5.3.2.sql => update_5.3.1-5.4.0.sql} | 2 +- 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 script/sql/update/oracle/update_5.3.1-5.4.0.sql create mode 100644 script/sql/update/postgres/update_5.3.1-5.4.0.sql create mode 100644 script/sql/update/sqlserver/update_5.3.1-5.4.0.sql rename script/sql/update/{update_5.3.1-5.3.2.sql => update_5.3.1-5.4.0.sql} (86%) diff --git a/script/sql/update/oracle/update_5.3.1-5.4.0.sql b/script/sql/update/oracle/update_5.3.1-5.4.0.sql new file mode 100644 index 000000000..ae53fc078 --- /dev/null +++ b/script/sql/update/oracle/update_5.3.1-5.4.0.sql @@ -0,0 +1,9 @@ +COMMENT ON COLUMN flow_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +ALTER TABLE sys_social + MODIFY (access_token VARCHAR2(2000 BYTE)) + MODIFY (refresh_token VARCHAR2(2000 BYTE)); diff --git a/script/sql/update/postgres/update_5.3.1-5.4.0.sql b/script/sql/update/postgres/update_5.3.1-5.4.0.sql new file mode 100644 index 000000000..5ba8302fc --- /dev/null +++ b/script/sql/update/postgres/update_5.3.1-5.4.0.sql @@ -0,0 +1,9 @@ +COMMENT ON COLUMN flow_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +ALTER TABLE sys_social +ALTER COLUMN access_token TYPE varchar(2000), +ALTER COLUMN refresh_token TYPE varchar(2000); diff --git a/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql new file mode 100644 index 000000000..7fedcbd6f --- /dev/null +++ b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql @@ -0,0 +1,56 @@ +IF ((SELECT COUNT(*) FROM ::fn_listextendedproperty('MS_Description', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'flow_status')) > 0) + EXEC sp_updateextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'flow_status' +ELSE + EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'flow_status' +GO + +IF ((SELECT COUNT(*) FROM ::fn_listextendedproperty('MS_Description', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'flow_status')) > 0) + EXEC sp_updateextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'flow_status' +ELSE + EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'flow_status' +GO + +IF ((SELECT COUNT(*) FROM ::fn_listextendedproperty('MS_Description', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'flow_status')) > 0) + EXEC sp_updateextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'flow_status' +ELSE + EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'flow_status' +GO + +-- sys_social 表修改列 +ALTER TABLE sys_social ALTER COLUMN access_token VARCHAR(2000) NOT NULL +GO +ALTER TABLE sys_social ALTER COLUMN refresh_token VARCHAR(2000) NULL +GO diff --git a/script/sql/update/update_5.3.1-5.3.2.sql b/script/sql/update/update_5.3.1-5.4.0.sql similarity index 86% rename from script/sql/update/update_5.3.1-5.3.2.sql rename to script/sql/update/update_5.3.1-5.4.0.sql index 6feb5c9f7..68d6bc7f1 100644 --- a/script/sql/update/update_5.3.1-5.3.2.sql +++ b/script/sql/update/update_5.3.1-5.4.0.sql @@ -10,4 +10,4 @@ ALTER TABLE `flow_his_task` ALTER TABLE `sys_social` MODIFY COLUMN `access_token` varchar(2000) NOT NULL COMMENT '用户的授权令牌' AFTER `avatar`; ALTER TABLE `sys_social` - MODIFY COLUMN `refresh_token` varchar(2000) NOT NULL COMMENT '刷新令牌,部分平台可能没有' AFTER `expire_in`; + MODIFY COLUMN `refresh_token` varchar(2000) DEFAULT NULL COMMENT '刷新令牌,部分平台可能没有' AFTER `expire_in`; From 122f2770b21f928cbfb4f027b8aa7f7e9eff3c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 12 May 2025 12:56:24 +0800 Subject: [PATCH 068/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20!pr678=20?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/dromara/common/json/config/JacksonConfig.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java index f800463ba..77cf83381 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java @@ -1,6 +1,5 @@ package org.dromara.common.json.config; -import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; @@ -41,10 +40,8 @@ public class JacksonConfig { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter)); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); - // 自定义 java.util.Date 的反序列化(支持多格式字符串解析) - SimpleModule dateModule = new SimpleModule(); - dateModule.addDeserializer(Date.class, new CustomDateDeserializer()); - builder.modules(javaTimeModule, dateModule); + javaTimeModule.addDeserializer(Date.class, new CustomDateDeserializer()); + builder.modules(javaTimeModule); builder.timeZone(TimeZone.getDefault()); log.info("初始化 jackson 配置"); }; From 97caabe0a2c8755fc381ff1bd96e03926bd86d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 12 May 2025 13:01:55 +0800 Subject: [PATCH 069/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20update=20?= =?UTF-8?q?sql=20=E4=B9=A6=E5=86=99=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/sql/update/oracle/update_5.3.1-5.4.0.sql | 1 + script/sql/update/postgres/update_5.3.1-5.4.0.sql | 1 + script/sql/update/sqlserver/update_5.3.1-5.4.0.sql | 14 +++----------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/script/sql/update/oracle/update_5.3.1-5.4.0.sql b/script/sql/update/oracle/update_5.3.1-5.4.0.sql index ae53fc078..d4de6ad2c 100644 --- a/script/sql/update/oracle/update_5.3.1-5.4.0.sql +++ b/script/sql/update/oracle/update_5.3.1-5.4.0.sql @@ -1,3 +1,4 @@ +ALTER TABLE flow_task ADD (flow_status VARCHAR2(20)); COMMENT ON COLUMN flow_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; diff --git a/script/sql/update/postgres/update_5.3.1-5.4.0.sql b/script/sql/update/postgres/update_5.3.1-5.4.0.sql index 5ba8302fc..8f5c9d4e0 100644 --- a/script/sql/update/postgres/update_5.3.1-5.4.0.sql +++ b/script/sql/update/postgres/update_5.3.1-5.4.0.sql @@ -1,3 +1,4 @@ +ALTER TABLE flow_task ADD COLUMN flow_status varchar(20); COMMENT ON COLUMN flow_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; diff --git a/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql index 7fedcbd6f..2cbf99e01 100644 --- a/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql +++ b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql @@ -1,14 +1,6 @@ -IF ((SELECT COUNT(*) FROM ::fn_listextendedproperty('MS_Description', -'SCHEMA', N'dbo', -'TABLE', N'flow_task', -'COLUMN', N'flow_status')) > 0) - EXEC sp_updateextendedproperty -'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', -'SCHEMA', N'dbo', -'TABLE', N'flow_task', -'COLUMN', N'flow_status' -ELSE - EXEC sp_addextendedproperty +ALTER TABLE flow_task ADD flow_status nvarchar(20) NULL; + +EXEC sp_addextendedproperty 'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', 'SCHEMA', N'dbo', 'TABLE', N'flow_task', From 553fca28a27b16963ada982505e131bbc2364214 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Mon, 12 May 2025 16:51:58 +0800 Subject: [PATCH 070/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E5=81=87?= =?UTF-8?q?=E5=88=86=E9=A1=B5=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/mybatis/core/page/TableDataInfo.java | 16 ++++++++++++++++ .../generator/service/GenTableServiceImpl.java | 8 +------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java index 370f47995..a1d499205 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java @@ -1,5 +1,6 @@ package org.dromara.common.mybatis.core.page; +import cn.hutool.core.collection.CollUtil; import cn.hutool.http.HttpStatus; import com.baomidou.mybatisplus.core.metadata.IPage; import lombok.Data; @@ -88,4 +89,19 @@ public class TableDataInfo implements Serializable { return rspData; } + /** + * 根据原始数据列表和分页参数,构建表格分页数据对象(用于假分页) + * + * @param list 原始数据列表(全部数据) + * @param page 分页参数对象(包含当前页码、每页大小等) + * @return 构造好的分页结果 TableDataInfo + */ + public static TableDataInfo build(List list, IPage page) { + if (CollUtil.isEmpty(list)) { + return TableDataInfo.build(); + } + List pageList = CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), list); + return new TableDataInfo(pageList, list.size()); + } + } diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java index 6b9e60617..b397f37f4 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java @@ -9,7 +9,6 @@ import com.baomidou.dynamic.datasource.annotation.DSTransactional; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; -import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; @@ -168,12 +167,7 @@ public class GenTableServiceImpl implements IGenTableService { return gen; }).sorted(Comparator.comparing(GenTable::getCreateTime).reversed()) .toList(); - - IPage page = pageQuery.build(); - page.setTotal(tables.size()); - // 手动分页 set数据 - page.setRecords(CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), tables)); - return TableDataInfo.build(page); + return TableDataInfo.build(tables, pageQuery.build()); } /** From b9e5914babcbf5436965a365b50b5391da4f046c Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Mon, 12 May 2025 18:11:32 +0800 Subject: [PATCH 071/121] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=8A=9E=E7=90=86=E4=BA=BA=E9=94=99=E8=AF=AF=E4=BD=BF?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflow/service/IFlwTaskAssigneeService.java | 2 +- .../workflow/service/impl/FlwCommonServiceImpl.java | 9 ++++++--- .../service/impl/FlwTaskAssigneeServiceImpl.java | 9 ++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java index 90d87e37f..7264ee079 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java @@ -28,6 +28,6 @@ public interface IFlwTaskAssigneeService { * @param storageIds 多个存储标识符字符串(逗号分隔) * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 */ - List fetchUsersByStorageIds(String storageIds); + List fetchUsersByStorageIds(List storageIds); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index 086ae6870..35434d389 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -72,10 +72,13 @@ public class FlwCommonServiceImpl implements IFlwCommonService { IFlwTaskAssigneeService taskAssigneeService = SpringUtils.getBean(IFlwTaskAssigneeService.class); Map> userListMap = StreamUtils.groupByKey(userList, User::getType); for (Map.Entry> entry : userListMap.entrySet()) { - List entryValue = entry.getValue(); - String processedBys = StreamUtils.join(entryValue, User::getProcessedBy); + List processedBys = entry.getValue().stream() + .map(User::getProcessedBy) + .filter(StringUtils::isNotBlank) + .distinct() + .collect(Collectors.toList()); // 根据 processedBy 前缀判断处理人类型,分别获取用户列表 - List users = taskAssigneeService.fetchUsersByStorageId(processedBys); + List users = taskAssigneeService.fetchUsersByStorageIds(processedBys); // 转换为 FlowUser 并添加到结果集合 if (CollUtil.isNotEmpty(users)) { users.forEach(dto -> { diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java index 67c58b001..9f847e184 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java @@ -176,7 +176,7 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand public List fetchUsersByStorageId(String storageId) { Pair parsed = this.parseStorageId(storageId); if (parsed == null) { - return Collections.emptyList(); + return List.of(); } return this.getUsersByType(parsed.getKey(), Collections.singletonList(parsed.getValue())); } @@ -190,9 +190,12 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 */ @Override - public List fetchUsersByStorageIds(String storageIds) { + public List fetchUsersByStorageIds(List storageIds) { + if (CollUtil.isEmpty(storageIds)) { + return List.of(); + } Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); - for (String storageId : storageIds.split(StrUtil.COMMA)) { + for (String storageId : storageIds) { Pair parsed = this.parseStorageId(storageId); if (parsed != null) { typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); From a4ad56f0ebd57e88455611d7dcb3408549ad7fc4 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Mon, 12 May 2025 18:18:10 +0800 Subject: [PATCH 072/121] =?UTF-8?q?fix=20=E7=BB=A7=E7=BB=AD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=9F=A5=E8=AF=A2=E5=8A=9E=E7=90=86=E4=BA=BA=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/workflow/service/impl/FlwTaskServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 77c7fcecf..4bc0b0878 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -600,7 +600,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { for (FlowNode flowNode : nextFlowNodes) { buildNextTaskList.stream().filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())).findFirst().ifPresent(t -> { if (CollUtil.isNotEmpty(t.getPermissionList())) { - List users = flwTaskAssigneeService.fetchUsersByStorageId(String.join(StringUtils.SEPARATOR, t.getPermissionList())); + List users = flwTaskAssigneeService.fetchUsersByStorageIds(t.getPermissionList()); if (CollUtil.isNotEmpty(users)) { flowNode.setPermissionFlag(StreamUtils.join(users, e -> String.valueOf(e.getUserId()))); } From facd3e351f0c94269a438223eb8dce56443a2940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 12 May 2025 18:21:21 +0800 Subject: [PATCH 073/121] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E5=AF=BC=E8=87=B4=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/IFlwTaskAssigneeService.java | 14 ++--------- .../service/impl/FlwCommonServiceImpl.java | 7 ++---- .../impl/FlwTaskAssigneeServiceImpl.java | 25 +++---------------- .../service/impl/FlwTaskServiceImpl.java | 2 +- 4 files changed, 9 insertions(+), 39 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java index 7264ee079..dfeb4df57 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java @@ -15,19 +15,9 @@ public interface IFlwTaskAssigneeService { * 根据存储标识符(storageId)解析分配类型和ID,并获取对应的用户列表 * 支持单个标识(例如 "user:123" 或 "456"),格式非法将返回空列表 * - * @param storageId 包含分配类型和ID的字符串 + * @param storageIds 包含分配类型和ID的字符串 * @return 匹配的用户列表,格式非法返回空列表 */ - List fetchUsersByStorageId(String storageId); - - /** - * 批量解析多个存储标识符(storageIds),按类型分类并合并查询用户列表 - * 输入格式支持多个以逗号分隔的标识(如 "user:123,role:456,789") - * 会自动去重返回结果,非法格式的标识将被忽略 - * - * @param storageIds 多个存储标识符字符串(逗号分隔) - * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 - */ - List fetchUsersByStorageIds(List storageIds); + List fetchUsersByStorageIds(String storageIds); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index 35434d389..8e9c5096c 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -72,11 +72,8 @@ public class FlwCommonServiceImpl implements IFlwCommonService { IFlwTaskAssigneeService taskAssigneeService = SpringUtils.getBean(IFlwTaskAssigneeService.class); Map> userListMap = StreamUtils.groupByKey(userList, User::getType); for (Map.Entry> entry : userListMap.entrySet()) { - List processedBys = entry.getValue().stream() - .map(User::getProcessedBy) - .filter(StringUtils::isNotBlank) - .distinct() - .collect(Collectors.toList()); + List entryValue = entry.getValue(); + String processedBys = StreamUtils.join(entryValue, User::getProcessedBy); // 根据 processedBy 前缀判断处理人类型,分别获取用户列表 List users = taskAssigneeService.fetchUsersByStorageIds(processedBys); // 转换为 FlowUser 并添加到结果集合 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java index 9f847e184..5ebf2bbeb 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java @@ -169,33 +169,16 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand * 根据存储标识符(storageId)解析分配类型和ID,并获取对应的用户列表 * 支持单个标识(例如 "user:123" 或 "456"),格式非法将返回空列表 * - * @param storageId 包含分配类型和ID的字符串 + * @param storageIds 包含分配类型和ID的字符串 * @return 匹配的用户列表,格式非法返回空列表 */ @Override - public List fetchUsersByStorageId(String storageId) { - Pair parsed = this.parseStorageId(storageId); - if (parsed == null) { - return List.of(); - } - return this.getUsersByType(parsed.getKey(), Collections.singletonList(parsed.getValue())); - } - - /** - * 批量解析多个存储标识符(storageIds),按类型分类并合并查询用户列表 - * 输入格式支持多个以逗号分隔的标识(如 "user:123,role:456,789") - * 会自动去重返回结果,非法格式的标识将被忽略 - * - * @param storageIds 多个存储标识符字符串(逗号分隔) - * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 - */ - @Override - public List fetchUsersByStorageIds(List storageIds) { - if (CollUtil.isEmpty(storageIds)) { + public List fetchUsersByStorageIds(String storageIds) { + if (StringUtils.isEmpty(storageIds)) { return List.of(); } Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); - for (String storageId : storageIds) { + for (String storageId : storageIds.split(StringUtils.SEPARATOR)) { Pair parsed = this.parseStorageId(storageId); if (parsed != null) { typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 4bc0b0878..0cdabbf4a 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -600,7 +600,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { for (FlowNode flowNode : nextFlowNodes) { buildNextTaskList.stream().filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())).findFirst().ifPresent(t -> { if (CollUtil.isNotEmpty(t.getPermissionList())) { - List users = flwTaskAssigneeService.fetchUsersByStorageIds(t.getPermissionList()); + List users = flwTaskAssigneeService.fetchUsersByStorageIds(String.join(StringUtils.SEPARATOR, t.getPermissionList())); if (CollUtil.isNotEmpty(users)) { flowNode.setPermissionFlag(StreamUtils.join(users, e -> String.valueOf(e.getUserId()))); } From fdfca0b33afa7b8dc3fc2ad678b2b4a5be233ebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 12 May 2025 18:28:24 +0800 Subject: [PATCH 074/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/common/mybatis/core/page/TableDataInfo.java | 2 +- .../workflow/service/IFlwTaskAssigneeService.java | 9 +++++---- .../service/impl/FlwTaskAssigneeServiceImpl.java | 9 +++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java index a1d499205..1fe2b3ef1 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java @@ -101,7 +101,7 @@ public class TableDataInfo implements Serializable { return TableDataInfo.build(); } List pageList = CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), list); - return new TableDataInfo(pageList, list.size()); + return new TableDataInfo<>(pageList, list.size()); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java index dfeb4df57..830abaf52 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java @@ -12,11 +12,12 @@ import java.util.List; public interface IFlwTaskAssigneeService { /** - * 根据存储标识符(storageId)解析分配类型和ID,并获取对应的用户列表 - * 支持单个标识(例如 "user:123" 或 "456"),格式非法将返回空列表 + * 批量解析多个存储标识符(storageIds),按类型分类并合并查询用户列表 + * 输入格式支持多个以逗号分隔的标识(如 "user:123,role:456,789") + * 会自动去重返回结果,非法格式的标识将被忽略 * - * @param storageIds 包含分配类型和ID的字符串 - * @return 匹配的用户列表,格式非法返回空列表 + * @param storageIds 多个存储标识符字符串(逗号分隔) + * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 */ List fetchUsersByStorageIds(String storageIds); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java index 5ebf2bbeb..3009e0a1d 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java @@ -166,11 +166,12 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand } /** - * 根据存储标识符(storageId)解析分配类型和ID,并获取对应的用户列表 - * 支持单个标识(例如 "user:123" 或 "456"),格式非法将返回空列表 + * 批量解析多个存储标识符(storageIds),按类型分类并合并查询用户列表 + * 输入格式支持多个以逗号分隔的标识(如 "user:123,role:456,789") + * 会自动去重返回结果,非法格式的标识将被忽略 * - * @param storageIds 包含分配类型和ID的字符串 - * @return 匹配的用户列表,格式非法返回空列表 + * @param storageIds 多个存储标识符字符串(逗号分隔) + * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 */ @Override public List fetchUsersByStorageIds(String storageIds) { From 90fb26fbf163724956995c1230228212629b605c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Wed, 14 May 2025 18:01:46 +0800 Subject: [PATCH 075/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20redis?= =?UTF-8?q?=E5=BB=BA=E8=AE=AE=E7=89=88=E6=9C=AC=E5=8D=87=E7=BA=A7=E5=88=B0?= =?UTF-8?q?7.2.8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml index 3814a2d89..3767194d2 100644 --- a/script/docker/docker-compose.yml +++ b/script/docker/docker-compose.yml @@ -48,7 +48,7 @@ services: network_mode: "host" redis: - image: redis:6.2.12 + image: redis:7.2.8 container_name: redis ports: - "6379:6379" From cf871d9387ef540b443b2dab5ff10d667fc46826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Wed, 14 May 2025 18:08:58 +0800 Subject: [PATCH 076/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20mysql?= =?UTF-8?q?=E5=BB=BA=E8=AE=AE=E7=89=88=E6=9C=AC=E5=8D=87=E7=BA=A7=E5=88=B0?= =?UTF-8?q?8.0.42?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml index 3767194d2..82548e4db 100644 --- a/script/docker/docker-compose.yml +++ b/script/docker/docker-compose.yml @@ -1,6 +1,6 @@ services: mysql: - image: mysql:8.0.33 + image: mysql:8.0.42 container_name: mysql environment: # 时区上海 From 0c8ac12e4dc4420e2322b00eb117feb6412e0402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Thu, 15 May 2025 17:48:33 +0800 Subject: [PATCH 077/121] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20=E7=B1=BB?= =?UTF-8?q?=E5=90=8D=E4=B9=A6=E5=86=99=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{WeSocketController.java => WebSocketController.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/{WeSocketController.java => WebSocketController.java} (96%) diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WeSocketController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java similarity index 96% rename from ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WeSocketController.java rename to ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java index 699a5e602..65bf68e8c 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WeSocketController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java @@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/demo/websocket") @Slf4j -public class WeSocketController { +public class WebSocketController { /** * 发布消息 From 21c87eee9a0349f76fd3ebf0d1714f3ec8fe6184 Mon Sep 17 00:00:00 2001 From: gssong <1742057357@qq.com> Date: Fri, 16 May 2025 20:29:30 +0800 Subject: [PATCH 078/121] =?UTF-8?q?update=20=E5=8D=87=E7=BA=A7warm-flow1.7?= =?UTF-8?q?.0->1.7.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../dromara/workflow/service/IFlwCommonService.java | 10 ---------- .../workflow/service/impl/FlwCommonServiceImpl.java | 13 ------------- .../service/impl/FlwInstanceServiceImpl.java | 2 +- .../workflow/service/impl/FlwTaskServiceImpl.java | 4 ++-- 5 files changed, 4 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index 311e17011..39ca8809c 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ 8.7.2-20250101 - 1.7.0 + 1.7.2 3.2.2 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java index 4d3540b91..26997fa9b 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java @@ -1,11 +1,9 @@ package org.dromara.workflow.service; -import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.User; import org.dromara.warm.flow.core.service.UserService; import java.util.List; -import java.util.Map; import java.util.Set; /** @@ -57,12 +55,4 @@ public interface IFlwCommonService { * @return 申请人节点编码 */ String applyNodeCode(Long definitionId); - - /** - * 合并变量 - * - * @param instance 流程实例 - * @param variable 变量 - */ - void mergeVariable(Instance instance, Map variable); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index 8e9c5096c..878d4f4cc 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -11,15 +11,12 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mail.utils.MailUtils; import org.dromara.common.sse.dto.SseMessageDto; import org.dromara.common.sse.utils.SseMessageUtils; -import org.dromara.warm.flow.core.FlowEngine; -import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Node; import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.core.entity.User; import org.dromara.warm.flow.core.enums.SkipType; import org.dromara.warm.flow.core.service.NodeService; import org.dromara.warm.flow.core.service.UserService; -import org.dromara.warm.flow.core.utils.MapUtil; import org.dromara.warm.flow.orm.entity.FlowTask; import org.dromara.warm.flow.orm.entity.FlowUser; import org.dromara.workflow.common.ConditionalOnEnable; @@ -180,14 +177,4 @@ public class FlwCommonServiceImpl implements IFlwCommonService { Node nextNode = nodeService.getNextNode(definitionId, startNode.getNodeCode(), null, SkipType.PASS.getKey()); return nextNode.getNodeCode(); } - - @Override - public void mergeVariable(Instance instance, Map variable) { - if (MapUtil.isNotEmpty(variable)) { - String variableStr = instance.getVariable(); - Map deserialize = FlowEngine.jsonConvert.strToMap(variableStr); - deserialize.putAll(variable); - instance.setVariable(FlowEngine.jsonConvert.objToStr(deserialize)); - } - } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index e50744005..d302bc217 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -360,7 +360,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { public void setVariable(Long instanceId, Map variable) { Instance instance = insService.getById(instanceId); if (instance != null) { - flwCommonService.mergeVariable(instance, variable); + taskService.mergeVariable(instance, variable); insService.updateById(instance); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 0cdabbf4a..be64dcbbc 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -112,7 +112,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { if (ObjectUtil.isNotNull(flowInstance)) { BusinessStatusEnum.checkStartStatus(flowInstance.getFlowStatus()); List taskList = taskService.list(new FlowTask().setInstanceId(flowInstance.getId())); - flwCommonService.mergeVariable(flowInstance, variables); + taskService.mergeVariable(flowInstance, variables); insService.updateById(flowInstance); StartProcessReturnDTO dto = new StartProcessReturnDTO(); dto.setProcessInstanceId(taskList.get(0).getInstanceId()); @@ -217,7 +217,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { variableMap.remove(task.getNodeCode()); } } - flwCommonService.mergeVariable(inst, variableMap); + taskService.mergeVariable(inst, variableMap); } /** From 777ae645c5f3a0792c46b436db870ca1c1a86793 Mon Sep 17 00:00:00 2001 From: songgaoshuai <1742057357@qq.com> Date: Mon, 19 May 2025 11:31:01 +0800 Subject: [PATCH 079/121] =?UTF-8?q?update=20=E6=89=8B=E5=8A=A8=E5=90=88?= =?UTF-8?q?=E5=B9=B6=E5=86=B2=E7=AA=81=20=E4=BC=98=E5=8C=96=20=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E6=A8=A1=E5=9D=97=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E6=8C=87=E5=AE=9A=E5=8A=9E=E7=90=86=E4=BA=BA?= =?UTF-8?q?=E3=80=81=E8=A7=92=E8=89=B2=E5=92=8C=E9=83=A8=E9=97=A8=E8=BD=AC?= =?UTF-8?q?=E5=85=B7=E4=BD=93=E7=94=A8=E6=88=B7=E3=80=81=E6=8A=84=E9=80=81?= =?UTF-8?q?=E4=BA=BA=E5=92=8C=E6=B6=88=E6=81=AF=E6=8E=A8=E9=80=81=EF=BC=8C?= =?UTF-8?q?=E6=94=B9=E5=88=B0=E9=80=9A=E8=BF=87=E5=85=A8=E5=B1=80=E5=88=86?= =?UTF-8?q?=E6=B4=BE=E7=9B=91=E5=90=AC=E5=99=A8=E5=92=8C=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E7=9B=91=E5=90=AC=E5=99=A8=E5=A4=84=E7=90=86=20update=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=80=80=E5=9B=9E=E7=94=B3=E8=AF=B7=E4=BA=BA?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E5=8F=91=E9=80=81=E6=B6=88=E6=81=AF=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listener/WorkflowGlobalListener.java | 51 ++++++++ .../workflow/service/IFlwCommonService.java | 25 +--- .../workflow/service/IFlwTaskService.java | 9 ++ .../service/impl/FlwCommonServiceImpl.java | 80 ++---------- .../service/impl/FlwTaskServiceImpl.java | 117 +++++------------- 5 files changed, 102 insertions(+), 180 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index b6bdfa268..a46cbc5b3 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -14,7 +14,10 @@ import org.dromara.warm.flow.core.listener.GlobalListener; import org.dromara.warm.flow.core.listener.ListenerVariable; import org.dromara.warm.flow.orm.entity.FlowTask; import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.common.enums.TaskStatusEnum; +import org.dromara.workflow.domain.bo.FlowCopyBo; import org.dromara.workflow.handler.FlowProcessEventHandler; +import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwInstanceService; import org.dromara.workflow.service.IFlwTaskService; import org.springframework.stereotype.Component; @@ -37,6 +40,7 @@ public class WorkflowGlobalListener implements GlobalListener { private final IFlwTaskService taskService; private final IFlwInstanceService instanceService; private final FlowProcessEventHandler flowProcessEventHandler; + private final IFlwCommonService flwCommonService; /** * 创建监听器,任务创建时执行 @@ -70,6 +74,32 @@ public class WorkflowGlobalListener implements GlobalListener { */ @Override public void assignment(ListenerVariable listenerVariable) { + Map variable = listenerVariable.getVariable(); + List nextTasks = listenerVariable.getNextTasks(); + FlowParams flowParams = listenerVariable.getFlowParams(); + Definition definition = listenerVariable.getDefinition(); + Instance instance = listenerVariable.getInstance(); + String applyNodeCode = flwCommonService.applyNodeCode(definition.getId()); + for (Task flowTask : nextTasks) { + // 如果办理或者退回并行存在需要指定办理人,则直接覆盖办理人 + if (variable.containsKey(flowTask.getNodeCode()) && (TaskStatusEnum.PASS.getStatus().equals(flowParams.getHisStatus()) + || TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus()))) { + String userIds = variable.get(flowTask.getNodeCode()).toString(); + flowTask.setPermissionList(List.of(userIds.split(StringUtils.SEPARATOR))); + variable.remove(flowTask.getNodeCode()); + } else { + // 否则把所有的角色或者部门转成对应的用户 + List permissionList = flowTask.getPermissionList(); + if (CollUtil.isNotEmpty(permissionList)) { + List newUserList = flwCommonService.buildUser(permissionList); + flowTask.setPermissionList(newUserList); + } + } + // 如果是申请节点,则把启动人添加到办理人 + if (flowTask.getNodeCode().equals(applyNodeCode)) { + flowTask.setPermissionList(List.of(instance.getCreateBy())); + } + } } /** @@ -96,6 +126,27 @@ public class WorkflowGlobalListener implements GlobalListener { if (StringUtils.isNotBlank(status)) { flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false); } + + // 只有办理或者退回的时候才执行消息通知和抄送 + if (TaskStatusEnum.PASS.getStatus().equals(flowParams.getHisStatus()) + || TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus())) { + Task task = listenerVariable.getTask(); + Map variable = listenerVariable.getVariable(); + List flowCopyList = (List) variable.get("flowCopyList"); + List messageType = (List) variable.get("messageType"); + String notice = (String) variable.get("notice"); + + // 添加抄送人 + taskService.setCopy(task, flowCopyList); + variable.remove("flowCopyList"); + + // 消息通知 + if (CollUtil.isNotEmpty(messageType)) { + flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); + variable.remove("messageType"); + variable.remove("notice"); + } + } } /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java index 26997fa9b..662d599eb 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java @@ -1,10 +1,6 @@ package org.dromara.workflow.service; -import org.dromara.warm.flow.core.entity.User; -import org.dromara.warm.flow.core.service.UserService; - import java.util.List; -import java.util.Set; /** * 通用 工作流服务 @@ -13,30 +9,13 @@ import java.util.Set; */ public interface IFlwCommonService { - /** - * 获取工作流用户service - * - * @return 工作流用户service - */ - UserService getFlowUserService(); - /** * 构建工作流用户 * - * @param userList 办理用户 - * @param taskId 任务ID + * @param permissionList 办理用户 * @return 用户 */ - Set buildUser(List userList, Long taskId); - - /** - * 构建工作流用户 - * - * @param userIdList 办理用户 - * @param taskId 任务ID - * @return 用户 - */ - Set buildFlowUser(List userIdList, Long taskId); + List buildUser(List permissionList); /** * 发送消息 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java index fcb078295..e172c001a 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java @@ -5,6 +5,7 @@ import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.warm.flow.core.entity.Node; +import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.orm.entity.FlowHisTask; import org.dromara.warm.flow.orm.entity.FlowNode; import org.dromara.warm.flow.orm.entity.FlowTask; @@ -38,6 +39,14 @@ public interface IFlwTaskService { */ boolean completeTask(CompleteTaskBo completeTaskBo); + /** + * 添加抄送人 + * + * @param task 任务信息 + * @param flowCopyList 抄送人 + */ + void setCopy(Task task, List flowCopyList); + /** * 查询当前用户的待办任务 * diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index 878d4f4cc..e6dc8155d 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -13,21 +13,18 @@ import org.dromara.common.sse.dto.SseMessageDto; import org.dromara.common.sse.utils.SseMessageUtils; import org.dromara.warm.flow.core.entity.Node; import org.dromara.warm.flow.core.entity.Task; -import org.dromara.warm.flow.core.entity.User; import org.dromara.warm.flow.core.enums.SkipType; import org.dromara.warm.flow.core.service.NodeService; -import org.dromara.warm.flow.core.service.UserService; import org.dromara.warm.flow.orm.entity.FlowTask; -import org.dromara.warm.flow.orm.entity.FlowUser; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.enums.MessageTypeEnum; -import org.dromara.workflow.common.enums.TaskAssigneeType; import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwTaskAssigneeService; import org.dromara.workflow.service.IFlwTaskService; import org.springframework.stereotype.Service; -import java.util.*; +import java.util.ArrayList; +import java.util.List; import java.util.stream.Collectors; @@ -41,82 +38,27 @@ import java.util.stream.Collectors; @RequiredArgsConstructor @Service public class FlwCommonServiceImpl implements IFlwCommonService { - private final UserService userService; private final NodeService nodeService; - /** - * 获取工作流用户service - */ - @Override - public UserService getFlowUserService() { - return userService; - } - /** * 构建工作流用户 * - * @param userList 办理用户 - * @param taskId 任务ID + * @param permissionList 办理用户 * @return 用户 */ @Override - public Set buildUser(List userList, Long taskId) { - if (CollUtil.isEmpty(userList)) { - return Set.of(); + public List buildUser(List permissionList) { + if (CollUtil.isEmpty(permissionList)) { + return List.of(); } - Set list = new HashSet<>(); - Set processedBySet = new HashSet<>(); IFlwTaskAssigneeService taskAssigneeService = SpringUtils.getBean(IFlwTaskAssigneeService.class); - Map> userListMap = StreamUtils.groupByKey(userList, User::getType); - for (Map.Entry> entry : userListMap.entrySet()) { - List entryValue = entry.getValue(); - String processedBys = StreamUtils.join(entryValue, User::getProcessedBy); - // 根据 processedBy 前缀判断处理人类型,分别获取用户列表 - List users = taskAssigneeService.fetchUsersByStorageIds(processedBys); - // 转换为 FlowUser 并添加到结果集合 - if (CollUtil.isNotEmpty(users)) { - users.forEach(dto -> { - String processedBy = String.valueOf(dto.getUserId()); - if (!processedBySet.contains(processedBy)) { - FlowUser flowUser = new FlowUser(); - flowUser.setType(entry.getKey()); - flowUser.setProcessedBy(processedBy); - flowUser.setAssociated(taskId); - list.add(flowUser); - processedBySet.add(processedBy); - } - }); - } - } - return list; + String processedBys = CollUtil.join(permissionList, StringUtils.SEPARATOR); + // 根据 processedBy 前缀判断处理人类型,分别获取用户列表 + List users = taskAssigneeService.fetchUsersByStorageIds(processedBys); + + return StreamUtils.toList(users, userDTO -> String.valueOf(userDTO.getUserId())); } - /** - * 构建工作流用户 - * - * @param userIdList 办理用户 - * @param taskId 任务ID - * @return 用户 - */ - @Override - public Set buildFlowUser(List userIdList, Long taskId) { - if (CollUtil.isEmpty(userIdList)) { - return Set.of(); - } - Set list = new HashSet<>(); - Set processedBySet = new HashSet<>(); - for (String userId : userIdList) { - if (!processedBySet.contains(userId)) { - FlowUser flowUser = new FlowUser(); - flowUser.setType(TaskAssigneeType.APPROVER.getCode()); - flowUser.setProcessedBy(String.valueOf(userId)); - flowUser.setAssociated(taskId); - list.add(flowUser); - processedBySet.add(String.valueOf(userId)); - } - } - return list; - } /** * 发送消息 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index be64dcbbc..1457f1d71 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -25,6 +25,7 @@ import org.dromara.common.core.validate.EditGroup; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.entity.*; import org.dromara.warm.flow.core.enums.NodeType; @@ -155,6 +156,14 @@ public class FlwTaskServiceImpl implements IFlwTaskService { String notice = completeTaskBo.getNotice(); // 获取抄送人 List flowCopyList = completeTaskBo.getFlowCopyList(); + // 设置抄送人 + completeTaskBo.getVariables().put("flowCopyList", flowCopyList); + // 消息类型 + completeTaskBo.getVariables().put("messageType", messageType); + // 消息通知 + completeTaskBo.getVariables().put("notice", notice); + + FlowTask flowTask = flowTaskMapper.selectById(taskId); if (ObjectUtil.isNull(flowTask)) { throw new ServiceException("流程任务不存在或任务已审批!"); @@ -180,12 +189,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { .hisStatus(TaskStatusEnum.PASS.getStatus()) .hisTaskExt(completeTaskBo.getFileId()); // 执行任务跳转,并根据返回的处理人设置下一步处理人 - Instance instance = taskService.skip(taskId, flowParams); - this.setHandler(instance, flowTask, flowCopyList); - // 消息通知 - flwCommonService.sendMessage(definition.getFlowName(), ins.getId(), messageType, notice); - //设置下一环节处理人 - setNextHandler(ins.getId(), completeTaskBo.getAssigneeMap()); + taskService.skip(taskId, flowParams); return true; } catch (Exception e) { log.error(e.getMessage(), e); @@ -193,33 +197,6 @@ public class FlwTaskServiceImpl implements IFlwTaskService { } } - /** - * 设置下一环节处理人 - * - * @param instanceId 实例ID - * @param assigneeMap 办理人 - */ - private void setNextHandler(Long instanceId, Map assigneeMap) { - if (CollUtil.isEmpty(assigneeMap)) { - return; - } - Instance inst = insService.getById(instanceId); - List flowTaskList = selectByInstId(instanceId); - Map variableMap = inst.getVariableMap(); - for (FlowTask task : flowTaskList) { - if (variableMap != null && variableMap.containsKey(task.getNodeCode())) { - String userIds = variableMap.get(task.getNodeCode()).toString(); - // 批量删除现有任务的办理人记录 - flwCommonService.getFlowUserService().deleteByTaskIds(List.of(task.getId())); - // 批量新增任务办理人记录 - Set users = flwCommonService.buildFlowUser(List.of(userIds.split(StringUtils.SEPARATOR)), task.getId()); - flwCommonService.getFlowUserService().saveBatch(new ArrayList<>(users)); - variableMap.remove(task.getNodeCode()); - } - } - taskService.mergeVariable(inst, variableMap); - } - /** * 设置弹窗处理人 * @@ -251,54 +228,14 @@ public class FlwTaskServiceImpl implements IFlwTaskService { return map; } - /** - * 设置办理人 - * - * @param instance 实例 - * @param task (当前任务)未办理的任务 - * @param flowCopyList 抄送人 - */ - private void setHandler(Instance instance, FlowTask task, List flowCopyList) { - if (ObjectUtil.isNull(instance)) { - return; - } - // 添加抄送人 - this.setCopy(task, flowCopyList); - // 根据流程实例ID查询所有关联的任务 - List flowTasks = this.selectByInstId(instance.getId()); - if (CollUtil.isEmpty(flowTasks)) { - return; - } - List taskIdList = StreamUtils.toList(flowTasks, FlowTask::getId); - // 获取与当前任务关联的用户列表 - List associatedUsers = flwCommonService.getFlowUserService().getByAssociateds(taskIdList); - if (CollUtil.isEmpty(associatedUsers)) { - return; - } - List userList = new ArrayList<>(); - // 遍历任务列表,处理每个任务的办理人 - for (FlowTask flowTask : flowTasks) { - List users = StreamUtils.filter(associatedUsers, user -> Objects.equals(user.getAssociated(), flowTask.getId())); - if (CollUtil.isNotEmpty(users)) { - userList.addAll(flwCommonService.buildUser(users, flowTask.getId())); - } - } - // 批量删除现有任务的办理人记录 - flwCommonService.getFlowUserService().deleteByTaskIds(taskIdList); - // 确保要保存的 userList 不为空 - if (CollUtil.isEmpty(userList)) { - return; - } - flwCommonService.getFlowUserService().saveBatch(userList); - } - /** * 添加抄送人 * * @param task 任务信息 * @param flowCopyList 抄送人 */ - public void setCopy(FlowTask task, List flowCopyList) { + @Override + public void setCopy(Task task, List flowCopyList) { if (CollUtil.isEmpty(flowCopyList)) { return; } @@ -329,7 +266,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { return flowUser; }).collect(Collectors.toList()); // 批量保存抄送人员 - flwCommonService.getFlowUserService().saveBatch(userList); + FlowEngine.userService().saveBatch(userList); } /** @@ -456,21 +393,25 @@ public class FlwTaskServiceImpl implements IFlwTaskService { Instance inst = insService.getById(task.getInstanceId()); BusinessStatusEnum.checkBackStatus(inst.getFlowStatus()); Long definitionId = task.getDefinitionId(); - Definition definition = defService.getById(definitionId); String applyNodeCode = flwCommonService.applyNodeCode(definitionId); + + Map variable = new HashMap<>(); + // 设置抄送人 + variable.put("flowCopyList", bo.getMessageType()); + // 消息类型 + variable.put("messageType", messageType); + // 消息通知 + variable.put("notice", notice); + FlowParams flowParams = FlowParams.build() .nodeCode(bo.getNodeCode()) + .variable(variable) .message(message) .skipType(SkipType.REJECT.getKey()) .flowStatus(applyNodeCode.equals(bo.getNodeCode()) ? TaskStatusEnum.BACK.getStatus() : TaskStatusEnum.WAITING.getStatus()) .hisStatus(TaskStatusEnum.BACK.getStatus()) .hisTaskExt(bo.getFileId()); taskService.skip(task.getId(), flowParams); - - Instance instance = insService.getById(inst.getId()); - this.setHandler(instance, task, null); - // 消息通知 - flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); return true; } catch (Exception e) { log.error(e.getMessage(), e); @@ -745,7 +686,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { List flowTasks = this.selectByIdList(taskIdList); // 批量删除现有任务的办理人记录 if (CollUtil.isNotEmpty(flowTasks)) { - flwCommonService.getFlowUserService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId)); + FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId)); List userList = flowTasks.stream() .map(flowTask -> { FlowUser flowUser = new FlowUser(); @@ -756,7 +697,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { }) .collect(Collectors.toList()); if (CollUtil.isNotEmpty(userList)) { - flwCommonService.getFlowUserService().saveBatch(userList); + FlowEngine.userService().saveBatch(userList); } } } catch (Exception e) { @@ -775,13 +716,13 @@ public class FlwTaskServiceImpl implements IFlwTaskService { public Map> currentTaskAllUser(List taskIdList) { Map> map = new HashMap<>(); // 获取与当前任务关联的用户列表 - List associatedUsers = flwCommonService.getFlowUserService().getByAssociateds(taskIdList); + List associatedUsers = FlowEngine.userService().getByAssociateds(taskIdList); Map> listMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated); for (Map.Entry> entry : listMap.entrySet()) { List value = entry.getValue(); if (CollUtil.isNotEmpty(value)) { - List userDTOS = userService.selectListByIds(StreamUtils.toList(value, e -> Long.valueOf(e.getProcessedBy()))); - map.put(entry.getKey(), userDTOS); + List userDtoList = userService.selectListByIds(StreamUtils.toList(value, e -> Long.valueOf(e.getProcessedBy()))); + map.put(entry.getKey(), userDtoList); } } return map; @@ -795,7 +736,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { @Override public List currentTaskAllUser(Long taskId) { // 获取与当前任务关联的用户列表 - List userList = flwCommonService.getFlowUserService().getByAssociateds(Collections.singletonList(taskId)); + List userList = FlowEngine.userService().getByAssociateds(Collections.singletonList(taskId)); if (CollUtil.isEmpty(userList)) { return Collections.emptyList(); } From 1a12aecd49ffb787d729364efca992cce567c8b4 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Mon, 19 May 2025 12:27:56 +0800 Subject: [PATCH 080/121] =?UTF-8?q?docs=20=E4=BC=98=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E8=87=AA=E5=AE=9A=E4=B9=89=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflow/common/ConditionalOnEnable.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/ConditionalOnEnable.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/ConditionalOnEnable.java index 5d24b3509..e844398e2 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/ConditionalOnEnable.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/ConditionalOnEnable.java @@ -7,6 +7,21 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * 自定义条件注解,用于基于配置启用或禁用特定功能 + *

+ * 该注解只会在配置文件中 `warm-flow.enabled=true` 时,标注了此注解的类或方法才会被 Spring 容器加载 + *

+ * 示例配置: + *

+ * warm-flow:
+ *   enabled: true  # 设置为 true 时,启用工作流功能
+ * 
+ *

+ * 使用此注解时,可以动态控制工作流功能是否启用,而不需要修改代码逻辑 + * + * @author Lion Li + */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @ConditionalOnProperty(value = "warm-flow.enabled", havingValue = "true") From 52ddccba3eee934e5718e49979099ae21473ed6d Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Mon, 19 May 2025 15:13:12 +0800 Subject: [PATCH 081/121] =?UTF-8?q?update=20=E5=8D=87=E7=BA=A7JustAuth?= =?UTF-8?q?=E7=9A=84=E9=92=89=E9=92=89=E5=92=8C=E5=BE=AE=E4=BF=A1=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E6=96=B9=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/dromara/common/social/utils/SocialUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java index a73b229f8..3f7924d9b 100644 --- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java @@ -43,7 +43,7 @@ public class SocialUtils { .redirectUri(obj.getRedirectUri()) .scopes(obj.getScopes()); return switch (source.toLowerCase()) { - case "dingtalk" -> new AuthDingTalkRequest(builder.build(), STATE_CACHE); + case "dingtalk" -> new AuthDingTalkV2Request(builder.build(), STATE_CACHE); case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE); case "github" -> new AuthGithubRequest(builder.build(), STATE_CACHE); case "gitee" -> new AuthGiteeRequest(builder.build(), STATE_CACHE); @@ -61,7 +61,7 @@ public class SocialUtils { case "renren" -> new AuthRenrenRequest(builder.build(), STATE_CACHE); case "stack_overflow" -> new AuthStackOverflowRequest(builder.stackOverflowKey(obj.getStackOverflowKey()).build(), STATE_CACHE); case "huawei" -> new AuthHuaweiV3Request(builder.build(), STATE_CACHE); - case "wechat_enterprise" -> new AuthWeChatEnterpriseQrcodeRequest(builder.agentId(obj.getAgentId()).build(), STATE_CACHE); + case "wechat_enterprise" -> new AuthWeChatEnterpriseQrcodeV2Request(builder.agentId(obj.getAgentId()).build(), STATE_CACHE); case "gitlab" -> new AuthGitlabRequest(builder.build(), STATE_CACHE); case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE); case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE); From 82a5ed632fe6c9f34067fed63a6b9854007c1b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 19 May 2025 16:06:17 +0800 Subject: [PATCH 082/121] =?UTF-8?q?add=20=E5=A2=9E=E5=8A=A0=20=E6=88=90?= =?UTF-8?q?=E5=91=98=E9=A1=B9=E7=9B=AE=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f4d4a2873..fe60405d5 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ > 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)
> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5) > 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean) +> 成员项目地址: 删除多租户与工作流 [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single) > 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网) From 62bbd78033eae84200b2fcd33ec3cd6f584d5f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 19 May 2025 17:09:38 +0800 Subject: [PATCH 083/121] =?UTF-8?q?add=20=E5=A2=9E=E5=8A=A0=20=E6=88=90?= =?UTF-8?q?=E5=91=98=E9=A1=B9=E7=9B=AE=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fe60405d5..a6fd86c93 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,9 @@ > 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system) > 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)
-> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5) -> 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean) -> 成员项目地址: 删除多租户与工作流 [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single) +> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)
+> 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)
+> 成员项目地址: 删除多租户与工作流 [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single)
> 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网) From c054029cfcb20411733a369cf9296dba5cbfbcf7 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Mon, 19 May 2025 21:23:45 +0800 Subject: [PATCH 084/121] update snailjob 1.4.0 => 1.5.0 --- pom.xml | 2 +- script/sql/oracle/oracle_ry_job.sql | 27 ++++++++----- script/sql/postgres/postgres_ry_job.sql | 24 ++++++++---- script/sql/ry_job.sql | 11 ++++-- script/sql/sqlserver/sqlserver_ry_job.sql | 48 ++++++++++++++++++----- 5 files changed, 80 insertions(+), 32 deletions(-) diff --git a/pom.xml b/pom.xml index 39ca8809c..cdfd7bc56 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 3.45.1 2.2.7 4.3.1 - 1.4.0 + 1.5.0 1.4.6 0.2.0 1.18.36 diff --git a/script/sql/oracle/oracle_ry_job.sql b/script/sql/oracle/oracle_ry_job.sql index 104aabbaa..ba914e5f6 100644 --- a/script/sql/oracle/oracle_ry_job.sql +++ b/script/sql/oracle/oracle_ry_job.sql @@ -3,7 +3,7 @@ SnailJob Database Transfer Tool Source Server Type : MySQL Target Server Type : Oracle - Date: 2025-02-25 22:16:28 + Date: 2025-04-26 10:01:54 */ @@ -149,7 +149,9 @@ CREATE TABLE sj_retry_dead_letter id number GENERATED ALWAYS AS IDENTITY, namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, group_name varchar2(64) NULL, + group_id number NOT NULL, scene_name varchar2(64) NULL, + scene_id number NOT NULL, idempotent_id varchar2(64) NULL, biz_no varchar2(64) DEFAULT '' NULL, executor_name varchar2(512) DEFAULT '' NULL, @@ -169,7 +171,9 @@ CREATE INDEX idx_sj_retry_dead_letter_04 ON sj_retry_dead_letter (create_dt); COMMENT ON COLUMN sj_retry_dead_letter.id IS '主键'; COMMENT ON COLUMN sj_retry_dead_letter.namespace_id IS '命名空间id'; COMMENT ON COLUMN sj_retry_dead_letter.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_dead_letter.group_id IS '组Id'; COMMENT ON COLUMN sj_retry_dead_letter.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_dead_letter.scene_id IS '场景ID'; COMMENT ON COLUMN sj_retry_dead_letter.idempotent_id IS '幂等id'; COMMENT ON COLUMN sj_retry_dead_letter.biz_no IS '业务编号'; COMMENT ON COLUMN sj_retry_dead_letter.executor_name IS '执行器名称'; @@ -184,7 +188,9 @@ CREATE TABLE sj_retry id number GENERATED ALWAYS AS IDENTITY, namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, group_name varchar2(64) NULL, + group_id number NOT NULL, scene_name varchar2(64) NULL, + scene_id number NOT NULL, idempotent_id varchar2(64) NULL, biz_no varchar2(64) DEFAULT '' NULL, executor_name varchar2(512) DEFAULT '' NULL, @@ -204,19 +210,20 @@ CREATE TABLE sj_retry ALTER TABLE sj_retry ADD CONSTRAINT pk_sj_retry PRIMARY KEY (id); -CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (namespace_id, group_name, task_type, idempotent_id, deleted); +CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (scene_id, task_type, idempotent_id, deleted); -CREATE INDEX idx_sj_retry_01 ON sj_retry (namespace_id, group_name, scene_name); -CREATE INDEX idx_sj_retry_02 ON sj_retry (namespace_id, group_name, retry_status); -CREATE INDEX idx_sj_retry_03 ON sj_retry (idempotent_id); -CREATE INDEX idx_sj_retry_04 ON sj_retry (biz_no); -CREATE INDEX idx_sj_retry_05 ON sj_retry (parent_id); -CREATE INDEX idx_sj_retry_06 ON sj_retry (create_dt); +CREATE INDEX idx_sj_retry_01 ON sj_retry (biz_no); +CREATE INDEX idx_sj_retry_02 ON sj_retry (retry_status, bucket_index); +CREATE INDEX idx_sj_retry_03 ON sj_retry (parent_id); +CREATE INDEX idx_sj_retry_04 ON sj_retry (create_dt); +CREATE INDEX idx_sj_retry_05 ON sj_retry (idempotent_id); COMMENT ON COLUMN sj_retry.id IS '主键'; COMMENT ON COLUMN sj_retry.namespace_id IS '命名空间id'; COMMENT ON COLUMN sj_retry.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry.group_id IS '组Id'; COMMENT ON COLUMN sj_retry.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry.scene_id IS '场景ID'; COMMENT ON COLUMN sj_retry.idempotent_id IS '幂等id'; COMMENT ON COLUMN sj_retry.biz_no IS '业务编号'; COMMENT ON COLUMN sj_retry.executor_name IS '执行器名称'; @@ -395,7 +402,7 @@ COMMENT ON TABLE sj_server_node IS '服务器节点'; -- sj_distributed_lock CREATE TABLE sj_distributed_lock ( - name varchar2(64) NOT NULL, + name varchar2(64) NULL, lock_until timestamp(3) DEFAULT CURRENT_TIMESTAMP(3) NOT NULL, locked_at timestamp(3) DEFAULT CURRENT_TIMESTAMP(3) NOT NULL, locked_by varchar2(255) NULL, @@ -404,7 +411,7 @@ CREATE TABLE sj_distributed_lock ); ALTER TABLE sj_distributed_lock - ADD CONSTRAINT pk_sj_distributed_lock PRIMARY KEY (name); + ADD CONSTRAINT pk_sj_distributed_lock PRIMARY KEY (id); COMMENT ON COLUMN sj_distributed_lock.name IS '锁名称'; COMMENT ON COLUMN sj_distributed_lock.lock_until IS '锁定时长'; diff --git a/script/sql/postgres/postgres_ry_job.sql b/script/sql/postgres/postgres_ry_job.sql index 8b682dfe5..10612e65c 100644 --- a/script/sql/postgres/postgres_ry_job.sql +++ b/script/sql/postgres/postgres_ry_job.sql @@ -2,7 +2,7 @@ SnailJob Database Transfer Tool Source Server Type : MySQL Target Server Type : PostgreSQL - Date: 2025-02-25 22:15:32 + Date: 2025-04-26 09:56:45 */ @@ -136,7 +136,9 @@ CREATE TABLE sj_retry_dead_letter id bigserial PRIMARY KEY, namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', group_name varchar(64) NOT NULL, + group_id bigint NOT NULL, scene_name varchar(64) NOT NULL, + scene_id bigint NOT NULL, idempotent_id varchar(64) NOT NULL, biz_no varchar(64) NOT NULL DEFAULT '', executor_name varchar(512) NOT NULL DEFAULT '', @@ -153,7 +155,9 @@ CREATE INDEX idx_sj_retry_dead_letter_04 ON sj_retry_dead_letter (create_dt); COMMENT ON COLUMN sj_retry_dead_letter.id IS '主键'; COMMENT ON COLUMN sj_retry_dead_letter.namespace_id IS '命名空间id'; COMMENT ON COLUMN sj_retry_dead_letter.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_dead_letter.group_id IS '组Id'; COMMENT ON COLUMN sj_retry_dead_letter.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_dead_letter.scene_id IS '场景ID'; COMMENT ON COLUMN sj_retry_dead_letter.idempotent_id IS '幂等id'; COMMENT ON COLUMN sj_retry_dead_letter.biz_no IS '业务编号'; COMMENT ON COLUMN sj_retry_dead_letter.executor_name IS '执行器名称'; @@ -168,7 +172,9 @@ CREATE TABLE sj_retry id bigserial PRIMARY KEY, namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', group_name varchar(64) NOT NULL, + group_id bigint NOT NULL, scene_name varchar(64) NOT NULL, + scene_id bigint NOT NULL, idempotent_id varchar(64) NOT NULL, biz_no varchar(64) NOT NULL DEFAULT '', executor_name varchar(512) NOT NULL DEFAULT '', @@ -185,19 +191,21 @@ CREATE TABLE sj_retry update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (namespace_id, group_name, task_type, idempotent_id, deleted); +CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (scene_id, task_type, idempotent_id, deleted); + +CREATE INDEX idx_sj_retry_01 ON sj_retry (biz_no); +CREATE INDEX idx_sj_retry_02 ON sj_retry (retry_status, bucket_index); +CREATE INDEX idx_sj_retry_03 ON sj_retry (parent_id); +CREATE INDEX idx_sj_retry_04 ON sj_retry (create_dt); +CREATE INDEX idx_sj_retry_05 ON sj_retry (idempotent_id); -CREATE INDEX idx_sj_retry_01 ON sj_retry (namespace_id, group_name, scene_name); -CREATE INDEX idx_sj_retry_02 ON sj_retry (namespace_id, group_name, retry_status); -CREATE INDEX idx_sj_retry_03 ON sj_retry (idempotent_id); -CREATE INDEX idx_sj_retry_04 ON sj_retry (biz_no); -CREATE INDEX idx_sj_retry_05 ON sj_retry (parent_id); -CREATE INDEX idx_sj_retry_06 ON sj_retry (create_dt); COMMENT ON COLUMN sj_retry.id IS '主键'; COMMENT ON COLUMN sj_retry.namespace_id IS '命名空间id'; COMMENT ON COLUMN sj_retry.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry.group_id IS '组Id'; COMMENT ON COLUMN sj_retry.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry.scene_id IS '场景ID'; COMMENT ON COLUMN sj_retry.idempotent_id IS '幂等id'; COMMENT ON COLUMN sj_retry.biz_no IS '业务编号'; COMMENT ON COLUMN sj_retry.executor_name IS '执行器名称'; diff --git a/script/sql/ry_job.sql b/script/sql/ry_job.sql index 3577d6614..dd86deb45 100644 --- a/script/sql/ry_job.sql +++ b/script/sql/ry_job.sql @@ -84,7 +84,9 @@ CREATE TABLE `sj_retry_dead_letter` `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', `group_name` varchar(64) NOT NULL COMMENT '组名称', + `group_id` bigint(20) NOT NULL COMMENT '组Id', `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `scene_id` bigint(20) NOT NULL COMMENT '场景ID', `idempotent_id` varchar(64) NOT NULL COMMENT '幂等id', `biz_no` varchar(64) NOT NULL DEFAULT '' COMMENT '业务编号', `executor_name` varchar(512) NOT NULL DEFAULT '' COMMENT '执行器名称', @@ -105,7 +107,9 @@ CREATE TABLE `sj_retry` `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', `group_name` varchar(64) NOT NULL COMMENT '组名称', + `group_id` bigint(20) NOT NULL COMMENT '组Id', `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `scene_id` bigint(20) NOT NULL COMMENT '场景ID', `idempotent_id` varchar(64) NOT NULL COMMENT '幂等id', `biz_no` varchar(64) NOT NULL DEFAULT '' COMMENT '业务编号', `executor_name` varchar(512) NOT NULL DEFAULT '' COMMENT '执行器名称', @@ -121,13 +125,12 @@ CREATE TABLE `sj_retry` `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', PRIMARY KEY (`id`), - KEY `idx_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`), - KEY `idx_namespace_id_group_name_retry_status` (`namespace_id`, `group_name`, `retry_status`), - KEY `idx_idempotent_id` (`idempotent_id`), KEY `idx_biz_no` (`biz_no`), + KEY `idx_idempotent_id` (`idempotent_id`), + KEY `idx_retry_status_bucket_index` (`retry_status`, `bucket_index`), KEY `idx_parent_id` (`parent_id`), KEY `idx_create_dt` (`create_dt`), - UNIQUE KEY `uk_name_task_type_idempotent_id_deleted` (`namespace_id`, `group_name`, `task_type`, `idempotent_id`, `deleted`) + UNIQUE KEY `uk_scene_tasktype_idempotentid_deleted` (`scene_id`, `task_type`, `idempotent_id`, `deleted`) ) ENGINE = InnoDB AUTO_INCREMENT = 0 DEFAULT CHARSET = utf8mb4 COMMENT ='重试信息表'; diff --git a/script/sql/sqlserver/sqlserver_ry_job.sql b/script/sql/sqlserver/sqlserver_ry_job.sql index 5a0bbb39b..9268e9f0d 100644 --- a/script/sql/sqlserver/sqlserver_ry_job.sql +++ b/script/sql/sqlserver/sqlserver_ry_job.sql @@ -2,7 +2,7 @@ SnailJob Database Transfer Tool Source Server Type : MySQL Target Server Type : Microsoft SQL Server - Date: 2025-02-25 22:16:48 + Date: 2025-04-26 10:03:23 */ @@ -410,7 +410,9 @@ CREATE TABLE sj_retry_dead_letter id bigint NOT NULL PRIMARY KEY IDENTITY, namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', group_name nvarchar(64) NOT NULL, + group_id bigint NOT NULL, scene_name nvarchar(64) NOT NULL, + scene_id bigint NOT NULL, idempotent_id nvarchar(64) NOT NULL, biz_no nvarchar(64) NOT NULL DEFAULT '', executor_name nvarchar(512) NOT NULL DEFAULT '', @@ -450,6 +452,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'group_name' GO +EXEC sp_addextendedproperty + 'MS_Description', N'组Id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'group_id' +GO + EXEC sp_addextendedproperty 'MS_Description', N'场景名称', 'SCHEMA', N'dbo', @@ -457,6 +466,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'scene_name' GO +EXEC sp_addextendedproperty + 'MS_Description', N'场景ID', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'scene_id' +GO + EXEC sp_addextendedproperty 'MS_Description', N'幂等id', 'SCHEMA', N'dbo', @@ -511,7 +527,9 @@ CREATE TABLE sj_retry id bigint NOT NULL PRIMARY KEY IDENTITY, namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', group_name nvarchar(64) NOT NULL, + group_id bigint NOT NULL, scene_name nvarchar(64) NOT NULL, + scene_id bigint NOT NULL, idempotent_id nvarchar(64) NOT NULL, biz_no nvarchar(64) NOT NULL DEFAULT '', executor_name nvarchar(512) NOT NULL DEFAULT '', @@ -529,20 +547,18 @@ CREATE TABLE sj_retry ) GO -CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (namespace_id, group_name, task_type, idempotent_id, deleted) +CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (scene_id, task_type, idempotent_id, deleted) GO -CREATE INDEX idx_sj_retry_01 ON sj_retry (namespace_id, group_name, scene_name) +CREATE INDEX idx_sj_retry_01 ON sj_retry (biz_no) GO -CREATE INDEX idx_sj_retry_02 ON sj_retry (namespace_id, group_name, retry_status) +CREATE INDEX idx_sj_retry_02 ON sj_retry (retry_status, bucket_index) GO -CREATE INDEX idx_sj_retry_03 ON sj_retry (idempotent_id) +CREATE INDEX idx_sj_retry_03 ON sj_retry (parent_id) GO -CREATE INDEX idx_sj_retry_04 ON sj_retry (biz_no) +CREATE INDEX idx_sj_retry_04 ON sj_retry (create_dt) GO -CREATE INDEX idx_sj_retry_05 ON sj_retry (parent_id) -GO -CREATE INDEX idx_sj_retry_06 ON sj_retry (create_dt) +CREATE INDEX idx_sj_retry_05 ON sj_retry (idempotent_id) GO EXEC sp_addextendedproperty @@ -566,6 +582,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'group_name' GO +EXEC sp_addextendedproperty + 'MS_Description', N'组Id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'group_id' +GO + EXEC sp_addextendedproperty 'MS_Description', N'场景名称', 'SCHEMA', N'dbo', @@ -573,6 +596,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'scene_name' GO +EXEC sp_addextendedproperty + 'MS_Description', N'场景ID', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'scene_id' +GO + EXEC sp_addextendedproperty 'MS_Description', N'幂等id', 'SCHEMA', N'dbo', From 737838d92fafe41882db7552af11f56c5aed945e Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Tue, 20 May 2025 12:31:49 +0800 Subject: [PATCH 085/121] =?UTF-8?q?add=20=E6=96=B0=E5=A2=9E=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=AD=97=E5=85=B8=E5=80=BC=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/core/service/DictService.java | 9 +++ .../common/core/validate/dict/DictValue.java | 35 +++++++++ .../validate/dict/DictValueValidator.java | 42 +++++++++++ .../validate/enumd/EnumPatternValidator.java | 74 +++++++++---------- .../service/impl/SysDictTypeServiceImpl.java | 20 +++++ 5 files changed, 143 insertions(+), 37 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValue.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValueValidator.java diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java index d80395cc8..fc33f1e35 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java @@ -84,4 +84,13 @@ public interface DictService { */ List getDictData(String dictType); + /** + * 校验指定的字典类型下是否存在指定的字典值 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return true 表示该字典值在指定字典类型中有效;false 表示无效 + */ + Boolean isValidDictValue(String dictType, String dictValue); + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValue.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValue.java new file mode 100644 index 000000000..be842723a --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValue.java @@ -0,0 +1,35 @@ +package org.dromara.common.core.validate.dict; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 字典项校验注解 + * + * @author AprilWind + */ +@Constraint(validatedBy = DictValueValidator.class) +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DictValue { + + /** + * 字典类型,如 "sys_user_sex" + */ + String dictType(); + + /** + * 默认校验失败提示信息 + */ + String message() default "字典值无效"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValueValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValueValidator.java new file mode 100644 index 000000000..8507e219e --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValueValidator.java @@ -0,0 +1,42 @@ +package org.dromara.common.core.validate.dict; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.SpringUtils; + +/** + * 自定义字典值校验器 + * + * @author AprilWind + */ +public class DictValueValidator implements ConstraintValidator { + + /** + * 字典类型 + */ + private String dictType; + + /** + * 初始化校验器,提取注解上的字典类型 + * + * @param annotation 注解实例 + */ + @Override + public void initialize(DictValue annotation) { + this.dictType = annotation.dictType(); + } + + /** + * 校验字段值是否为指定字典类型中的合法值 + * + * @param value 被校验的字段值 + * @param context 校验上下文(可用于构建错误信息) + * @return true 表示校验通过(合法字典值),false 表示不通过 + */ + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return SpringUtils.getBean(DictService.class).isValidDictValue(dictType, value); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java index 6cfa11a33..e63f44a00 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java @@ -1,37 +1,37 @@ -package org.dromara.common.core.validate.enumd; - -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.core.utils.reflect.ReflectUtils; - -/** - * 自定义枚举校验注解实现 - * - * @author 秋辞未寒 - * @date 2024-12-09 - */ -public class EnumPatternValidator implements ConstraintValidator { - - private EnumPattern annotation;; - - @Override - public void initialize(EnumPattern annotation) { - ConstraintValidator.super.initialize(annotation); - this.annotation = annotation; - } - - @Override - public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { - if (StringUtils.isNotBlank(value)) { - String fieldName = annotation.fieldName(); - for (Object e : annotation.type().getEnumConstants()) { - if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) { - return true; - } - } - } - return false; - } - -} +package org.dromara.common.core.validate.enumd; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.reflect.ReflectUtils; + +/** + * 自定义枚举校验注解实现 + * + * @author 秋辞未寒 + * @date 2024-12-09 + */ +public class EnumPatternValidator implements ConstraintValidator { + + private EnumPattern annotation; + + @Override + public void initialize(EnumPattern annotation) { + ConstraintValidator.super.initialize(annotation); + this.annotation = annotation; + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if (StringUtils.isNotBlank(value)) { + String fieldName = annotation.fieldName(); + for (Object e : annotation.type().getEnumConstants()) { + if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) { + return true; + } + } + } + return false; + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java index c417b42ca..7eaea5563 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java @@ -294,4 +294,24 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService return BeanUtil.copyToList(list, DictDataDTO.class); } + /** + * 校验指定的字典类型下是否存在指定的字典值 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return true 表示该字典值在指定字典类型中有效;false 表示无效 + */ + @Override + public Boolean isValidDictValue(String dictType, String dictValue) { + if (StringUtils.isBlank(dictType) || StringUtils.isBlank(dictValue)) { + return false; + } + List datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); + if (CollUtil.isEmpty(datas)) { + return false; + } + Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel); + return map.containsKey(dictValue); + } + } From 1228e8f3eaacf96d61a652cf424e9483205ff84c Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Tue, 20 May 2025 14:03:11 +0800 Subject: [PATCH 086/121] =?UTF-8?q?update=20=E7=BB=9F=E4=B8=80=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=99=A8=E5=90=8D=E7=A7=B0=E9=A3=8E=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{dict/DictValue.java => dicts/DictPattern.java} | 6 +++--- .../DictPatternValidator.java} | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) rename ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/{dict/DictValue.java => dicts/DictPattern.java} (83%) rename ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/{dict/DictValueValidator.java => dicts/DictPatternValidator.java} (84%) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValue.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java similarity index 83% rename from ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValue.java rename to ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java index be842723a..d5199873d 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValue.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java @@ -1,4 +1,4 @@ -package org.dromara.common.core.validate.dict; +package org.dromara.common.core.validate.dicts; import jakarta.validation.Constraint; import jakarta.validation.Payload; @@ -13,10 +13,10 @@ import java.lang.annotation.Target; * * @author AprilWind */ -@Constraint(validatedBy = DictValueValidator.class) +@Constraint(validatedBy = DictPatternValidator.class) @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) -public @interface DictValue { +public @interface DictPattern { /** * 字典类型,如 "sys_user_sex" diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValueValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java similarity index 84% rename from ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValueValidator.java rename to ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java index 8507e219e..734975aea 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dict/DictValueValidator.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java @@ -1,4 +1,4 @@ -package org.dromara.common.core.validate.dict; +package org.dromara.common.core.validate.dicts; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; @@ -10,7 +10,7 @@ import org.dromara.common.core.utils.SpringUtils; * * @author AprilWind */ -public class DictValueValidator implements ConstraintValidator { +public class DictPatternValidator implements ConstraintValidator { /** * 字典类型 @@ -23,7 +23,7 @@ public class DictValueValidator implements ConstraintValidator Date: Tue, 20 May 2025 14:19:21 +0800 Subject: [PATCH 087/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=AD=97?= =?UTF-8?q?=E5=85=B8=E9=A1=B9=E6=A0=A1=E9=AA=8C=E6=B3=A8=E8=A7=A3=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=88=86=E9=9A=94=E7=AC=A6=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/core/service/DictService.java | 9 --------- .../core/validate/dicts/DictPattern.java | 5 +++++ .../validate/dicts/DictPatternValidator.java | 15 +++++++++++++- .../service/impl/SysDictTypeServiceImpl.java | 20 ------------------- 4 files changed, 19 insertions(+), 30 deletions(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java index fc33f1e35..d80395cc8 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java @@ -84,13 +84,4 @@ public interface DictService { */ List getDictData(String dictType); - /** - * 校验指定的字典类型下是否存在指定的字典值 - * - * @param dictType 字典类型 - * @param dictValue 字典值 - * @return true 表示该字典值在指定字典类型中有效;false 表示无效 - */ - Boolean isValidDictValue(String dictType, String dictValue); - } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java index d5199873d..73fc4c444 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java @@ -23,6 +23,11 @@ public @interface DictPattern { */ String dictType(); + /** + * 分隔符 + */ + String separator(); + /** * 默认校验失败提示信息 */ diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java index 734975aea..558a3438a 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java @@ -4,6 +4,7 @@ import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; import org.dromara.common.core.service.DictService; import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; /** * 自定义字典值校验器 @@ -17,6 +18,11 @@ public class DictPatternValidator implements ConstraintValidator datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); - if (CollUtil.isEmpty(datas)) { - return false; - } - Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel); - return map.containsKey(dictValue); - } - } From 1db0bc83b27deb43927711d5f40f4b5e613bc234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Wed, 21 May 2025 15:42:42 +0800 Subject: [PATCH 088/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E5=88=9B=E5=BB=BA=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E5=B0=86=E7=8A=B6=E6=80=81=E4=BA=A4=E7=BB=99=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E6=96=B9=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/core/domain/event/ProcessCreateTaskEvent.java | 5 +++++ .../dromara/workflow/handler/FlowProcessEventHandler.java | 1 + .../dromara/workflow/listener/WorkflowGlobalListener.java | 2 +- .../workflow/service/impl/TestLeaveServiceImpl.java | 8 +++++--- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessCreateTaskEvent.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessCreateTaskEvent.java index d603884d1..81d43fdc9 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessCreateTaskEvent.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessCreateTaskEvent.java @@ -51,4 +51,9 @@ public class ProcessCreateTaskEvent implements Serializable { */ private String businessId; + /** + * 流程状态 + */ + private String status; + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java index c9e7a9237..6a15d43ba 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java @@ -68,6 +68,7 @@ public class FlowProcessEventHandler { processCreateTaskEvent.setNodeCode(instance.getNodeCode()); processCreateTaskEvent.setNodeName(instance.getNodeName()); processCreateTaskEvent.setTaskId(taskId); + processCreateTaskEvent.setStatus(instance.getFlowStatus()); SpringUtils.context().publishEvent(processCreateTaskEvent); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index a46cbc5b3..2c95f8aaa 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -52,7 +52,7 @@ public class WorkflowGlobalListener implements GlobalListener { Instance instance = listenerVariable.getInstance(); Definition definition = listenerVariable.getDefinition(); Task task = listenerVariable.getTask(); - if (task != null && BusinessStatusEnum.WAITING.getStatus().equals(instance.getFlowStatus())) { + if (task != null) { // 判断流程状态(发布审批中事件) flowProcessEventHandler.processCreateTaskHandler(definition.getFlowCode(), instance, task.getId()); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java index 4ed393e58..521d890e9 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java @@ -176,9 +176,11 @@ public class TestLeaveServiceImpl implements ITestLeaveService { @EventListener(condition = "#processCreateTaskEvent.flowCode.startsWith('leave')") public void processCreateTaskHandler(ProcessCreateTaskEvent processCreateTaskEvent) { log.info("当前任务创建了{}", processCreateTaskEvent.toString()); - TestLeave testLeave = baseMapper.selectById(Long.valueOf(processCreateTaskEvent.getBusinessId())); - testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus()); - baseMapper.updateById(testLeave); + if (BusinessStatusEnum.WAITING.getStatus().equals(processCreateTaskEvent.getStatus())) { + TestLeave testLeave = baseMapper.selectById(Long.valueOf(processCreateTaskEvent.getBusinessId())); + testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus()); + baseMapper.updateById(testLeave); + } } /** From 8232908b3f637e67fdc9bfcd1fa173b9994c7e86 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Thu, 22 May 2025 17:07:05 +0800 Subject: [PATCH 089/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E8=AF=B7=E5=81=87=E6=97=A5=E6=9C=9F=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java | 3 +++ .../main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java index a1a4b5968..395f71d95 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java @@ -10,6 +10,7 @@ import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.workflow.domain.TestLeave; +import org.springframework.format.annotation.DateTimeFormat; import java.util.Date; @@ -40,6 +41,7 @@ public class TestLeaveBo extends BaseEntity { * 开始时间 */ @NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class}) + @DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd") private Date startDate; @@ -47,6 +49,7 @@ public class TestLeaveBo extends BaseEntity { * 结束时间 */ @NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class}) + @DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd") private Date endDate; diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java index 741949f79..57777edde 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java @@ -2,6 +2,7 @@ package org.dromara.workflow.domain.vo; import cn.idev.excel.annotation.ExcelIgnoreUnannotated; import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.workflow.domain.TestLeave; @@ -41,12 +42,14 @@ public class TestLeaveVo implements Serializable { * 开始时间 */ @ExcelProperty(value = "开始时间") + @JsonFormat(pattern = "yyyy-MM-dd") private Date startDate; /** * 结束时间 */ @ExcelProperty(value = "结束时间") + @JsonFormat(pattern = "yyyy-MM-dd") private Date endDate; /** From c40a8b2f0b70b525a117c0e5f799c37560475866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Thu, 22 May 2025 17:32:29 +0800 Subject: [PATCH 090/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E8=B7=AF=E7=94=B1=E8=BF=81=E7=A7=BB=E5=88=B0=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/sql/oracle/oracle_ry_vue_5.X.sql | 6 ++++++ script/sql/oracle/oracle_ry_workflow.sql | 2 ++ script/sql/postgres/postgres_ry_vue_5.X.sql | 5 +++++ script/sql/postgres/postgres_ry_workflow.sql | 3 +++ script/sql/ry_vue_5.X.sql | 5 +++++ script/sql/ry_workflow.sql | 2 ++ script/sql/sqlserver/sqlserver_ry_vue_5.X.sql | 11 +++++++++++ script/sql/sqlserver/sqlserver_ry_workflow.sql | 4 ++++ script/sql/update/oracle/update_5.3.1-5.4.0.sql | 8 ++++++++ script/sql/update/postgres/update_5.3.1-5.4.0.sql | 8 ++++++++ .../sql/update/sqlserver/update_5.3.1-5.4.0.sql | 15 +++++++++++++++ script/sql/update/update_5.3.1-5.4.0.sql | 8 ++++++++ 12 files changed, 77 insertions(+) diff --git a/script/sql/oracle/oracle_ry_vue_5.X.sql b/script/sql/oracle/oracle_ry_vue_5.X.sql index ce38b069d..e4455269a 100644 --- a/script/sql/oracle/oracle_ry_vue_5.X.sql +++ b/script/sql/oracle/oracle_ry_vue_5.X.sql @@ -447,6 +447,12 @@ insert into sys_menu values('115', '代码生成', '3', '2', 'gen', insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', 1, 0, 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, sysdate, null, null, '租户管理菜单'); insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, sysdate, null, null, '租户套餐管理菜单'); insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, sysdate, null, null, '客户端管理菜单'); +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '12', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '13', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '14', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '15', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate, null, null, ''); + -- springboot-admin监控 insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, sysdate, null, null, 'Admin监控菜单'); -- oss菜单 diff --git a/script/sql/oracle/oracle_ry_workflow.sql b/script/sql/oracle/oracle_ry_workflow.sql index 7925fc3e4..58453206d 100644 --- a/script/sql/oracle/oracle_ry_workflow.sql +++ b/script/sql/oracle/oracle_ry_workflow.sql @@ -387,6 +387,8 @@ INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category', INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11623', '流程分类查询', '11622', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:query', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11624', '流程分类新增', '11622', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:add', '#', 103, 1, SYSDATE, NULL, NULL, ''); diff --git a/script/sql/postgres/postgres_ry_vue_5.X.sql b/script/sql/postgres/postgres_ry_vue_5.X.sql index 0f92c839e..25c3b788b 100644 --- a/script/sql/postgres/postgres_ry_vue_5.X.sql +++ b/script/sql/postgres/postgres_ry_vue_5.X.sql @@ -448,6 +448,11 @@ insert into sys_menu values('115', '代码生成', '3', '2', 'gen', insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', '1', '0', 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, now(), null, null, '租户管理菜单'); insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', '1', '0', 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, now(), null, null, '租户套餐管理菜单'); insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', '1', '0', 'C', '0', '0', 'system:client:list', 'international', 103, 1, now(), null, null, '客户端管理菜单'); +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', '1', '1', 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '12', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', '1', '1', 'C', '1', '0', 'system:role:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '13', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', '1', '1', 'C', '1', '0', 'system:user:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '14', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', '1', '1', 'C', '1', '0', 'system:dict:list', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '15', 'oss-config/index', 'system/oss/config', '', '1', '1', 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, now(), null, null, ''); -- springboot-admin监控 insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', '1', '0', 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, now(), null, null, 'Admin监控菜单'); diff --git a/script/sql/postgres/postgres_ry_workflow.sql b/script/sql/postgres/postgres_ry_workflow.sql index c943024df..1b3b58b1c 100644 --- a/script/sql/postgres/postgres_ry_workflow.sql +++ b/script/sql/postgres/postgres_ry_workflow.sql @@ -370,6 +370,9 @@ INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category', INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); + INSERT INTO sys_menu VALUES ('11623', '流程分类查询', '11622', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:query', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11624', '流程分类新增', '11622', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:add', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11625', '流程分类修改', '11622', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:edit', '#', 103, 1, now(), NULL, NULL, ''); diff --git a/script/sql/ry_vue_5.X.sql b/script/sql/ry_vue_5.X.sql index ffc469e29..3e0b3a432 100644 --- a/script/sql/ry_vue_5.X.sql +++ b/script/sql/ry_vue_5.X.sql @@ -282,6 +282,11 @@ insert into sys_menu values('115', '代码生成', '3', '2', 'gen', insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', 1, 0, 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, sysdate(), null, null, '租户管理菜单'); insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, sysdate(), null, null, '租户套餐管理菜单'); insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, sysdate(), null, null, '客户端管理菜单'); +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '12', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '13', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '14', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '15', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate(), null, null, ''); -- springboot-admin监控 insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, sysdate(), null, null, 'Admin监控菜单'); diff --git a/script/sql/ry_workflow.sql b/script/sql/ry_workflow.sql index a9cddeb24..d0946181f 100644 --- a/script/sql/ry_workflow.sql +++ b/script/sql/ry_workflow.sql @@ -214,6 +214,8 @@ insert into sys_menu values ('11622', '流程分类', '11616', '1', 'category', insert into sys_menu values ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, sysdate(), NULL, NULL, ''); insert into sys_menu values ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, sysdate(), NULL, NULL, ''); insert into sys_menu values ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', 1, 1, 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); -- 流程分类管理相关按钮 insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1,sysdate(), null, null, ''); insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add', '#', 103, 1,sysdate(), null, null, ''); diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql index c7fed712a..cff7e182b 100644 --- a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql +++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql @@ -1690,6 +1690,17 @@ INSERT sys_menu VALUES (122, N'租户套餐管理', 6, 2, N'tenantPackage', N'sy GO INSERT sys_menu VALUES (123, N'客户端管理', 1, 11, N'client', N'system/client/index', N'', 1, 0, N'C', N'0', N'0', N'system:client:list', N'international', 103, 1, getdate(), NULL, NULL, N'客户端管理菜单') GO +INSERT sys_menu VALUES (116, N'修改生成配置', 3, 2, N'gen-edit/index/:tableId(\\d+)', N'tool/gen/editTable', N'', 1, 1, N'C', N'1', N'0', N'tool:gen:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (130, N'分配用户', 1, 12, N'role-auth/user/:roleId(\\d+)', N'system/role/authUser', N'', 1, 1, N'C', N'1', N'0', N'system:role:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (131, N'分配角色', 1, 13, N'user-auth/role/:userId(\\d+)', N'system/user/authRole', N'', 1, 1, N'C', N'1', N'0', N'system:user:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (132, N'字典数据', 1, 14, N'dict-data/index/:dictId(\\d+)', N'system/dict/data', N'', 1, 1, N'C', N'1', N'0', N'system:dict:list', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (133, N'文件配置管理', 1, 15, N'oss-config/index', N'system/oss/config', N'', 1, 1, N'C', N'1', N'0', N'system:ossConfig:list', N'#', 103, 1, getdate(), null, null, N''); +GO + INSERT sys_menu VALUES (117, N'Admin监控', 2, 5, N'Admin', N'monitor/admin/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:admin:list', N'dashboard', 103, 1, getdate(), NULL, NULL, N'Admin监控菜单'); GO INSERT sys_menu VALUES (118, N'文件管理', 1, 10, N'oss', N'system/oss/index', N'', 1, 0, N'C', '0', N'0', N'system:oss:list', N'upload', 103, 1, getdate(), NULL, NULL, N'文件管理菜单'); diff --git a/script/sql/sqlserver/sqlserver_ry_workflow.sql b/script/sql/sqlserver/sqlserver_ry_workflow.sql index 46f0bf941..34c0d04e3 100644 --- a/script/sql/sqlserver/sqlserver_ry_workflow.sql +++ b/script/sql/sqlserver/sqlserver_ry_workflow.sql @@ -1272,6 +1272,10 @@ INSERT sys_menu VALUES (11630, N'流程监控', 11616, 4, N'monitor', NULL, N'', GO INSERT sys_menu VALUES (11631, N'待办任务', 11630, 2, N'allTaskWaiting', N'workflow/task/allTaskWaiting', N'', 1, 1, N'C', N'0', N'0', N'', N'waiting', 103, 1, GETDATE(), NULL, NULL, N''); GO +INSERT sys_menu VALUES (11700, N'流程设计', 11616, 5, N'design/index', N'workflow/processDefinition/design', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11701, N'请假申请', 11616, 6, N'leaveEdit/index', N'workflow/leave/leaveEdit', N'', 1, 1, N'C', N'0', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO -- 流程分类管理相关按钮 INSERT sys_menu VALUES (11623, N'流程分类查询', 11622, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:category:query', N'#', 103, 1, GETDATE(), NULL, NULL, N''); diff --git a/script/sql/update/oracle/update_5.3.1-5.4.0.sql b/script/sql/update/oracle/update_5.3.1-5.4.0.sql index d4de6ad2c..1bf6c1cc1 100644 --- a/script/sql/update/oracle/update_5.3.1-5.4.0.sql +++ b/script/sql/update/oracle/update_5.3.1-5.4.0.sql @@ -8,3 +8,11 @@ COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(0待提交 1审 ALTER TABLE sys_social MODIFY (access_token VARCHAR2(2000 BYTE)) MODIFY (refresh_token VARCHAR2(2000 BYTE)); + +INSERT INTO sys_menu VALUES ('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('130', '分配用户', '1', '12', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('131', '分配角色', '1', '13', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('132', '字典数据', '1', '14', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('133', '文件配置管理', '1', '15', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); diff --git a/script/sql/update/postgres/update_5.3.1-5.4.0.sql b/script/sql/update/postgres/update_5.3.1-5.4.0.sql index 8f5c9d4e0..8fb7915ed 100644 --- a/script/sql/update/postgres/update_5.3.1-5.4.0.sql +++ b/script/sql/update/postgres/update_5.3.1-5.4.0.sql @@ -8,3 +8,11 @@ COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(0待提交 1审 ALTER TABLE sys_social ALTER COLUMN access_token TYPE varchar(2000), ALTER COLUMN refresh_token TYPE varchar(2000); + +INSERT INTO sys_menu VALUES ('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', '1', '1', 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('130', '分配用户', '1', '12', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', '1', '1', 'C', '1', '0', 'system:role:edit', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('131', '分配角色', '1', '13', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', '1', '1', 'C', '1', '0', 'system:user:edit', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('132', '字典数据', '1', '14', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', '1', '1', 'C', '1', '0', 'system:dict:list', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('133', '文件配置管理', '1', '15', 'oss-config/index', 'system/oss/config', '', '1', '1', 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); diff --git a/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql index 2cbf99e01..0c1f0fcea 100644 --- a/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql +++ b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql @@ -46,3 +46,18 @@ ALTER TABLE sys_social ALTER COLUMN access_token VARCHAR(2000) NOT NULL GO ALTER TABLE sys_social ALTER COLUMN refresh_token VARCHAR(2000) NULL GO + +INSERT sys_menu VALUES (116, N'修改生成配置', 3, 2, N'gen-edit/index/:tableId(\\d+)', N'tool/gen/editTable', N'', 1, 1, N'C', N'1', N'0', N'tool:gen:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (130, N'分配用户', 1, 12, N'role-auth/user/:roleId(\\d+)', N'system/role/authUser', N'', 1, 1, N'C', N'1', N'0', N'system:role:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (131, N'分配角色', 1, 13, N'user-auth/role/:userId(\\d+)', N'system/user/authRole', N'', 1, 1, N'C', N'1', N'0', N'system:user:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (132, N'字典数据', 1, 14, N'dict-data/index/:dictId(\\d+)', N'system/dict/data', N'', 1, 1, N'C', N'1', N'0', N'system:dict:list', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (133, N'文件配置管理', 1, 15, N'oss-config/index', N'system/oss/config', N'', 1, 1, N'C', N'1', N'0', N'system:ossConfig:list', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (11700, N'流程设计', 11616, 5, N'design/index', N'workflow/processDefinition/design', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11701, N'请假申请', 11616, 6, N'leaveEdit/index', N'workflow/leave/leaveEdit', N'', 1, 1, N'C', N'0', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO diff --git a/script/sql/update/update_5.3.1-5.4.0.sql b/script/sql/update/update_5.3.1-5.4.0.sql index 68d6bc7f1..c85d6b567 100644 --- a/script/sql/update/update_5.3.1-5.4.0.sql +++ b/script/sql/update/update_5.3.1-5.4.0.sql @@ -11,3 +11,11 @@ ALTER TABLE `sys_social` MODIFY COLUMN `access_token` varchar(2000) NOT NULL COMMENT '用户的授权令牌' AFTER `avatar`; ALTER TABLE `sys_social` MODIFY COLUMN `refresh_token` varchar(2000) DEFAULT NULL COMMENT '刷新令牌,部分平台可能没有' AFTER `expire_in`; + +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '12', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '13', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '14', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '15', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', 1, 1, 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); From 05d5d9be2cf0f34dc4505661db68a069a9935933 Mon Sep 17 00:00:00 2001 From: qxy <563660345@qq.com> Date: Thu, 22 May 2025 09:49:27 +0000 Subject: [PATCH 091/121] =?UTF-8?q?!683=20update=20=E4=BC=98=E5=8C=96=20ng?= =?UTF-8?q?inx=E4=BB=A3=E7=90=86snail-job=20websocket=E5=8F=82=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=20=E8=A7=A3=E5=86=B3=E9=83=A8=E7=BD=B2=E5=88=B0?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=90=8E=EF=BC=8C=E6=9F=A5=E7=9C=8B?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E4=BC=9A=E6=98=BE=E7=A4=BAws=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E5=A4=B1=E8=B4=A5=20*=20update=20snail-job=20websocke?= =?UTF-8?q?t=E9=85=8D=E7=BD=AE=EF=BC=8C=20=E8=A7=A3=E5=86=B3=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E5=88=B0=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=90=8E=EF=BC=8C?= =?UTF-8?q?=E6=9F=A5=E7=9C=8B=E6=97=A5=E5=BF=97=E4=BC=9A=E6=98=BE=E7=A4=BA?= =?UTF-8?q?ws=E8=BF=9E=E6=8E=A5=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/docker/nginx/conf/nginx.conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/script/docker/nginx/conf/nginx.conf b/script/docker/nginx/conf/nginx.conf index 22b074f1d..4b9b179ed 100644 --- a/script/docker/nginx/conf/nginx.conf +++ b/script/docker/nginx/conf/nginx.conf @@ -106,6 +106,12 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # sse 与 websocket参数 + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_buffering off; + proxy_cache off; proxy_pass http://snailjob-server/snail-job/; } From b726a91cdb1c296ecb7bd67e53fe0a6a5914ab5e Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Thu, 22 May 2025 17:52:23 +0800 Subject: [PATCH 092/121] =?UTF-8?q?update=20=E6=96=B0=E5=A2=9E=E5=8F=91?= =?UTF-8?q?=E5=8F=B7=E5=99=A8=E5=B7=A5=E5=85=B7=E7=B1=BB=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/redis/utils/SequenceUtils.java | 345 +++++++++--------- 1 file changed, 180 insertions(+), 165 deletions(-) diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java index e28c84e89..657dbbc07 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java @@ -1,165 +1,180 @@ -package org.dromara.common.redis.utils; - -import cn.hutool.core.date.DatePattern; -import cn.hutool.core.date.DateUtil; -import org.dromara.common.core.utils.SpringUtils; -import org.dromara.common.core.utils.StringUtils; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.redisson.api.RIdGenerator; -import org.redisson.api.RedissonClient; - -import java.time.Duration; - -/** - * 发号器工具类 - * - * @author 秋辞未寒 - * @date 2024-12-10 - */ -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class SequenceUtils { - - /** - * 默认初始值 - */ - public static final Long DEFAULT_INIT_VALUE = 1L; - /** - * 默认步长 - */ - public static final Long DEFAULT_STEP_VALUE = 1L; - /** - * 默认过期时间-天 - */ - public static final Duration DEFAULT_EXPIRE_TIME_DAY = Duration.ofDays(1); - /** - * 默认过期时间-分钟 - */ - public static final Duration DEFAULT_EXPIRE_TIME_MINUTE = Duration.ofMinutes(1); - - /** - * 获取Redisson客户端实例 - */ - private static final RedissonClient REDISSON_CLIENT = SpringUtils.getBean(RedissonClient.class); - - /** - * 获取ID生成器 - * - * @param key 业务key - * @param expireTime 过期时间 - * @param initValue ID初始值 - * @param stepValue ID步长 - * @return ID生成器 - */ - private static RIdGenerator getIdGenerator(String key, Duration expireTime, Long initValue, Long stepValue) { - if (initValue == null || initValue <= 0) { - initValue = DEFAULT_INIT_VALUE; - } - if (stepValue == null || stepValue <= 0) { - stepValue = DEFAULT_STEP_VALUE; - } - RIdGenerator idGenerator = REDISSON_CLIENT.getIdGenerator(key); - // 设置初始值和步长 - idGenerator.tryInit(initValue, stepValue); - // 设置过期时间 - idGenerator.expire(expireTime); - return idGenerator; - } - - /** - * 获取指定业务key的唯一id - * - * @param key 业务key - * @param expireTime 过期时间 - * @param initValue ID初始值 - * @param stepValue ID步长 - * @return 唯一id - */ - public static long nextId(String key, Duration expireTime, Long initValue, Long stepValue) { - return getIdGenerator(key, expireTime, initValue, stepValue).nextId(); - } - - /** - * 获取指定业务key的唯一id字符串 - * - * @param key 业务key - * @param expireTime 过期时间 - * @param initValue ID初始值 - * @param stepValue ID步长 - * @return 唯一id - */ - public static String nextIdStr(String key, Duration expireTime, Long initValue, Long stepValue) { - return String.valueOf(nextId(key, expireTime, initValue, stepValue)); - } - - /** - * 获取指定业务key的唯一id (ID初始值=1,ID步长=1) - * - * @param key 业务key - * @param expireTime 过期时间 - * @return 唯一id - */ - public static long nextId(String key, Duration expireTime) { - return getIdGenerator(key, expireTime, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); - } - - /** - * 获取指定业务key的唯一id字符串 (ID初始值=1,ID步长=1) - * - * @param key 业务key - * @param expireTime 过期时间 - * @return 唯一id - */ - public static String nextIdStr(String key, Duration expireTime) { - return String.valueOf(nextId(key, expireTime)); - } - - /** - * 获取 yyyyMMdd 开头的唯一id - * - * @return 唯一id - */ - public static String nextIdDate() { - return nextIdDate(""); - } - - /** - * 获取 prefix + yyyyMMdd 开头的唯一id - * - * @param prefix 业务前缀 - * @return 唯一id - */ - public static String nextIdDate(String prefix) { - // 前缀+日期 构建 prefixKey - String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER)); - // 获取下一个id - long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_DAY, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); - // 返回完整id - return StringUtils.format("{}{}", prefixKey, nextId); - } - - /** - * 获取 yyyyMMddHHmmss 开头的唯一id - * - * @return 唯一id - */ - public static String nextIdDateTime() { - return nextIdDateTime(""); - } - - /** - * 获取 prefix + yyyyMMddHHmmss 开头的唯一id - * - * @param prefix 业务前缀 - * @return 唯一id - */ - public static String nextIdDateTime(String prefix) { - // 前缀+日期时间 构建 prefixKey - String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_FORMATTER)); - // 获取下一个id - long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_MINUTE, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); - // 返回完整id - return StringUtils.format("{}{}", prefixKey, nextId); - } - -} +package org.dromara.common.redis.utils; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; +import org.redisson.api.RIdGenerator; +import org.redisson.api.RedissonClient; + +import java.time.Duration; + +/** + * 发号器工具类 + * + * @author 秋辞未寒 + * @date 2024-12-10 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SequenceUtils { + + /** + * 默认初始值 + */ + public static final Long DEFAULT_INIT_VALUE = 1L; + + /** + * 默认步长 + */ + public static final Long DEFAULT_STEP_VALUE = 1L; + + /** + * 默认过期时间-天 + */ + public static final Duration DEFAULT_EXPIRE_TIME_DAY = Duration.ofDays(1); + + /** + * 默认过期时间-分钟 + */ + public static final Duration DEFAULT_EXPIRE_TIME_MINUTE = Duration.ofMinutes(1); + + /** + * 获取Redisson客户端实例 + */ + private static final RedissonClient REDISSON_CLIENT = SpringUtils.getBean(RedissonClient.class); + + /** + * 获取ID生成器 + * + * @param key 业务key + * @param expireTime 过期时间 + * @param initValue ID初始值 + * @param stepValue ID步长 + * @return ID生成器 + */ + private static RIdGenerator getIdGenerator(String key, Duration expireTime, Long initValue, Long stepValue) { + if (initValue == null || initValue <= 0) { + initValue = DEFAULT_INIT_VALUE; + } + if (stepValue == null || stepValue <= 0) { + stepValue = DEFAULT_STEP_VALUE; + } + RIdGenerator idGenerator = REDISSON_CLIENT.getIdGenerator(key); + // 设置初始值和步长 + idGenerator.tryInit(initValue, stepValue); + // 设置过期时间 + idGenerator.expire(expireTime); + return idGenerator; + } + + /** + * 获取指定业务key的唯一id + * + * @param key 业务key + * @param expireTime 过期时间 + * @param initValue ID初始值 + * @param stepValue ID步长 + * @return 唯一id + */ + public static long nextId(String key, Duration expireTime, Long initValue, Long stepValue) { + return getIdGenerator(key, expireTime, initValue, stepValue).nextId(); + } + + /** + * 获取指定业务key的唯一id字符串 + * + * @param key 业务key + * @param expireTime 过期时间 + * @param initValue ID初始值 + * @param stepValue ID步长 + * @return 唯一id + */ + public static String nextIdStr(String key, Duration expireTime, Long initValue, Long stepValue) { + return String.valueOf(nextId(key, expireTime, initValue, stepValue)); + } + + /** + * 获取指定业务key的唯一id (ID初始值=1,ID步长=1) + * + * @param key 业务key + * @param expireTime 过期时间 + * @return 唯一id + */ + public static long nextId(String key, Duration expireTime) { + return getIdGenerator(key, expireTime, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); + } + + /** + * 获取指定业务key的唯一id字符串 (ID初始值=1,ID步长=1) + * + * @param key 业务key + * @param expireTime 过期时间 + * @return 唯一id + */ + public static String nextIdStr(String key, Duration expireTime) { + return String.valueOf(nextId(key, expireTime)); + } + + /** + * 获取指定业务key的唯一id字符串 (ID初始值=1,ID步长=1),不足位数自动补零 + * + * @param key 业务key + * @param expireTime 过期时间 + * @param width 位数,不足左补0 + * @return 补零后的唯一id字符串 + */ + public static String nextPaddedIdStr(String key, Duration expireTime, Integer width) { + return StringUtils.leftPad(nextIdStr(key, expireTime), width, '0'); + } + + /** + * 获取 yyyyMMdd 开头的唯一id + * + * @return 唯一id + */ + public static String nextIdDate() { + return nextIdDate(""); + } + + /** + * 获取 prefix + yyyyMMdd 开头的唯一id + * + * @param prefix 业务前缀 + * @return 唯一id + */ + public static String nextIdDate(String prefix) { + // 前缀+日期 构建 prefixKey + String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER)); + // 获取下一个id + long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_DAY, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); + // 返回完整id + return StringUtils.format("{}{}", prefixKey, nextId); + } + + /** + * 获取 yyyyMMddHHmmss 开头的唯一id + * + * @return 唯一id + */ + public static String nextIdDateTime() { + return nextIdDateTime(""); + } + + /** + * 获取 prefix + yyyyMMddHHmmss 开头的唯一id + * + * @param prefix 业务前缀 + * @return 唯一id + */ + public static String nextIdDateTime(String prefix) { + // 前缀+日期时间 构建 prefixKey + String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_FORMATTER)); + // 获取下一个id + long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_MINUTE, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); + // 返回完整id + return StringUtils.format("{}{}", prefixKey, nextId); + } + +} From d729c8ecdef89fb3867bb6a26ce776efdcc8457f Mon Sep 17 00:00:00 2001 From: gssong <1742057357@qq.com> Date: Thu, 22 May 2025 20:34:25 +0800 Subject: [PATCH 093/121] =?UTF-8?q?update=20=E8=B0=83=E6=95=B4=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E7=9B=91=E5=90=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/constant/FlowConstant.java | 30 ++++++++++++------ .../listener/WorkflowGlobalListener.java | 31 ++++++++++++------- .../service/impl/FlwInstanceServiceImpl.java | 1 - .../service/impl/FlwTaskServiceImpl.java | 13 +++----- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java index 1b10eb8ea..f3290b841 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java @@ -13,21 +13,11 @@ public interface FlowConstant { */ String INITIATOR = "initiator"; - /** - * 流程实例id - */ - String PROCESS_INSTANCE_ID = "processInstanceId"; - /** * 业务id */ String BUSINESS_ID = "businessId"; - /** - * 任务id - */ - String TASK_ID = "taskId"; - /** * 委托 */ @@ -63,4 +53,24 @@ public interface FlowConstant { */ Long FLOW_CATEGORY_ID = 100L; + /** + * 是否为申请人提交常量 + */ + String SUBMIT = "submit"; + + /** + * 抄送常量 + */ + String FLOW_COPY_LIST = "flowCopyList"; + + /** + * 消息类型常量 + */ + String MESSAGE_TYPE = "messageType"; + + /** + * 消息通知常量 + */ + String MESSAGE_NOTICE = "messageNotice"; + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index 2c95f8aaa..cae4daaaa 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -1,6 +1,7 @@ package org.dromara.workflow.listener; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -14,6 +15,7 @@ import org.dromara.warm.flow.core.listener.GlobalListener; import org.dromara.warm.flow.core.listener.ListenerVariable; import org.dromara.warm.flow.orm.entity.FlowTask; import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.enums.TaskStatusEnum; import org.dromara.workflow.domain.bo.FlowCopyBo; import org.dromara.workflow.handler.FlowProcessEventHandler; @@ -37,7 +39,7 @@ import java.util.Map; @RequiredArgsConstructor public class WorkflowGlobalListener implements GlobalListener { - private final IFlwTaskService taskService; + private final IFlwTaskService flwTaskService; private final IFlwInstanceService instanceService; private final FlowProcessEventHandler flowProcessEventHandler; private final IFlwCommonService flwCommonService; @@ -51,11 +53,19 @@ public class WorkflowGlobalListener implements GlobalListener { public void create(ListenerVariable listenerVariable) { Instance instance = listenerVariable.getInstance(); Definition definition = listenerVariable.getDefinition(); + FlowParams flowParams = listenerVariable.getFlowParams(); + Map variable = flowParams.getVariable(); Task task = listenerVariable.getTask(); if (task != null) { // 判断流程状态(发布审批中事件) flowProcessEventHandler.processCreateTaskHandler(definition.getFlowCode(), instance, task.getId()); } + Boolean submit = MapUtil.getBool(variable, FlowConstant.SUBMIT); + if (submit != null && submit) { + flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, instance.getFlowStatus(), variable, true); + } + variable.remove(FlowConstant.SUBMIT); + flowParams.variable(variable); } /** @@ -126,25 +136,24 @@ public class WorkflowGlobalListener implements GlobalListener { if (StringUtils.isNotBlank(status)) { flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false); } - + Map variable = listenerVariable.getVariable(); // 只有办理或者退回的时候才执行消息通知和抄送 if (TaskStatusEnum.PASS.getStatus().equals(flowParams.getHisStatus()) || TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus())) { Task task = listenerVariable.getTask(); - Map variable = listenerVariable.getVariable(); - List flowCopyList = (List) variable.get("flowCopyList"); - List messageType = (List) variable.get("messageType"); - String notice = (String) variable.get("notice"); + List flowCopyList = (List) variable.get(FlowConstant.FLOW_COPY_LIST); + List messageType = (List) variable.get(FlowConstant.MESSAGE_TYPE); + String notice = (String) variable.get(FlowConstant.MESSAGE_NOTICE); // 添加抄送人 - taskService.setCopy(task, flowCopyList); - variable.remove("flowCopyList"); + flwTaskService.setCopy(task, flowCopyList); + variable.remove(FlowConstant.FLOW_COPY_LIST); // 消息通知 if (CollUtil.isNotEmpty(messageType)) { flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); - variable.remove("messageType"); - variable.remove("notice"); + variable.remove(FlowConstant.MESSAGE_TYPE); + variable.remove(FlowConstant.MESSAGE_NOTICE); } } } @@ -162,7 +171,7 @@ public class WorkflowGlobalListener implements GlobalListener { return flowStatus; } else { Long instanceId = instance.getId(); - List flowTasks = taskService.selectByInstId(instanceId); + List flowTasks = flwTaskService.selectByInstId(instanceId); if (CollUtil.isEmpty(flowTasks)) { String status = BusinessStatusEnum.FINISH.getStatus(); // 更新流程状态为已完成 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index d302bc217..c11d3cc2a 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -248,7 +248,6 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { BusinessStatusEnum.checkCancelStatus(instance.getFlowStatus()); FlowParams flowParams = FlowParams.build() .message(message) - .skipType(SkipType.PASS.getKey()) .flowStatus(BusinessStatusEnum.CANCEL.getStatus()) .hisStatus(BusinessStatusEnum.CANCEL.getStatus()) .handler(userIdStr) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 1457f1d71..a550df0d3 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -39,12 +39,12 @@ import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper; import org.dromara.warm.flow.orm.mapper.FlowNodeMapper; import org.dromara.warm.flow.orm.mapper.FlowTaskMapper; import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.enums.TaskAssigneeType; import org.dromara.workflow.common.enums.TaskStatusEnum; import org.dromara.workflow.domain.bo.*; import org.dromara.workflow.domain.vo.FlowHisTaskVo; import org.dromara.workflow.domain.vo.FlowTaskVo; -import org.dromara.workflow.handler.FlowProcessEventHandler; import org.dromara.workflow.handler.WorkflowPermissionHandler; import org.dromara.workflow.mapper.FlwCategoryMapper; import org.dromara.workflow.mapper.FlwTaskMapper; @@ -81,7 +81,6 @@ public class FlwTaskServiceImpl implements IFlwTaskService { private final FlowTaskMapper flowTaskMapper; private final FlowHisTaskMapper flowHisTaskMapper; private final IdentifierGenerator identifierGenerator; - private final FlowProcessEventHandler flowProcessEventHandler; private final UserService userService; private final FlwTaskMapper flwTaskMapper; private final FlwCategoryMapper flwCategoryMapper; @@ -157,11 +156,11 @@ public class FlwTaskServiceImpl implements IFlwTaskService { // 获取抄送人 List flowCopyList = completeTaskBo.getFlowCopyList(); // 设置抄送人 - completeTaskBo.getVariables().put("flowCopyList", flowCopyList); + completeTaskBo.getVariables().put(FlowConstant.FLOW_COPY_LIST, flowCopyList); // 消息类型 - completeTaskBo.getVariables().put("messageType", messageType); + completeTaskBo.getVariables().put(FlowConstant.MESSAGE_TYPE, messageType); // 消息通知 - completeTaskBo.getVariables().put("notice", notice); + completeTaskBo.getVariables().put(FlowConstant.MESSAGE_NOTICE, notice); FlowTask flowTask = flowTaskMapper.selectById(taskId); @@ -169,11 +168,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { throw new ServiceException("流程任务不存在或任务已审批!"); } Instance ins = insService.getById(flowTask.getInstanceId()); - // 获取流程定义信息 - Definition definition = defService.getById(flowTask.getDefinitionId()); // 检查流程状态是否为草稿、已撤销或已退回状态,若是则执行流程提交监听 if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) { - flowProcessEventHandler.processHandler(definition.getFlowCode(), ins, ins.getFlowStatus(), null, true); + completeTaskBo.getVariables().put(FlowConstant.SUBMIT, true); } // 设置弹窗处理人 Map assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap()); From 9fc043b105bb64ceaf3c6b34784157453d3e6397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=AF=BB=E4=BF=97?= <1983933789@qq.com> Date: Thu, 22 May 2025 13:39:05 +0000 Subject: [PATCH 094/121] =?UTF-8?q?!684=20fix:=20sql=E8=A1=A5=E5=85=A8?= =?UTF-8?q?=E5=88=86=E5=8F=B7=20*=20fix:=20sql=E8=A1=A5=E5=85=A8=E5=88=86?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/sql/update/update_5.3.1-5.4.0.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/sql/update/update_5.3.1-5.4.0.sql b/script/sql/update/update_5.3.1-5.4.0.sql index c85d6b567..51dee0b84 100644 --- a/script/sql/update/update_5.3.1-5.4.0.sql +++ b/script/sql/update/update_5.3.1-5.4.0.sql @@ -5,7 +5,7 @@ ALTER TABLE `flow_instance` MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `variable`; ALTER TABLE `flow_his_task` - MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `skip_type` + MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `skip_type`; ALTER TABLE `sys_social` MODIFY COLUMN `access_token` varchar(2000) NOT NULL COMMENT '用户的授权令牌' AFTER `avatar`; From cea4855f57ee0b6c4fc83d60fca5daa9b0ccdf32 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Fri, 23 May 2025 11:51:09 +0800 Subject: [PATCH 095/121] =?UTF-8?q?update=20=E4=BB=A3=E7=A0=81=E7=94=9F?= =?UTF-8?q?=E6=88=90ServiceImpl=E5=B1=82=E5=A2=9E=E5=8A=A0=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/vm/java/serviceImpl.java.vm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm index 48cc8b15b..be6c3bf7b 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import ${packageName}.domain.bo.${ClassName}Bo; import ${packageName}.domain.vo.${ClassName}Vo; @@ -27,6 +28,7 @@ import java.util.Collection; * @author ${author} * @date ${datetime} */ +@Slf4j @RequiredArgsConstructor @Service public class ${ClassName}ServiceImpl implements I${ClassName}Service { From 336b2e8cc3428270d6d57d62c6d0a16b1949511d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E9=93=83=E8=96=AF=E5=A4=B4?= <5601833+xlsea@user.noreply.gitee.com> Date: Fri, 23 May 2025 10:04:44 +0000 Subject: [PATCH 096/121] =?UTF-8?q?!686=20feat=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E7=BA=A7=E8=81=94=E5=88=A0=E9=99=A4=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E6=8E=A5=E5=8F=A3=20*=20feat=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E7=BA=A7=E8=81=94=E5=88=A0=E9=99=A4=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/system/SysMenuController.java | 26 ++++++++++++--- .../system/mapper/SysRoleMenuMapper.java | 14 ++++++++ .../system/service/ISysMenuService.java | 16 +++++++++ .../service/impl/SysMenuServiceImpl.java | 33 +++++++++++++++++-- 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java index a6c3e115e..3171ec390 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java @@ -51,8 +51,8 @@ public class SysMenuController extends BaseController { * 获取菜单列表 */ @SaCheckRole(value = { - TenantConstants.SUPER_ADMIN_ROLE_KEY, - TenantConstants.TENANT_ADMIN_ROLE_KEY + TenantConstants.SUPER_ADMIN_ROLE_KEY, + TenantConstants.TENANT_ADMIN_ROLE_KEY }, mode = SaMode.OR) @SaCheckPermission("system:menu:list") @GetMapping("/list") @@ -67,8 +67,8 @@ public class SysMenuController extends BaseController { * @param menuId 菜单ID */ @SaCheckRole(value = { - TenantConstants.SUPER_ADMIN_ROLE_KEY, - TenantConstants.TENANT_ADMIN_ROLE_KEY + TenantConstants.SUPER_ADMIN_ROLE_KEY, + TenantConstants.TENANT_ADMIN_ROLE_KEY }, mode = SaMode.OR) @SaCheckPermission("system:menu:query") @GetMapping(value = "/{menuId}") @@ -173,4 +173,22 @@ public class SysMenuController extends BaseController { public record MenuTreeSelectVo(List checkedKeys, List> menus) { } + /** + * 批量级联删除菜单 + * + * @param menuIds 菜单ID串 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:menu:remove") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/cascade/{menuIds}") + public R remove(@PathVariable("menuIds") Long[] menuIds) { + List menuIdList = List.of(menuIds); + if (menuService.hasChildByMenuId(menuIdList)) { + return R.warn("存在子菜单,不允许删除"); + } + menuService.deleteMenuById(menuIdList); + return R.ok(); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java index 0a657b42d..d754303d3 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java @@ -1,8 +1,11 @@ package org.dromara.system.mapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.system.domain.SysRoleMenu; +import java.util.List; + /** * 角色与菜单关联表 数据层 * @@ -10,4 +13,15 @@ import org.dromara.system.domain.SysRoleMenu; */ public interface SysRoleMenuMapper extends BaseMapperPlus { + /** + * 根据菜单ID串删除关联关系 + * + * @return 结果 + */ + default int deleteByMenuIds(List menuIds) { + LambdaUpdateWrapper lqw = new LambdaUpdateWrapper() + .in(SysRoleMenu::getMenuId, menuIds); + return this.delete(lqw); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java index 72d705e9b..f972691be 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java @@ -105,6 +105,14 @@ public interface ISysMenuService { */ boolean hasChildByMenuId(Long menuId); + /** + * 是否存在菜单子节点 + * + * @param menuIds 菜单ID串 + * @return 结果 true 存在 false 不存在 + */ + boolean hasChildByMenuId(List menuIds); + /** * 查询菜单是否存在角色 * @@ -137,6 +145,14 @@ public interface ISysMenuService { */ int deleteMenuById(Long menuId); + /** + * 批量删除菜单管理信息 + * + * @param menuIds 菜单ID串 + * @return 结果 + */ + void deleteMenuById(List menuIds); + /** * 校验菜单名称是否唯一 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java index 19048abc4..692b3ce00 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java @@ -28,6 +28,7 @@ import org.dromara.system.mapper.SysRoleMenuMapper; import org.dromara.system.mapper.SysTenantPackageMapper; import org.dromara.system.service.ISysMenuService; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.*; @@ -173,11 +174,15 @@ public class SysMenuServiceImpl implements ISysMenuService { if (tenantPackage.getMenuCheckStrictly()) { parentIds = baseMapper.selectObjs(new LambdaQueryWrapper() .select(SysMenu::getParentId) - .in(SysMenu::getMenuId, menuIds), x -> {return Convert.toLong(x);}); + .in(SysMenu::getMenuId, menuIds), x -> { + return Convert.toLong(x); + }); } return baseMapper.selectObjs(new LambdaQueryWrapper() .in(SysMenu::getMenuId, menuIds) - .notIn(CollUtil.isNotEmpty(parentIds), SysMenu::getMenuId, parentIds), x -> {return Convert.toLong(x);}); + .notIn(CollUtil.isNotEmpty(parentIds), SysMenu::getMenuId, parentIds), x -> { + return Convert.toLong(x); + }); } /** @@ -278,6 +283,17 @@ public class SysMenuServiceImpl implements ISysMenuService { return baseMapper.exists(new LambdaQueryWrapper().eq(SysMenu::getParentId, menuId)); } + /** + * 是否存在菜单子节点 + * + * @param menuIds 菜单ID串 + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(List menuIds) { + return baseMapper.exists(new LambdaQueryWrapper().in(SysMenu::getParentId, menuIds).notIn(SysMenu::getMenuId, menuIds)); + } + /** * 查询菜单使用数量 * @@ -324,6 +340,19 @@ public class SysMenuServiceImpl implements ISysMenuService { return baseMapper.deleteById(menuId); } + /** + * 批量删除菜单管理信息 + * + * @param menuIds 菜单ID串 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteMenuById(List menuIds) { + baseMapper.deleteByIds(menuIds); + roleMenuMapper.deleteByMenuIds(menuIds); + } + /** * 校验菜单名称是否唯一 * From a0831dda45528c7c5cce57431defb6b6de96a795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 23 May 2025 10:07:25 +0000 Subject: [PATCH 097/121] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20=E8=AF=B7?= =?UTF-8?q?=E5=81=87=E8=A1=A8=E5=8D=95=E8=8F=9C=E5=8D=95sql=20=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E7=8A=B6=E6=80=81=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 疯狂的狮子Li <15040126243@163.com> --- script/sql/oracle/oracle_ry_workflow.sql | 2 +- script/sql/postgres/postgres_ry_workflow.sql | 2 +- script/sql/ry_workflow.sql | 2 +- script/sql/sqlserver/sqlserver_ry_workflow.sql | 2 +- script/sql/update/oracle/update_5.3.1-5.4.0.sql | 2 +- script/sql/update/postgres/update_5.3.1-5.4.0.sql | 2 +- script/sql/update/sqlserver/update_5.3.1-5.4.0.sql | 2 +- script/sql/update/update_5.3.1-5.4.0.sql | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/script/sql/oracle/oracle_ry_workflow.sql b/script/sql/oracle/oracle_ry_workflow.sql index 58453206d..4c6f80e74 100644 --- a/script/sql/oracle/oracle_ry_workflow.sql +++ b/script/sql/oracle/oracle_ry_workflow.sql @@ -388,7 +388,7 @@ INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument' INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); -INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11623', '流程分类查询', '11622', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:query', '#', 103, 1, SYSDATE, NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11624', '流程分类新增', '11622', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:add', '#', 103, 1, SYSDATE, NULL, NULL, ''); diff --git a/script/sql/postgres/postgres_ry_workflow.sql b/script/sql/postgres/postgres_ry_workflow.sql index 1b3b58b1c..8db29c704 100644 --- a/script/sql/postgres/postgres_ry_workflow.sql +++ b/script/sql/postgres/postgres_ry_workflow.sql @@ -371,7 +371,7 @@ INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument' INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); -INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11623', '流程分类查询', '11622', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:query', '#', 103, 1, now(), NULL, NULL, ''); INSERT INTO sys_menu VALUES ('11624', '流程分类新增', '11622', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:add', '#', 103, 1, now(), NULL, NULL, ''); diff --git a/script/sql/ry_workflow.sql b/script/sql/ry_workflow.sql index d0946181f..5e055d2ed 100644 --- a/script/sql/ry_workflow.sql +++ b/script/sql/ry_workflow.sql @@ -215,7 +215,7 @@ insert into sys_menu values ('11629', '我发起的', '11618', '1', 'myDocument' insert into sys_menu values ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, sysdate(), NULL, NULL, ''); insert into sys_menu values ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate(), NULL, NULL, ''); insert into sys_menu values ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); -insert into sys_menu values ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', 1, 1, 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); -- 流程分类管理相关按钮 insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1,sysdate(), null, null, ''); insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add', '#', 103, 1,sysdate(), null, null, ''); diff --git a/script/sql/sqlserver/sqlserver_ry_workflow.sql b/script/sql/sqlserver/sqlserver_ry_workflow.sql index 34c0d04e3..88d92fcb4 100644 --- a/script/sql/sqlserver/sqlserver_ry_workflow.sql +++ b/script/sql/sqlserver/sqlserver_ry_workflow.sql @@ -1274,7 +1274,7 @@ INSERT sys_menu VALUES (11631, N'待办任务', 11630, 2, N'allTaskWaiting', N'w GO INSERT sys_menu VALUES (11700, N'流程设计', 11616, 5, N'design/index', N'workflow/processDefinition/design', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); GO -INSERT sys_menu VALUES (11701, N'请假申请', 11616, 6, N'leaveEdit/index', N'workflow/leave/leaveEdit', N'', 1, 1, N'C', N'0', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +INSERT sys_menu VALUES (11701, N'请假申请', 11616, 6, N'leaveEdit/index', N'workflow/leave/leaveEdit', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); GO -- 流程分类管理相关按钮 diff --git a/script/sql/update/oracle/update_5.3.1-5.4.0.sql b/script/sql/update/oracle/update_5.3.1-5.4.0.sql index 1bf6c1cc1..533778cde 100644 --- a/script/sql/update/oracle/update_5.3.1-5.4.0.sql +++ b/script/sql/update/oracle/update_5.3.1-5.4.0.sql @@ -15,4 +15,4 @@ INSERT INTO sys_menu VALUES ('131', '分配角色', '1', '13', 'user-auth/ INSERT INTO sys_menu VALUES ('132', '字典数据', '1', '14', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate, null, null, ''); INSERT INTO sys_menu VALUES ('133', '文件配置管理', '1', '15', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate, null, null, ''); INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); -INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); diff --git a/script/sql/update/postgres/update_5.3.1-5.4.0.sql b/script/sql/update/postgres/update_5.3.1-5.4.0.sql index 8fb7915ed..108494f6d 100644 --- a/script/sql/update/postgres/update_5.3.1-5.4.0.sql +++ b/script/sql/update/postgres/update_5.3.1-5.4.0.sql @@ -15,4 +15,4 @@ INSERT INTO sys_menu VALUES ('131', '分配角色', '1', '13', 'user-auth/ INSERT INTO sys_menu VALUES ('132', '字典数据', '1', '14', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', '1', '1', 'C', '1', '0', 'system:dict:list', '#', 103, 1, now(), null, null, ''); INSERT INTO sys_menu VALUES ('133', '文件配置管理', '1', '15', 'oss-config/index', 'system/oss/config', '', '1', '1', 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, now(), null, null, ''); INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); -INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); diff --git a/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql index 0c1f0fcea..00f2e8e5e 100644 --- a/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql +++ b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql @@ -59,5 +59,5 @@ INSERT sys_menu VALUES (133, N'文件配置管理', 1, 15, N'oss-config/index GO INSERT sys_menu VALUES (11700, N'流程设计', 11616, 5, N'design/index', N'workflow/processDefinition/design', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); GO -INSERT sys_menu VALUES (11701, N'请假申请', 11616, 6, N'leaveEdit/index', N'workflow/leave/leaveEdit', N'', 1, 1, N'C', N'0', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +INSERT sys_menu VALUES (11701, N'请假申请', 11616, 6, N'leaveEdit/index', N'workflow/leave/leaveEdit', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); GO diff --git a/script/sql/update/update_5.3.1-5.4.0.sql b/script/sql/update/update_5.3.1-5.4.0.sql index 51dee0b84..ebcad665f 100644 --- a/script/sql/update/update_5.3.1-5.4.0.sql +++ b/script/sql/update/update_5.3.1-5.4.0.sql @@ -18,4 +18,4 @@ insert into sys_menu values('131', '分配角色', '1', '13', 'user-auth/r insert into sys_menu values('132', '字典数据', '1', '14', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate(), null, null, ''); insert into sys_menu values('133', '文件配置管理', '1', '15', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate(), null, null, ''); insert into sys_menu values('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); -insert into sys_menu values('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', 1, 1, 'C', '0', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); From 8c603ff8d711d8003b04cae1e0a0ad0a3df5c5a9 Mon Sep 17 00:00:00 2001 From: gssong <1742057357@qq.com> Date: Sat, 24 May 2025 00:16:27 +0800 Subject: [PATCH 098/121] =?UTF-8?q?update=20=E8=B0=83=E6=95=B4=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=9B=91=E5=90=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listener/WorkflowGlobalListener.java | 54 +++++++++++-------- .../service/impl/FlwTaskServiceImpl.java | 2 - 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index cae4daaaa..3a20ec0cd 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -7,12 +7,14 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.utils.StringUtils; +import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.entity.Definition; import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.core.listener.GlobalListener; import org.dromara.warm.flow.core.listener.ListenerVariable; +import org.dromara.warm.flow.core.service.InsService; import org.dromara.warm.flow.orm.entity.FlowTask; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.constant.FlowConstant; @@ -43,6 +45,7 @@ public class WorkflowGlobalListener implements GlobalListener { private final IFlwInstanceService instanceService; private final FlowProcessEventHandler flowProcessEventHandler; private final IFlwCommonService flwCommonService; + private final InsService insService; /** * 创建监听器,任务创建时执行 @@ -53,19 +56,11 @@ public class WorkflowGlobalListener implements GlobalListener { public void create(ListenerVariable listenerVariable) { Instance instance = listenerVariable.getInstance(); Definition definition = listenerVariable.getDefinition(); - FlowParams flowParams = listenerVariable.getFlowParams(); - Map variable = flowParams.getVariable(); Task task = listenerVariable.getTask(); if (task != null) { // 判断流程状态(发布审批中事件) flowProcessEventHandler.processCreateTaskHandler(definition.getFlowCode(), instance, task.getId()); } - Boolean submit = MapUtil.getBool(variable, FlowConstant.SUBMIT); - if (submit != null && submit) { - flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, instance.getFlowStatus(), variable, true); - } - variable.remove(FlowConstant.SUBMIT); - flowParams.variable(variable); } /** @@ -110,6 +105,13 @@ public class WorkflowGlobalListener implements GlobalListener { flowTask.setPermissionList(List.of(instance.getCreateBy())); } } + //申请人提交事件 + Boolean submit = MapUtil.getBool(variable, FlowConstant.SUBMIT); + if (submit != null && submit) { + flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, instance.getFlowStatus(), variable, true); + } + variable.remove(FlowConstant.SUBMIT); + flowParams.variable(variable); } /** @@ -136,24 +138,32 @@ public class WorkflowGlobalListener implements GlobalListener { if (StringUtils.isNotBlank(status)) { flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false); } - Map variable = listenerVariable.getVariable(); + + Map variable = flowParams.getVariable(); // 只有办理或者退回的时候才执行消息通知和抄送 if (TaskStatusEnum.PASS.getStatus().equals(flowParams.getHisStatus()) || TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus())) { Task task = listenerVariable.getTask(); - List flowCopyList = (List) variable.get(FlowConstant.FLOW_COPY_LIST); - List messageType = (List) variable.get(FlowConstant.MESSAGE_TYPE); - String notice = (String) variable.get(FlowConstant.MESSAGE_NOTICE); - - // 添加抄送人 - flwTaskService.setCopy(task, flowCopyList); - variable.remove(FlowConstant.FLOW_COPY_LIST); - - // 消息通知 - if (CollUtil.isNotEmpty(messageType)) { - flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); - variable.remove(FlowConstant.MESSAGE_TYPE); - variable.remove(FlowConstant.MESSAGE_NOTICE); + if (variable != null) { + if (variable.containsKey(FlowConstant.FLOW_COPY_LIST)) { + List flowCopyList = (List) variable.get(FlowConstant.FLOW_COPY_LIST); + // 添加抄送人 + flwTaskService.setCopy(task, flowCopyList); + } + if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) { + List messageType = (List) variable.get(FlowConstant.MESSAGE_TYPE); + String notice = (String) variable.get(FlowConstant.MESSAGE_NOTICE); + // 消息通知 + if (CollUtil.isNotEmpty(messageType)) { + flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); + } + } + Map variableMap = instance.getVariableMap(); + variableMap.remove(FlowConstant.FLOW_COPY_LIST); + variableMap.remove(FlowConstant.MESSAGE_TYPE); + variableMap.remove(FlowConstant.MESSAGE_NOTICE); + instance.setVariable(FlowEngine.jsonConvert.objToStr(variableMap)); + insService.updateById(instance); } } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index a550df0d3..a298b0eb9 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -393,8 +393,6 @@ public class FlwTaskServiceImpl implements IFlwTaskService { String applyNodeCode = flwCommonService.applyNodeCode(definitionId); Map variable = new HashMap<>(); - // 设置抄送人 - variable.put("flowCopyList", bo.getMessageType()); // 消息类型 variable.put("messageType", messageType); // 消息通知 From 887d5e85d012c5eb336d0beef721e5cf52205fb9 Mon Sep 17 00:00:00 2001 From: gssong <1742057357@qq.com> Date: Sun, 25 May 2025 11:48:01 +0800 Subject: [PATCH 099/121] =?UTF-8?q?add=20=E5=A2=9E=E5=8A=A0logicflow?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=9B=BE=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflow/service/impl/FlwInstanceServiceImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index c11d3cc2a..de4d7d5de 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -20,12 +20,12 @@ import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.warm.flow.core.constant.ExceptionCons; +import org.dromara.warm.flow.core.dto.DefChart; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.entity.Definition; import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Task; import org.dromara.warm.flow.core.enums.NodeType; -import org.dromara.warm.flow.core.enums.SkipType; import org.dromara.warm.flow.core.service.ChartService; import org.dromara.warm.flow.core.service.DefService; import org.dromara.warm.flow.core.service.InsService; @@ -317,7 +317,8 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { list.addAll(BeanUtil.copyToList(flowHisTasks, FlowHisTaskVo.class)); } String flowChart = chartService.chartIns(instanceId); - return Map.of("list", list, "image", flowChart); + DefChart defChart = chartService.chartInsObj(instanceId); + return Map.of("list", list, "image", flowChart,"defChart",defChart); } /** From ffc971cf92a4d12af597b3b5352158f30ef35816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 26 May 2025 10:07:08 +0800 Subject: [PATCH 100/121] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20flowParams?= =?UTF-8?q?=20=E4=B8=BAnull=E5=AF=BC=E8=87=B4=E7=9A=84=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/workflow/listener/WorkflowGlobalListener.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index 3a20ec0cd..4765d8216 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -139,6 +139,9 @@ public class WorkflowGlobalListener implements GlobalListener { flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false); } + if (ObjectUtil.isNull(flowParams)) { + return; + } Map variable = flowParams.getVariable(); // 只有办理或者退回的时候才执行消息通知和抄送 if (TaskStatusEnum.PASS.getStatus().equals(flowParams.getHisStatus()) From 6b387b2456f5c4045169b26c3f9affc63c1d3a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 26 May 2025 11:56:05 +0800 Subject: [PATCH 101/121] update springboot 3.4.5 => 3.4.6 update springdoc 2.8.5 => 2.8.8 update mybatis-plus 3.5.11 => 3.5.12 update springboot-admin 3.4.5 => 3.4.7 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index cdfd7bc56..950620382 100644 --- a/pom.xml +++ b/pom.xml @@ -14,20 +14,20 @@ 5.3.1 - 3.4.5 + 3.4.6 UTF-8 UTF-8 17 3.5.16 - 2.8.5 + 2.8.8 0.15.0 1.2.0 2.3 1.42.0 - 3.5.11 + 3.5.12 3.9.1 5.8.35 - 3.4.5 + 3.4.7 3.45.1 2.2.7 4.3.1 From 2b89c3f8d0b119f15b7a145b42dab55790c79e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 26 May 2025 12:17:33 +0800 Subject: [PATCH 102/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E8=A1=A8?= =?UTF-8?q?=E6=A0=BC=E5=A2=9E=E5=8A=A0border?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm | 1 + .../ruoyi-generator/src/main/resources/vm/vue/index.vue.vm | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm index caf3472e1..4570d46db 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -80,6 +80,7 @@ v-loading="loading" :data="${businessName}List" row-key="${treeCode}" + border :default-expand-all="isExpandAll" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" > diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index a92d19adc..df51ed063 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -82,7 +82,7 @@ - + #foreach($column in $columns) #set($javaField=$column.javaField) From 1752695751813a58d6142768f6d2ee75d9a9ea8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 26 May 2025 14:43:05 +0800 Subject: [PATCH 103/121] update mapstruct-plus 1.4.6 => 1.4.8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 950620382..b460f737a 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ 2.2.7 4.3.1 1.5.0 - 1.4.6 + 1.4.8 0.2.0 1.18.36 1.80 From 4e3fc7002da7122871f2e4350fda4674f4728c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 26 May 2025 14:43:37 +0800 Subject: [PATCH 104/121] =?UTF-8?q?update=20minio=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=88=B0=E6=9C=80=E6=96=B0=20RELEASE.2025-05-24T17-08-30Z?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml index 82548e4db..5fa579c15 100644 --- a/script/docker/docker-compose.yml +++ b/script/docker/docker-compose.yml @@ -65,7 +65,7 @@ services: network_mode: "host" minio: - image: minio/minio:RELEASE.2023-04-13T03-08-07Z + image: minio/minio:RELEASE.2025-05-24T17-08-30Z container_name: minio ports: # api 端口 From a7ea096319043bd180981b279f621ab6d0250abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 26 May 2025 15:59:04 +0800 Subject: [PATCH 105/121] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E9=80=9A=E8=BF=87loginId=E6=9F=A5=E8=AF=A2=E8=A7=92?= =?UTF-8?q?=E8=89=B2=E5=92=8C=E8=8F=9C=E5=8D=95=E6=9D=83=E9=99=90=20?= =?UTF-8?q?=E8=80=8C=E9=9D=9E=E5=BD=93=E5=89=8D=E7=94=A8=E6=88=B7=E6=97=B6?= =?UTF-8?q?=20=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/service/PermissionService.java | 28 ++++++++++++++++++ .../core/service/SaPermissionImpl.java | 29 ++++++++++++++----- .../impl/SysPermissionServiceImpl.java | 5 ++-- 3 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PermissionService.java diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PermissionService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PermissionService.java new file mode 100644 index 000000000..d7db79a91 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PermissionService.java @@ -0,0 +1,28 @@ +package org.dromara.common.core.service; + +import java.util.Set; + +/** + * 用户权限处理 + * + * @author Lion Li + */ +public interface PermissionService { + + /** + * 获取角色数据权限 + * + * @param userId 用户id + * @return 角色权限信息 + */ + Set getRolePermission(Long userId); + + /** + * 获取菜单数据权限 + * + * @param userId 用户id + * @return 菜单权限信息 + */ + Set getMenuPermission(Long userId); + +} diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java index 1cef9a773..80e2d4403 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java @@ -1,9 +1,13 @@ package org.dromara.common.satoken.core.service; import cn.dev33.satoken.stp.StpInterface; +import cn.hutool.core.util.ObjectUtil; import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.enums.UserType; +import org.dromara.common.core.service.PermissionService; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; @@ -15,19 +19,25 @@ import java.util.List; */ public class SaPermissionImpl implements StpInterface { + @Autowired + private PermissionService permissionService; + /** * 获取菜单权限列表 */ @Override public List getPermissionList(Object loginId, String loginType) { LoginUser loginUser = LoginHelper.getLoginUser(); + if (ObjectUtil.isNull(loginUser)) { + List list = StringUtils.splitList(loginId.toString(), ":"); + return new ArrayList<>(permissionService.getMenuPermission(Long.parseLong(list.get(1)))); + } UserType userType = UserType.getUserType(loginUser.getUserType()); - if (userType == UserType.SYS_USER) { - return new ArrayList<>(loginUser.getMenuPermission()); - } else if (userType == UserType.APP_USER) { + if (userType == UserType.APP_USER) { // 其他端 自行根据业务编写 } - return new ArrayList<>(); + // SYS_USER 默认返回权限 + return new ArrayList<>(loginUser.getMenuPermission()); } /** @@ -36,12 +46,15 @@ public class SaPermissionImpl implements StpInterface { @Override public List getRoleList(Object loginId, String loginType) { LoginUser loginUser = LoginHelper.getLoginUser(); + if (ObjectUtil.isNull(loginUser)) { + List list = StringUtils.splitList(loginId.toString(), ":"); + return new ArrayList<>(permissionService.getRolePermission(Long.parseLong(list.get(1)))); + } UserType userType = UserType.getUserType(loginUser.getUserType()); - if (userType == UserType.SYS_USER) { - return new ArrayList<>(loginUser.getRolePermission()); - } else if (userType == UserType.APP_USER) { + if (userType == UserType.APP_USER) { // 其他端 自行根据业务编写 } - return new ArrayList<>(); + // SYS_USER 默认返回权限 + return new ArrayList<>(loginUser.getRolePermission()); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java index 9852821e6..4bf697420 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java @@ -1,11 +1,12 @@ package org.dromara.system.service.impl; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.TenantConstants; +import org.dromara.common.core.service.PermissionService; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.service.ISysMenuService; import org.dromara.system.service.ISysPermissionService; import org.dromara.system.service.ISysRoleService; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.HashSet; @@ -18,7 +19,7 @@ import java.util.Set; */ @RequiredArgsConstructor @Service -public class SysPermissionServiceImpl implements ISysPermissionService { +public class SysPermissionServiceImpl implements ISysPermissionService, PermissionService { private final ISysRoleService roleService; private final ISysMenuService menuService; From d1889c42a3a06e130908c7e2e615f00826a3dfde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 26 May 2025 16:31:10 +0800 Subject: [PATCH 106/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E6=9D=83?= =?UTF-8?q?=E9=99=90=E8=8E=B7=E5=8F=96=20=E5=A2=9E=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=99=BB=E5=BD=95=E4=BA=86=E4=BD=86=E6=98=AF=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E7=9A=84loginId=E6=98=AF=E5=88=AB=E4=BA=BA=E7=9A=84?= =?UTF-8?q?=E5=9C=BA=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/common/satoken/core/service/SaPermissionImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java index 80e2d4403..35d16cd6e 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java @@ -28,7 +28,7 @@ public class SaPermissionImpl implements StpInterface { @Override public List getPermissionList(Object loginId, String loginType) { LoginUser loginUser = LoginHelper.getLoginUser(); - if (ObjectUtil.isNull(loginUser)) { + if (ObjectUtil.isNull(loginUser) || !loginUser.getLoginId().equals(loginId)) { List list = StringUtils.splitList(loginId.toString(), ":"); return new ArrayList<>(permissionService.getMenuPermission(Long.parseLong(list.get(1)))); } @@ -46,7 +46,7 @@ public class SaPermissionImpl implements StpInterface { @Override public List getRoleList(Object loginId, String loginType) { LoginUser loginUser = LoginHelper.getLoginUser(); - if (ObjectUtil.isNull(loginUser)) { + if (ObjectUtil.isNull(loginUser) || !loginUser.getLoginId().equals(loginId)) { List list = StringUtils.splitList(loginId.toString(), ":"); return new ArrayList<>(permissionService.getRolePermission(Long.parseLong(list.get(1)))); } From 79ec850eca4d6e02551a67acbc8359a3d4075876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=93=E5=8D=8E?= <290631660@qq.com> Date: Mon, 26 May 2025 12:50:03 +0000 Subject: [PATCH 107/121] =?UTF-8?q?!689=20update=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E9=80=80=E5=9B=9E=E4=BB=BB=E5=8A=A1bo=E5=85=B3=E4=BA=8E?= =?UTF-8?q?=E9=A9=B3=E5=9B=9E=E7=9A=84=E8=8A=82=E7=82=B9=E7=9A=84=E9=9D=9E?= =?UTF-8?q?=E7=A9=BA=E6=A0=A1=E9=AA=8C=20*=20update=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E9=80=80=E5=9B=9E=E4=BB=BB=E5=8A=A1bo=E5=85=B3=E4=BA=8E?= =?UTF-8?q?=E9=A9=B3=E5=9B=9E=E7=9A=84=E8=8A=82=E7=82=B9=E7=9A=84=E9=9D=9E?= =?UTF-8?q?=E7=A9=BA=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/workflow/domain/bo/BackProcessBo.java | 1 - .../service/impl/FlwInstanceServiceImpl.java | 15 ++++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java index 3117a33f5..654bf0800 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java @@ -43,7 +43,6 @@ public class BackProcessBo implements Serializable { /** * 驳回的节点id(目前未使用,直接驳回到申请人) */ - @NotBlank(message = "驳回的节点不能为空", groups = AddGroup.class) private String nodeCode; /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index de4d7d5de..40ac21c0d 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -422,15 +422,12 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { if (instance != null) { BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus()); } - List flowTaskList = flwTaskService.selectByInstId(bo.getId()); - for (FlowTask flowTask : flowTaskList) { - FlowParams flowParams = FlowParams.build() - .message(bo.getComment()) - .flowStatus(BusinessStatusEnum.INVALID.getStatus()) - .hisStatus(TaskStatusEnum.INVALID.getStatus()) - .ignore(true); - taskService.termination(flowTask.getId(), flowParams); - } + FlowParams flowParams = FlowParams.build() + .message(bo.getComment()) + .flowStatus(BusinessStatusEnum.INVALID.getStatus()) + .hisStatus(TaskStatusEnum.INVALID.getStatus()) + .ignore(true); + taskService.terminationByInsId(bo.getId(), flowParams); return true; } catch (Exception e) { log.error(e.getMessage(), e); From a002a4e7a1e1622481a4ade1033c210279a11a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=93=E5=8D=8E?= <290631660@qq.com> Date: Tue, 27 May 2025 09:02:05 +0000 Subject: [PATCH 108/121] =?UTF-8?q?!690=20=E6=96=B0=E5=A2=9E=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E5=89=8D=E7=AB=AF=E6=98=BE=E7=A4=BA=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E5=9B=BE=E6=96=B9=E5=BC=8F=E5=92=8C=E6=96=B0=E5=A2=9E=E5=8A=9E?= =?UTF-8?q?=E7=90=86=E4=BA=BA=E8=BD=AC=E6=8D=A2=E6=8E=A5=E5=8F=A3=20*=20fe?= =?UTF-8?q?at=20=E6=96=B0=E5=A2=9E=E9=80=9A=E8=BF=87=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E6=B5=81=E7=A8=8B=E5=9B=BE=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../handler/WorkflowPermissionHandler.java | 19 ++++++++++++++++--- .../listener/WorkflowGlobalListener.java | 7 ------- .../service/impl/FlwInstanceServiceImpl.java | 4 +--- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index b460f737a..95b550e70 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ 8.7.2-20250101 - 1.7.2 + 1.7.3-m1 3.2.2 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java index 53dbd20ed..f9ede15ce 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java @@ -1,12 +1,13 @@ package org.dromara.workflow.handler; +import cn.hutool.core.collection.CollUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.handler.PermissionHandler; -import org.dromara.warm.flow.core.service.impl.TaskServiceImpl; import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.service.IFlwCommonService; import org.springframework.stereotype.Component; import java.util.Collections; @@ -23,9 +24,11 @@ import java.util.List; @Slf4j public class WorkflowPermissionHandler implements PermissionHandler { + private final IFlwCommonService flwCommonService; + /** - * 审批前获取当前办理人,办理时会校验的该权限集合 - * 后续在{@link TaskServiceImpl#checkAuth(Task, FlowParams)} 中调用 + * 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务 + * 后续在{@link FlowParams#getPermissionFlag} 中获取 * 返回当前用户权限集合 */ @Override @@ -43,4 +46,14 @@ public class WorkflowPermissionHandler implements PermissionHandler { return LoginHelper.getUserIdStr(); } + /** + * 转换办理人,比如设计器中预设了能办理的人,如果其中包含角色或者部门id等,可以通过此接口进行转换成用户id + */ + @Override + public List convertPermissions(List permissions) { + if (CollUtil.isNotEmpty(permissions)) { + permissions = flwCommonService.buildUser(permissions); + } + return permissions; + } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index 4765d8216..a5b5d4d1d 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -92,13 +92,6 @@ public class WorkflowGlobalListener implements GlobalListener { String userIds = variable.get(flowTask.getNodeCode()).toString(); flowTask.setPermissionList(List.of(userIds.split(StringUtils.SEPARATOR))); variable.remove(flowTask.getNodeCode()); - } else { - // 否则把所有的角色或者部门转成对应的用户 - List permissionList = flowTask.getPermissionList(); - if (CollUtil.isNotEmpty(permissionList)) { - List newUserList = flwCommonService.buildUser(permissionList); - flowTask.setPermissionList(newUserList); - } } // 如果是申请节点,则把启动人添加到办理人 if (flowTask.getNodeCode().equals(applyNodeCode)) { diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index 40ac21c0d..dcf123689 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -316,9 +316,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { if (CollUtil.isNotEmpty(flowHisTasks)) { list.addAll(BeanUtil.copyToList(flowHisTasks, FlowHisTaskVo.class)); } - String flowChart = chartService.chartIns(instanceId); - DefChart defChart = chartService.chartInsObj(instanceId); - return Map.of("list", list, "image", flowChart,"defChart",defChart); + return Map.of("list", list); } /** From a776d282941412f3c5e1ee4c63c5fd6a5a383e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Tue, 27 May 2025 17:13:32 +0800 Subject: [PATCH 109/121] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=B7=A5=E4=BD=9C=E6=B5=81=E5=AD=97=E4=BD=93=E6=96=87?= =?UTF-8?q?=E4=BB=B6(=E4=B8=8D=E9=9C=80=E8=A6=81=E4=BA=86=20=E6=94=B9?= =?UTF-8?q?=E6=88=90=E5=89=8D=E7=AB=AF=E6=B8=B2=E6=9F=93=E4=BA=86)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-admin/Dockerfile | 2 -- ruoyi-admin/zhFonts/.uuid | 1 - ruoyi-admin/zhFonts/SIMSUN.TTC | Bin 18008680 -> 0 bytes ruoyi-admin/zhFonts/fonts.dir | 4 ---- ruoyi-admin/zhFonts/fonts.scale | 4 ---- 5 files changed, 11 deletions(-) delete mode 100644 ruoyi-admin/zhFonts/.uuid delete mode 100644 ruoyi-admin/zhFonts/SIMSUN.TTC delete mode 100644 ruoyi-admin/zhFonts/fonts.dir delete mode 100644 ruoyi-admin/zhFonts/fonts.scale diff --git a/ruoyi-admin/Dockerfile b/ruoyi-admin/Dockerfile index a3a66756f..278d67bb4 100644 --- a/ruoyi-admin/Dockerfile +++ b/ruoyi-admin/Dockerfile @@ -18,8 +18,6 @@ EXPOSE ${SERVER_PORT} EXPOSE ${SNAIL_PORT} ADD ./target/ruoyi-admin.jar ./app.jar -# 工作流字体文件 -ADD ./zhFonts/ /usr/share/fonts/zhFonts/ SHELL ["/bin/bash", "-c"] diff --git a/ruoyi-admin/zhFonts/.uuid b/ruoyi-admin/zhFonts/.uuid deleted file mode 100644 index cee5cdd7c..000000000 --- a/ruoyi-admin/zhFonts/.uuid +++ /dev/null @@ -1 +0,0 @@ -3f2ee348-0303-40ca-bf03-03f48d2d2141 \ No newline at end of file diff --git a/ruoyi-admin/zhFonts/SIMSUN.TTC b/ruoyi-admin/zhFonts/SIMSUN.TTC deleted file mode 100644 index 6ca8de3dab297b4a66a6cf57e1992148d768191a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18008680 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

k(n<*+CMg;C|WZwB8v_vWAFJ|5v|QWpFNPM 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

kn;ISH!aHI7HdY5F9T@}3b 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¨C6 zEKb0lLsyALjg%cjENXwHGjyI<2-o@Sj!3iFt^|eOb+vW*7b#7XE=>{3M6s~rr+u(M zT-mig$YneB;$YKI&Dt6)|1qV*AsJ#8s1PcP-vI8y)^7HwFZ_@hLo6F!bHcBRW5t(A zU-Yi|48b5$7E>)nWy-~~r9>v*^~S!gVc017Zf*BcriH3`aL)m|eTx8a);k^i6U^Xb z&|aCetq)7Eu%fTxwOSVyg58!I=NqH;0iMLM3ZWIr|fg}in zuHCcEt>(P;E0&$hZwZ~pWJ=Yxv^^geqpj_7$%<4p{)0p8Q6n40xo2&;C$(+%=*D%v z518qi8gs9<)LyF5ow6tHdM?_+5XjJA#)%qx90R*ryDoZ`AEx!7){<^^a0R^f^fm|s3CGzNk={kpl2{JwI1wnCRK0SU42^J3 z6oN(e2UKwoII=lh?qphEq0PX7B~>+^hW?J~&_4_HMN9L*+9J zfef9>ZK?Z4wWK9@%&~5a#q49 z6_Gla7KzriSKI3@nO3#hCrq_t?2CzrK-Hd>>jK7Y#{3D2nN`BS_Ub!zmndye8Qpk> z&z`7td+e28Zuo89O|AB%Udmbd|5~eahwk|e1xiL;_hFRX4->HcyL*27ue|(A=BgoO z4>JZI(-UI;?Ixlj_56l2Mi4~;j$GNP2h>bxC>tB#Em01pY+ifNfY2A{;F5urQb8YKy_tqV+KQj z3p!`Ik{6$B;Cx$?8bg#I;O=%$`=-sCnK9sM+yA@%1|tR+3!po>oH1q${x>a1LSPU5=u^G6eP+U~=t%!Cc35g~HmkUyAES;lb^7SHYd2yOLBsYMzW? zNtmvwE|K^5qcmaW#neFluN+3fRyEtznooswTh#T##mWBS52I~cm^bT^=P9MK{2lo89DUco`k; zF7=4{dOe3gi8D^L1Y7MAlSOP|R)H^Skafd{RE`@X^Wk2zlbh%k{6$*@%Hn6*rB^y7 z9)+-?j3!{8o`bvI$euIhl*CglHZd&BJ`j*7*X(T1r(4jo{jSd|zU_i3d??nbGW&GQ zY34>}$>=+H(S+P^bhp%yRw z!y0k?w>z)v3farHSkxg?r53(lS`PO%vFoT)?b@@$zvqMZtYS}*@O|dRyst-i(wnUg z{rrpIS1Q?sWAM;ZXRYeU8L21#JZw`hXMMW%U5F!RXL02IBE0xJ@?hS7_o1;3?AVJG z&|+J(^$L~iiKu#K6T+?16R=H^lV$W6+5hs&ogA<`irPD{f-^w%6?ke?ItZp_e`VbG5i|h3zS7!+ie-Uz4Ohg`gj;0)BL;#KX2p^9X^}=_B=hho z?Yl=g>yW=R-9n?8;+#x+RzsORWRXM7iZuS>$NX0+dK@J$sg^`vm%Pt*wP;*g_yyTB zBt={ovO9|}I7_}#+iiJdwYGHRS|-9ea#pDTj%B51?v&%U_N=7+XICwyBMKcWp`xS% ziEd~9R7?IzM4&hD>ye0TS+rjNgK_Pez9?q{7l2w={Fc7tEyt1(uejWnzVJZR!Q9l? z>})}D;n=_{!`Ce1#g8RNzKuMoJ@}~>P@2CpvLJkM!@6|B%xjrC!YIyg$v!7Ml8lU- zGU&Np9kfQ)t>hI)TkQ=YGszr{+`m`^w=T!N_6LIx>{SRCm(g?8<68vv$_o90i=ZrT z^72WVu!7h+T2%Z#yyGf)%Cb~*H$y<5QR&LCtF88Ey*ZG{y4X`^v{xQp$-CO+u)b%@ zy~1N!l)tAgmoC>AhHUV5sI8oh2smG4SuEn4*|?EI;8k%6<$uyRbjz@-f!eY1s(`eZ z8AEYWGbGjZu0F61o}Cp#=tgs-9$xEK+^kQwuAR{PqU_}|`rM~9KUe=LMtpY~uB07( zjOmFcV6zEmQqEiT-@t>W^~T&QY#auJ%sZC5zH!x#HP!}SuVcDGEp#{Qufhx2Vwf%K zEDoVN(mjgwDp*xWbFtU;cA4tK&;#?+3)$*TAOOUU@Ui_G|AFTqO=fhi_H!}oszkF}Ol7c%4vFG2JE)Kz1^(gV`x0q-VwrvaV>hKPx z1-!C3uX*nG5YQt)ozEY*wgn8nYwvCnzIW|B4_6tpgO|pug||$?bHcY7{yxIvyruf| zI|5B!SJ$;&+4>(S&h;!@w94wWjq&tQ0IXz#Lo;4->0djH6`$)0_8TQ_<&2gg)gmWUU$hZgK6>-suc&Ueq@bR}M>YAx8EFcRmY zqX3<->yRv+@U!2pfi;`yK8d&feu(aGaU0_&#~h-rYefg}U0r83R0D z{6}MA7-YjfGgGFpAWBo_D^$*@fB|$-Au1Te0#WS{@o^{p!(HO_^O|s zm3TosF#qm^UT|?1+YzsxUlfI0Q{aoD^%UCztB?5{k8f~bx^2oC7V$@a z^|{IUMuY5*+iU!7r=MX6iXUt(x@9Ww55d*(6Bh()(sR=q?;o5$xY!MASdjnV5Me|fYMbtS#+E3&!8m}I)z_TbS6oKLL$q3-y7 z@3#Ri#1MkM(3Al*;*v8fxA4B<9_I~H!kh0?10b;Kq+e+q+h-e%8n!Wd$Yr;mPPBjHE{g@1M9Ix^FJ;dwmI*`ei{$ZK-$Q`+kn$SZ zuO$?oS2Yd^-oKUr-?Y2)Iy$cAz+1jWcVF<+ zyqamD&~>#WV7k=ot_-s7yag{l9=L|UlgUs1r@6(DWLnT{S9NxQ3|99u1ZGxUEyrM1 zvs&~D;1bcx(Y+rESOPPvPSYh%n}$uMrp9>3oa8Gm6QRor&+IYa$@gcrds=?@TZTZ- z3ZQLuYfT3W#OX%(2?FX=5mwwN)v!!Vwdf_1!p<&j^JPVHkml7i0ZsRD_lsvbIMao$ z@HKIVsH0|8LtdoIPq&y^ha*?s@=#+<^=r(mW(b+G_PnHbdE`~5#qr0t%7b)s9BT!Z4 zou{N2#3eKWYJ-9gl@0KAgM0zA3^XYoA@AvRkxIfy@E8K_YD&8C2k*+z!&9IXXW@E} zU&1%f3?Wny%$Ch`<>Tqq@FET55NW3OjJQZ(aL5ISj*ZA-hA6;X>Vm&~&+=S3lX^mc zwZoQ{3Hf>v!&`mkB<3m*y7iPotRIm7CBLvbdaO2u%P^iQEOo7LN^=Q%ZtW{tbTySDTRU0DfF$rSO74Ms`_cYoGt5N{2XHh5c4Bdwh!}7=jx!6?0iAv`h42El zj1j#2-=P;!SZM?1qFK>nurlHzR&4oRLi4n=_4MiqNDbl{OaK?^LoCtzGcHbx0wW?` zNDoxT1AKVl^|d*2_WuU}Ak^SJ9?WH1hO>x=hj2k~MFq0tYChU;IlTy!R&d5Ri=an9 zBcr)u4WyCOCtY*{K53{Fbo_Yveh4`SjQU*+XJOSCmXo0P*mk*D$isT+7AZLwOv02> zESE7Y(6;i`rT`{~Rij)vJ2p8dOOWsy)E3J!Q!cYdARx&GIDBr>R74p6ub_+`!u92_ ziSUv=C-qcW^tm6hW(p<+vPK0&aiB|JA@mSc7jXH>yaZ_!7|cs2Sq4u3K5@!9^8oA1Ux~)bB$^VA-SRU`*)Bv)e>8P*;P15 zpg|_Xjbw|&r?k#vCS}Csm?$paiM318^&O0zhg&K<7}{0UvqddH(TW13Qe^=R$w-dNOXf-TDNkA~Me0|xl4%Rq@zg5Mt~gl) z;>?bLcqrh*r5O!LewuoA3-s#jQTa)1Hh~b2>+^egIui7Kcpk@1G71=ujXa>IMQ za-2v6Acv>+A@QUMGm5R}o!Pdbxer69%){fU%xgipGxB^A!>TSrl1HI3zm~_s`g}Mp zE&^*t29A0T3>-ZI?4KB)4`+is0E zbjtB|l_#<{$nSj!dQy2d(>;-rDHFBl17(dla=o^K^lYLDunIynte7kAtr7aHR0Bp2 z5$b~#s8v0XUxky#4q?LMVq8<;%7__N)B}<^45RQFZ$oy853m{MUNNw`1W#}~S_PJ- z@WArP8rMg=4Qh=hZBodTWXF-MbZXS_gn)~lt+RJWvN1#KeTTp7Mz=rk6Otx~B?=Q2 z7%I%uQ%i*}KS(hPhpwc2uu3MqKyF@Ez&a=?b4BeB^7UqoK&fK2Ev-BeR%o?WvQ|mo z2s*)>@Pp!3TZQ25(1R=B;jw}PkAHFfkhUA1npw&SThEA|5RIb!3)%jx*Bco+w5~(F1mqRs8@^PO3Gj^A|hFOKc^>9^#yLYJHVg@ zzl>zYMrW)6Q^@?T71Pp5cZCQ%OyNA$3m$F-$Gvn@W&-r^Zr*Hpu6mD9pcr_^MSwjC zR-S3G)s17$GenY<)2o>FE+>!rNN5mOaOR|NQY4R*kasRgbAUjZ>b%&Yd2N2Xw%-WO zGu>0wd4jj5t3~3je#&g)xKh&$Tukxxk{c*x!5n4?N`UF8>s20pyWPIbL}g%fPe`kQ zAxjaKn+li)xZg{PVg^yrZwIsF%VG{?X70F@-!Y|?>XVu3mx~D(%|Ij`$hxkMBX?BC zhKAq@BR@6tZpu&P5cq`Nk82XH)iI;mB7}R>=}Cn~;N|XuXWBEyr(Fbk2v{Dnx}NNq zyUZ$d5^^qq_k9E;<+vrwbxjN8pErNR4;j_vr4eNEnXza5xCXu2vP`wZ#r!^o>zh8d zM#|O4!M|hGJe)k_o--Y7qUYqc!fS3N33OC*2T*9h>B;P!uk0!}zh}E?QqM3DgxM?_ zOF6HZ0`9>B(^}YUJ1Dh>Ayv7qa*vH30yL*WXfE?+6%@!R{sK%-69~~b4?XzTFxx@q zlMPO&B9Pa@o0r-wkUF}>jQEQs^5nF;MiX#C-^iC+G*Vk+vAkiaWtb+=dS22VRBA49 z4h-@Hd^|%Hc$dW^ZVq4s3Nj@Kz50}duucWGCt%OH*rai&yXR{cn|%xqy#l#PFm68v zA&#`U~6ud;(NbaG9 zja~;5D$xjHyIkcJI@wv_`F8+>wAQ-8^6)UYWkxY44p6|JP_A`f!*Wc>`*ChlPfRb>li5NXu?3;5? zpd{9vNx9n(vlUYM3&S)1%zf&Cc^rR+Po|WM4y?s=fJ1i$Mij-_LH?Fm<_eBw3vOBL z&WQ2lR42jOO0=6} zd%*pwxzVD9H=*cR!MT)s3cw`#Yf1zaW1$lp=H7)8e*SLA5DxMsVFivzCWKvf1A7^!muo z>^LPBasw$G1XK17FWIkD9`0-p{-lgq23RbCqDa*eNT%xxkZ6%e0IZ5H)oSmiSbrmn zT#AXXrN6cViUY3M4vg-KHd(SucJvg1lVMfzb1Ye=#huqElIe%F`PYWvwCicG-JlWt zq{hFQ9)m~R?!l;p-$*#s8or!81=9}4BtS&Q(mp=O3*7?iA8yAff2db))o2&vJQqy} zjd{on6q`focFDJ(ylDd8UTy}fMTq0o0;{Avm!t`yelm}Tef;1c$U^oMVc;B163=+p z4F)1T*q+`FynR=!W0`fCopnkLxL${=yMpj4l-8nE5bW5~o`uejCCD`6goMPcw+N(V z-KsDIs=f5202*+*3A5LDv_|dKTKLRt5voMZe+t=}bS(|;pHrYw^?*&$jR_6JCR)QZ z_mTMAz4!RsHt%W@TrheFkZ4f33GjOzH$0rhvAxK25Xkk6A=g4pF-(Vm9^%miAX+^& z%RM0$aYtUPn>gU#si*@Bp~RVG5dIG2IqCc^Qa8c{=CE7dQ+jM)7S%)?co<;B&Gf8b zec%{wgP6DTmI0-S-EkhF?*X?h^e$m29nF&OVooM3oRm%22S7M~yu7P{S)WI6yPi*+ zI0~|pI%mW55MF`WL^UNkaXlsR*{kP}G^au%P`WsNt?~xrq;pY-Q?}!;o`?+Uac3J?>9`SU3bjA_HM^9=-@!X9H+GQGAxy4H$rBz9Ok=@t=@bjW*U{g%*}fGeUc3wE(vx z4>x4XNPyM)(zW`yKolzi&wjfy2Mtc|IFcRFC_2h++uFDRqZxuGL6SX!kmjVE2y&2SXhDk3Ikc^|sMH#D zur>(vOTAEII=s^sg_y9qg`{VN&VJ4)*_jywJ1_xmBTYbfm(ccCL2g+$o&wBif?T!@ zUTw&|Y$;QI>qZ8GUU+B>YEoh09&y4Z;l^eCq%ymY3h@iiu4j96Nv@&x|5?(}Rx z8KQ^c1JB#xiFl1vv#KV*u#htQ0i%L-E0Q?q1Qvs%1e5xM>t%307Fx^nsp z?A=gGN*+Y2o0%-^5^kCFxLgVFUf5^~IB3b#gLLZQdz-}^P^9iWrZcHLGPl@RHONtU z9s&S%i_OD}{}2?IjCCz|AG|~GGLpz2NRJ-SwxZB)5F>TzB9)T0%sSg_l@6AvBeO5u z3Yr_D>Koap+K@RZ%u8tftxuU0D<$eNAMn(B1xny2QBN( z{e&WW#2>%|0-P}7OapM@2~Sw>+7aig%vK(@iDoX}Zy`i6#bW#5a;?uwu#Q`340lq3 zxrg!1Z-~GAoMTZa%?i~b>OgQu^?<;lExL|j6)qMy-L^(`^{`2rhU6pWUcW-#gUJcm z05`bSzek4_CdhSilpA0KPPfaEQ*FDk$nt+G?`C?s$N&6lfL~vF6MT86DXKKZNO1&IKo?8o|I1^MqVHqsgCk z{PjtHLEHsuX&OYOCxv7rW#%xmKxnMaWhS-eBqhTLWci)xDM~nj$l2m**3@nK9aux#genE7Ei3u5iYTAaJE%O2t2S#-WKyO(`q?>rtWg4l2B$` z>s5sY%oYRz?uyMAKw7G%okKKTzg6t|yNh1`gDg-_@9atOmo@^l zbX8ETenJ}FNxI9Jt{^(g9C3x-ZFgw%4*yN9R zTAJf-No9hQ|7z|dWSDx=l^t&o_4($`W!8aAKaU8vW}XtKduSk_%cK+0A#1o!7y&0& zPfr(_W#5LK?d2og%cI{y^i2~8@kJxqFa;h=B9rJ;vGrjyYq%PIl8Q8TeG0xdNO&KIm`@aA!T54_Wi)Ej5;YOzX2HZILKR1+GZGLz|wPj*0V zW*qGYz?|L;j2?U%j00gG`B^$j$Il(Nh|U%1{JbO~nBXvxjf7^&+lvE!yU@~irlkXD;Kqg+@&3 z#`#LG+a;`7*0P%!V)~q^!P1fGp>NemLI^`35VCX>(iO}$@x@obI~2CS4e$@wo|pC6-_F-;&|`IADG=iP2&m6)+{uf%OhczNOfsHor1-lJr zWDD#T_7H#}5X<7G9yhkV;4TDo(qn)j&|wg4t%*0t#5W*qzoK5J}J z!4|ln`SGPf?LC~>UI1yooNsvQ8Ynro3XM6;IzXfi?G2Ij%jb*81kUFjT@%8 z3AvM+k7B1JN>!?u1)`uIl0*=xZwEMsBZvIlDI$B=03n1^Xw@3XP4oh%Lj|7mBVty> zgYTJ=V7#QthGGba4Vf0HqX~j?jS5vr6f#~$b!m&9${$d66*3D1i@n`D|FNz$Bdxj7 z3XW?v9fv8LA9*#9#B{Oc4l^3xk18kB+)u^X!yHU!2`;|)K!(ek!Vh3;Szw_dXA-d_ zQO6mf z#d^c@>?!D@swL{ZU6|{DUBYpV4ViHaSDU{_e}k!`$SJ@&^>ENQQGZJHWF$dU66paN z_N$9(cSlmbc6JVK)$(#*$lg?+a_io2@7hdTo6wltlrsv6e++%G+2f*Dv2f7qzzq(4 z?PjgiB5{Zr;aPLiwTMq_p|ay#R#nRRLz@$g?U~ z5l6Uix@UzJ=n|xkCSSZ3EvgGtS zz~9q=mmS(E-cG;*S$veT5J`_Z1*%Lto2__hsA|OjocSYML;|Szge$BB@;ANYeK30PL zJzn#h%dYm#bc<>AW^kUC2TQ|72qI|$%fnf^6_SSFwx(PX-56J_2=UgN@t3afgEs4e zXYEDTG14=Gwm1jZ0f5wWy0^1)a}lb(jT~ic&TMnMrmyg&htZpfDg*oS>EU zr7_?wCurpnU9C8E!(P2!qcPjXbC})W*&uFg_(1EhjbYS>KW4KH#>~A?==iT|Z6g^F zXD;d}ROCadN*Ju3C#RIupQ?Ya;(t_!OtY4YQ*?Bi^pec?0W#NK7{nb+CRp@4I)z0{x4( z1HOjPT+uK+1|RafZnddZkIkh283NKU;-oS^PUZTdUF|NKP>%a`9z4@ zCb)AZ71?oq2>b}7-q_QRRxVIp<&kk+AR5NQ~WTSuQ|zPx-%zeKx}8GQQ4B0wo2Neg;_0N`a2Y&jyBT?|Kz zo^7{cd$oIe+ht3r#X`0#(I8aQ#4KbBMHX7tSy>^ON!cjzisScMqo(H>u7i3@dd!pu z-5ek^nsv+=c*c)*KrSWOxF(qn0OPqJh;aG=uU6hr;bm3m^mK~>Yc;{~;3gIGAJz(# zMRQ0@e4B;q4$ZK{lO3O3%LDkA8^L(ksySAlj4!7*3V^X+L3U*2DY&jkzQOITOF%0sTg;0WT^ zoaPi-J}nGZE2nhQEr<(L86L=R3ZU3r;shn@=B z9$5*XPA0>|W-qx46fcDSLD_(jQ;m!A{nLoGT9Afj0T?P4$h{~)O!ssN@i~+1T#>3R z76cK6QeJv+U^{=;daHNMNKrbJHUoMSnT8%g1->>afZN`CBo>gn)}RI zqPRWi>4ixCbM1U)qoxOUEN$`6niZccck)&ziQMsbL}?zaD@-Q+){%EA;mH2=!^RF~ zqjWI?()rq!vRPftXuh{i;Va7yerRrD2KQS?1oyN<~Vs%N9)J$-6N!0~D+!j7* z%Z<;v0t{MIi!PRzwqS`oYw*r_tk*n1+%;EYI?-sGs9F1!$3=4l>6k`CQJr1BjqqOL zaGhv7RUERHtgKd-FN!QyJNV%bn7Zs}+0uB!DV{+Q(}5MV z4qgfsNX3){!x9)=f-5p$aHFU%sARKAVliAT&0DylRo(+AseN8AJzam!q#ufwPUwko ztmHqYW`>DaoA@Q^+JlZdMY+04;>p117H$DNIJ%w;?M^L_6G+SmyC-}0`8Rp?-U{eO zLk1KZ^?z8zIykz^a0Fo;pco)a!h!feQG+ok1S!G5W8lQb`tFZ}nd?oIdjvcNZkSC5 z#pMgdhT;!-A&Iu4>Uol$?%fw^ZgUaHJD!z3x$Zl$691+@_q5`?Ak7km##E-}aqP%@ zL{FYJVcWxQEmFJZif*0;V>odEXjdhFM-rwx>zE^gES3_Vbf^;TV0N!p2Bpn_>u|cF z9^R~+XFYLBD6bQ0DoWWz1#|^5CW;G=DN80KbL8{_!5;%82;c%kl9!gYMKR+1l{3m` z@mkj5}#b+|1NokHm&pNww{032F?tszEYFF`1kqTh|fbJAxhrlKwCY&>Y z%`p-&xzk^J5qFl=#wi0m0^?g@5LrzSET^XTwE;xLAsh&DN>ya1Ey!f!vT?EU5LdVA zVmy>8z<{9pssf;JdW_U$y^|dk1+&lsNLhUyGzy;h=cq&bUwin~7~KMntjlA_%p?P| zDimhc?ZI6MW!iK@eSW8RexhqlJYdS7l<#GBW2Oa2TVA>&fvVwT`3_ zv(7Rgu6d%-n&>V%NkX@n(chq9^rEqKEse}NjCg!0ukZa`e7$oiF@bY}X4-#iND?^{ z9V`};!pz!}{Cq$j`mGh&W%L**{)VK#s&x>&F>c-nP6yZ+s032~1n^V%dWCL5Yhx(& zD)j*XzL_R4;^X}U!BedJ&uW;2I&ulr6Ws!~7E3wvyrf)niDOlu8US({+Yo>P_^GFu z8JrTpT?8fDP~{oKZx2$g@9PS#tOzkJ@Z6P#x#u!LFU&&tB{hL$&`<=r6|J08pbHL> zBy;Tw#oQ2Y$x;{}n+|>XH^E5O~NWQb3udgTf#2bXs!vK`fYWl@7$}u-` ze|VRC=LUQDVTS834p7x|N3r(o&Az3T+#G4hTW|*y!AB{1BE6Pmp)FwdD6R?t)h!}k zRzZ`5F-}L%+zoH#H$hXOO%<+&cZ&Ka?VM0XKW|HtE|o)rey)( zUUSZ<=}039gy@6p_e&O47$R^KHbYq-H)%BuJzyyFCn(W&W|h%h!J&1+q4n*GYi0ev zaq#^dLh2(k7lI!64hcg5Ul*lGp2-@#A;%njkUa(EnTZFBWB$6+CMUYwgv~)^K&^)? z7J0&cbg1}YJ0~XLi!VYvZ4iUAl5V-C!_5?wrwxCu&5_fy0(sN&U0(SMBhZ2ixdN~3 z9{F{PB<<)!m%GF^rp3H^Ps^TK@X2SL0%8yHFcb)EhD&2O<$IxivofP;H+W;gxxP0QhX4rP=KL|#+1;G9ve!jYlQ~qiqvF5xyJFMww>JDc2sZ zac!cI0YRC zGR1cgK0xv}(_>J=ZJ*~^Ph2rahM<<^bN1sfrQ}6^$kq%cE8h_j_<;5(-2$vR@|0$r zO4wwEFzpHIz!T(`OlSpinqa_RRqcEu2!M=JrM70V)`Lz?O=n4UZl(oo3lK=#oXBH= z;5m|&PY64{Q0H&`u$$cvXVq%TL!~7=zg^6{t!M;f`1%dEk=x=~G1aoJXE(d4}02+R}=)qzF41Shr`C zbj%&0qPsSeb%UIA7BWy!c^Yk$|CVe@*x3utXFv>fTTGR{fmm(-q1Few>puz9X{!dj ztbYoJSt)XrI|qprB*FT1ynID;Ug2%_>kZqN&WU!hY7A_)0

-+4M=ANO1Cz;Od&) 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