commit 4cb2b84df969cc0eb3a65299e0c0bb8b00790b38
Author: wangwei_123 <1255324804@qq.com>
Date: Fri Jul 25 10:44:56 2025 +0800
初始化
diff --git a/ruoyi-vue-pro-master-jdk17/.gitignore b/ruoyi-vue-pro-master-jdk17/.gitignore
new file mode 100644
index 0000000..49330ee
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/.gitignore
@@ -0,0 +1,54 @@
+######################################################################
+# Build Tools
+
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+.flattened-pom.xml
+
+######################################################################
+# IDE
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+nbproject/private/
+build/*
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+
+######################################################################
+# Others
+*.log
+*.xml.versionsBackup
+*.swp
+
+!*/build/*.java
+!*/build/*.html
+!*/build/*.xml
+
+### JRebel ###
+rebel.xml
+
+application-my.yaml
+
+/yudao-ui-app/unpackage/
+**/.DS_Store
diff --git a/ruoyi-vue-pro-master-jdk17/.image/Java监控.jpg b/ruoyi-vue-pro-master-jdk17/.image/Java监控.jpg
new file mode 100644
index 0000000..6ad522a
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/Java监控.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/MySQL.jpg b/ruoyi-vue-pro-master-jdk17/.image/MySQL.jpg
new file mode 100644
index 0000000..64a1940
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/MySQL.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/OA请假-列表.jpg b/ruoyi-vue-pro-master-jdk17/.image/OA请假-列表.jpg
new file mode 100644
index 0000000..787bb73
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/OA请假-列表.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/OA请假-发起.jpg b/ruoyi-vue-pro-master-jdk17/.image/OA请假-发起.jpg
new file mode 100644
index 0000000..1a7342d
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/OA请假-发起.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/OA请假-详情.jpg b/ruoyi-vue-pro-master-jdk17/.image/OA请假-详情.jpg
new file mode 100644
index 0000000..a83e7c1
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/OA请假-详情.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/Redis.jpg b/ruoyi-vue-pro-master-jdk17/.image/Redis.jpg
new file mode 100644
index 0000000..9569352
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/Redis.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/01.png b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/01.png
new file mode 100644
index 0000000..0f65d99
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/01.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/02.png b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/02.png
new file mode 100644
index 0000000..05ec781
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/02.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/03.png b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/03.png
new file mode 100644
index 0000000..f400c68
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/03.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/04.png b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/04.png
new file mode 100644
index 0000000..d5d5ea0
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/04.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/05.png b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/05.png
new file mode 100644
index 0000000..1de6d8a
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/05.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/06.png b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/06.png
new file mode 100644
index 0000000..400ae90
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/06.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/07.png b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/07.png
new file mode 100644
index 0000000..2ed8c0f
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/07.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/08.png b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/08.png
new file mode 100644
index 0000000..090e64a
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/08.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/09.png b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/09.png
new file mode 100644
index 0000000..f2032c8
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/admin-uniapp/09.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/ai-feature.png b/ruoyi-vue-pro-master-jdk17/.image/common/ai-feature.png
new file mode 100644
index 0000000..7f8c92f
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/ai-feature.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/ai-preview.gif b/ruoyi-vue-pro-master-jdk17/.image/common/ai-preview.gif
new file mode 100644
index 0000000..5f13ac4
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/ai-preview.gif differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/bpm-feature.png b/ruoyi-vue-pro-master-jdk17/.image/common/bpm-feature.png
new file mode 100644
index 0000000..23787fb
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/bpm-feature.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/crm-feature.png b/ruoyi-vue-pro-master-jdk17/.image/common/crm-feature.png
new file mode 100644
index 0000000..e1c9670
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/crm-feature.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/erp-feature.png b/ruoyi-vue-pro-master-jdk17/.image/common/erp-feature.png
new file mode 100644
index 0000000..d30b30e
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/erp-feature.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/infra-feature.png b/ruoyi-vue-pro-master-jdk17/.image/common/infra-feature.png
new file mode 100644
index 0000000..f5cef50
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/infra-feature.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/mall-feature.png b/ruoyi-vue-pro-master-jdk17/.image/common/mall-feature.png
new file mode 100644
index 0000000..cca05c0
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/mall-feature.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/mall-preview.png b/ruoyi-vue-pro-master-jdk17/.image/common/mall-preview.png
new file mode 100644
index 0000000..e164cd2
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/mall-preview.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/project-vs.png b/ruoyi-vue-pro-master-jdk17/.image/common/project-vs.png
new file mode 100644
index 0000000..561e092
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/project-vs.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/ruoyi-vue-pro-architecture.png b/ruoyi-vue-pro-master-jdk17/.image/common/ruoyi-vue-pro-architecture.png
new file mode 100644
index 0000000..7bd7d59
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/ruoyi-vue-pro-architecture.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/ruoyi-vue-pro-biz.png b/ruoyi-vue-pro-master-jdk17/.image/common/ruoyi-vue-pro-biz.png
new file mode 100644
index 0000000..24a385a
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/ruoyi-vue-pro-biz.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/system-feature.png b/ruoyi-vue-pro-master-jdk17/.image/common/system-feature.png
new file mode 100644
index 0000000..366087c
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/system-feature.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/yudao-cloud-architecture.png b/ruoyi-vue-pro-master-jdk17/.image/common/yudao-cloud-architecture.png
new file mode 100644
index 0000000..59416d8
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/yudao-cloud-architecture.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/common/yudao-roadmap.png b/ruoyi-vue-pro-master-jdk17/.image/common/yudao-roadmap.png
new file mode 100644
index 0000000..f4becc9
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/common/yudao-roadmap.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/个人中心.jpg b/ruoyi-vue-pro-master-jdk17/.image/个人中心.jpg
new file mode 100644
index 0000000..ce57f6e
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/个人中心.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/代码生成.jpg b/ruoyi-vue-pro-master-jdk17/.image/代码生成.jpg
new file mode 100644
index 0000000..751603e
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/代码生成.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/令牌管理.jpg b/ruoyi-vue-pro-master-jdk17/.image/令牌管理.jpg
new file mode 100644
index 0000000..04abf4d
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/令牌管理.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/任务列表-审批.jpg b/ruoyi-vue-pro-master-jdk17/.image/任务列表-审批.jpg
new file mode 100644
index 0000000..cba312a
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/任务列表-审批.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/任务列表-已办.jpg b/ruoyi-vue-pro-master-jdk17/.image/任务列表-已办.jpg
new file mode 100644
index 0000000..7a8d0fb
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/任务列表-已办.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/任务列表-待办.jpg b/ruoyi-vue-pro-master-jdk17/.image/任务列表-待办.jpg
new file mode 100644
index 0000000..a90323f
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/任务列表-待办.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/任务日志.jpg b/ruoyi-vue-pro-master-jdk17/.image/任务日志.jpg
new file mode 100644
index 0000000..599e50a
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/任务日志.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/商户信息.jpg b/ruoyi-vue-pro-master-jdk17/.image/商户信息.jpg
new file mode 100644
index 0000000..483eace
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/商户信息.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/在线用户.jpg b/ruoyi-vue-pro-master-jdk17/.image/在线用户.jpg
new file mode 100644
index 0000000..b183009
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/在线用户.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/大屏设计器-列表.jpg b/ruoyi-vue-pro-master-jdk17/.image/大屏设计器-列表.jpg
new file mode 100644
index 0000000..9a45c3b
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/大屏设计器-列表.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/大屏设计器-编辑.jpg b/ruoyi-vue-pro-master-jdk17/.image/大屏设计器-编辑.jpg
new file mode 100644
index 0000000..63298a0
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/大屏设计器-编辑.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/大屏设计器-预览.jpg b/ruoyi-vue-pro-master-jdk17/.image/大屏设计器-预览.jpg
new file mode 100644
index 0000000..501d9ea
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/大屏设计器-预览.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/字典数据.jpg b/ruoyi-vue-pro-master-jdk17/.image/字典数据.jpg
new file mode 100644
index 0000000..8298c89
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/字典数据.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/字典类型.jpg b/ruoyi-vue-pro-master-jdk17/.image/字典类型.jpg
new file mode 100644
index 0000000..6613392
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/字典类型.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/定时任务.jpg b/ruoyi-vue-pro-master-jdk17/.image/定时任务.jpg
new file mode 100644
index 0000000..d5bbd85
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/定时任务.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/岗位管理.jpg b/ruoyi-vue-pro-master-jdk17/.image/岗位管理.jpg
new file mode 100644
index 0000000..42b64d2
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/岗位管理.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/工作流设计器-bpmn.jpg b/ruoyi-vue-pro-master-jdk17/.image/工作流设计器-bpmn.jpg
new file mode 100644
index 0000000..2a61f60
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/工作流设计器-bpmn.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/工作流设计器-simple.jpg b/ruoyi-vue-pro-master-jdk17/.image/工作流设计器-simple.jpg
new file mode 100644
index 0000000..9ef2c9e
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/工作流设计器-simple.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/应用信息-列表.jpg b/ruoyi-vue-pro-master-jdk17/.image/应用信息-列表.jpg
new file mode 100644
index 0000000..da419a2
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/应用信息-列表.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/应用信息-编辑.jpg b/ruoyi-vue-pro-master-jdk17/.image/应用信息-编辑.jpg
new file mode 100644
index 0000000..913cfbc
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/应用信息-编辑.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/应用管理.jpg b/ruoyi-vue-pro-master-jdk17/.image/应用管理.jpg
new file mode 100644
index 0000000..6e7789f
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/应用管理.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/我的流程-列表.jpg b/ruoyi-vue-pro-master-jdk17/.image/我的流程-列表.jpg
new file mode 100644
index 0000000..223d17a
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/我的流程-列表.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/我的流程-发起.jpg b/ruoyi-vue-pro-master-jdk17/.image/我的流程-发起.jpg
new file mode 100644
index 0000000..7a83306
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/我的流程-发起.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/我的流程-详情.jpg b/ruoyi-vue-pro-master-jdk17/.image/我的流程-详情.jpg
new file mode 100644
index 0000000..6a01541
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/我的流程-详情.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/报表设计器-图形报表.jpg b/ruoyi-vue-pro-master-jdk17/.image/报表设计器-图形报表.jpg
new file mode 100644
index 0000000..681b318
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/报表设计器-图形报表.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/报表设计器-打印设计.jpg b/ruoyi-vue-pro-master-jdk17/.image/报表设计器-打印设计.jpg
new file mode 100644
index 0000000..bb86da6
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/报表设计器-打印设计.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/报表设计器-数据报表.jpg b/ruoyi-vue-pro-master-jdk17/.image/报表设计器-数据报表.jpg
new file mode 100644
index 0000000..9ca5b9b
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/报表设计器-数据报表.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/操作日志.jpg b/ruoyi-vue-pro-master-jdk17/.image/操作日志.jpg
new file mode 100644
index 0000000..4a0611a
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/操作日志.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/支付订单.jpg b/ruoyi-vue-pro-master-jdk17/.image/支付订单.jpg
new file mode 100644
index 0000000..0a56dd7
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/支付订单.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/敏感词.jpg b/ruoyi-vue-pro-master-jdk17/.image/敏感词.jpg
new file mode 100644
index 0000000..92a5397
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/敏感词.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/数据库文档.jpg b/ruoyi-vue-pro-master-jdk17/.image/数据库文档.jpg
new file mode 100644
index 0000000..a4339d9
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/数据库文档.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/文件管理.jpg b/ruoyi-vue-pro-master-jdk17/.image/文件管理.jpg
new file mode 100644
index 0000000..054b19f
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/文件管理.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/文件管理2.jpg b/ruoyi-vue-pro-master-jdk17/.image/文件管理2.jpg
new file mode 100644
index 0000000..b12e5c3
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/文件管理2.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/文件配置.jpg b/ruoyi-vue-pro-master-jdk17/.image/文件配置.jpg
new file mode 100644
index 0000000..e618049
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/文件配置.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/日志中心.jpg b/ruoyi-vue-pro-master-jdk17/.image/日志中心.jpg
new file mode 100644
index 0000000..27c1c6c
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/日志中心.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/流程模型-列表.jpg b/ruoyi-vue-pro-master-jdk17/.image/流程模型-列表.jpg
new file mode 100644
index 0000000..ffdc584
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/流程模型-列表.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/流程模型-定义.jpg b/ruoyi-vue-pro-master-jdk17/.image/流程模型-定义.jpg
new file mode 100644
index 0000000..18b316c
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/流程模型-定义.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/流程模型-设计.jpg b/ruoyi-vue-pro-master-jdk17/.image/流程模型-设计.jpg
new file mode 100644
index 0000000..9614969
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/流程模型-设计.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/流程表单.jpg b/ruoyi-vue-pro-master-jdk17/.image/流程表单.jpg
new file mode 100644
index 0000000..60669c1
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/流程表单.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/生成效果.jpg b/ruoyi-vue-pro-master-jdk17/.image/生成效果.jpg
new file mode 100644
index 0000000..98ff2cc
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/生成效果.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/用户分组.jpg b/ruoyi-vue-pro-master-jdk17/.image/用户分组.jpg
new file mode 100644
index 0000000..39af1cd
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/用户分组.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/用户管理.jpg b/ruoyi-vue-pro-master-jdk17/.image/用户管理.jpg
new file mode 100644
index 0000000..844604a
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/用户管理.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/登录.jpg b/ruoyi-vue-pro-master-jdk17/.image/登录.jpg
new file mode 100644
index 0000000..b782b98
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/登录.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/登录日志.jpg b/ruoyi-vue-pro-master-jdk17/.image/登录日志.jpg
new file mode 100644
index 0000000..25662d9
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/登录日志.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/短信日志.jpg b/ruoyi-vue-pro-master-jdk17/.image/短信日志.jpg
new file mode 100644
index 0000000..ada8e56
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/短信日志.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/短信模板.jpg b/ruoyi-vue-pro-master-jdk17/.image/短信模板.jpg
new file mode 100644
index 0000000..09381cc
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/短信模板.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/短信渠道.jpg b/ruoyi-vue-pro-master-jdk17/.image/短信渠道.jpg
new file mode 100644
index 0000000..df3a5c3
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/短信渠道.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/租户套餐.png b/ruoyi-vue-pro-master-jdk17/.image/租户套餐.png
new file mode 100644
index 0000000..9663167
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/租户套餐.png differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/租户管理.jpg b/ruoyi-vue-pro-master-jdk17/.image/租户管理.jpg
new file mode 100644
index 0000000..647416a
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/租户管理.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/系统接口.jpg b/ruoyi-vue-pro-master-jdk17/.image/系统接口.jpg
new file mode 100644
index 0000000..6d39d42
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/系统接口.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/菜单管理.jpg b/ruoyi-vue-pro-master-jdk17/.image/菜单管理.jpg
new file mode 100644
index 0000000..ad3b797
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/菜单管理.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/表单构建.jpg b/ruoyi-vue-pro-master-jdk17/.image/表单构建.jpg
new file mode 100644
index 0000000..81f0374
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/表单构建.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/角色管理.jpg b/ruoyi-vue-pro-master-jdk17/.image/角色管理.jpg
new file mode 100644
index 0000000..eed776e
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/角色管理.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/访问日志.jpg b/ruoyi-vue-pro-master-jdk17/.image/访问日志.jpg
new file mode 100644
index 0000000..ef301aa
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/访问日志.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/退款订单.jpg b/ruoyi-vue-pro-master-jdk17/.image/退款订单.jpg
new file mode 100644
index 0000000..2c6c6c9
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/退款订单.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/通知公告.jpg b/ruoyi-vue-pro-master-jdk17/.image/通知公告.jpg
new file mode 100644
index 0000000..97bb42f
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/通知公告.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/部门管理.jpg b/ruoyi-vue-pro-master-jdk17/.image/部门管理.jpg
new file mode 100644
index 0000000..6eab233
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/部门管理.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/配置管理.jpg b/ruoyi-vue-pro-master-jdk17/.image/配置管理.jpg
new file mode 100644
index 0000000..0abaec9
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/配置管理.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/链路追踪.jpg b/ruoyi-vue-pro-master-jdk17/.image/链路追踪.jpg
new file mode 100644
index 0000000..12f7aa8
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/链路追踪.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/错误日志.jpg b/ruoyi-vue-pro-master-jdk17/.image/错误日志.jpg
new file mode 100644
index 0000000..eb615ea
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/错误日志.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/错误码管理.jpg b/ruoyi-vue-pro-master-jdk17/.image/错误码管理.jpg
new file mode 100644
index 0000000..ea91dde
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/错误码管理.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/.image/首页.jpg b/ruoyi-vue-pro-master-jdk17/.image/首页.jpg
new file mode 100644
index 0000000..10a7fde
Binary files /dev/null and b/ruoyi-vue-pro-master-jdk17/.image/首页.jpg differ
diff --git a/ruoyi-vue-pro-master-jdk17/LICENSE b/ruoyi-vue-pro-master-jdk17/LICENSE
new file mode 100644
index 0000000..bd9da62
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2021 ruoyi-vue-pro
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/ruoyi-vue-pro-master-jdk17/README.md b/ruoyi-vue-pro-master-jdk17/README.md
new file mode 100644
index 0000000..efdb44c
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/README.md
@@ -0,0 +1,391 @@
+
+
+
+
+
+
+**严肃声明:现在、未来都不会有商业版本,所有代码全部开源!!**
+
+**「我喜欢写代码,乐此不疲」**
+**「我喜欢做开源,以此为乐」**
+
+我 🐶 在上海艰苦奋斗,早中晚在 top3 大厂认真搬砖,夜里为开源做贡献。
+
+如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。
+
+## 🐶 新手必读
+
+* 演示地址【Vue3 + element-plus】:
+* 演示地址【Vue3 + vben(ant-design-vue)】:
+* 演示地址【Vue2 + element-ui】:
+* 启动文档:
+* 视频教程:
+
+## 🐰 版本说明
+
+| 版本 | JDK 8 + Spring Boot 2.7 | JDK 17/21 + Spring Boot 3.2 |
+|---------------------------------------------------------------------|---------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
+| 【完整版】[ruoyi-vue-pro](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master-jdk17/) 分支 |
+| 【精简版】[yudao-boot-mini](https://gitee.com/yudaocode/yudao-boot-mini) | [`master`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master-jdk17/) 分支 |
+
+* 【完整版】:包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP 等功能
+* 【精简版】:只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP 等功能
+
+可参考 [《迁移文档》](https://doc.iocoder.cn/migrate-module/) ,只需要 5-10 分钟,即可将【完整版】按需迁移到【精简版】
+
+## 🐯 平台简介
+
+**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。
+
+> 有任何问题,或者想要的功能,可以在 _Issues_ 中提给艿艿。
+>
+> 😜 给项目点点 Star 吧,这对我们真的很重要!
+
+
+
+* Java 后端:`master` 分支为 JDK 8 + Spring Boot 2.7,`master-jdk17` 分支为 JDK 17/21 + Spring Boot 3.2
+* 管理后台的电脑端:Vue3 提供 `element-plus`、`vben(ant-design-vue)` 两个版本,Vue2 提供 `element-ui` 版本
+* 管理后台的移动端:采用 `uni-app` 方案,一份代码多终端适配,同时支持 APP、小程序、H5!
+* 后端采用 Spring Boot 多模块架构、MySQL + MyBatis Plus、Redis + Redisson
+* 数据库可使用 MySQL、Oracle、PostgreSQL、SQL Server、MariaDB、国产达梦 DM、TiDB 等
+* 消息队列可使用 Event、Redis、RabbitMQ、Kafka、RocketMQ 等
+* 权限认证使用 Spring Security & Token & Redis,支持多终端、多种用户的认证系统,支持 SSO 单点登录
+* 支持加载动态权限菜单,按钮级别权限控制,Redis 缓存提升性能
+* 支持 SaaS 多租户,可自定义每个租户的权限,提供透明化的多租户底层封装
+* 工作流使用 Flowable,支持动态表单、在线设计流程、会签 / 或签、多种任务分配方式
+* 高效率开发,使用代码生成器可以一键生成 Java、Vue 前后端代码、SQL 脚本、接口文档,支持单表、树表、主子表
+* 实时通信,采用 Spring WebSocket 实现,内置 Token 身份校验,支持 WebSocket 集群
+* 集成微信小程序、微信公众号、企业微信、钉钉等三方登陆,集成支付宝、微信等支付与退款
+* 集成阿里云、腾讯云等短信渠道,集成 MinIO、阿里云、腾讯云、七牛云等云存储服务
+* 集成报表设计器、大屏设计器,通过拖拽即可生成酷炫的报表与大屏
+
+## 🐳 项目关系
+
+
+
+三个项目的功能对比,可见社区共同整理的 [国产开源项目对比](https://www.yuque.com/xiatian-bsgny/lm0ec1/wqf8mn) 表格。
+
+### 后端项目
+
+| 项目 | Star | 简介 |
+|-----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|
+| [ruoyi-vue-pro](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [](https://gitee.com/zhijiantianya/ruoyi-vue-pro) [](https://github.com/YunaiV/ruoyi-vue-pro) | 基于 Spring Boot 多模块架构 |
+| [yudao-cloud](https://gitee.com/zhijiantianya/yudao-cloud) | [](https://gitee.com/zhijiantianya/yudao-cloud) [](https://github.com/YunaiV/yudao-cloud) | 基于 Spring Cloud 微服务架构 |
+| [Spring-Boot-Labs](https://gitee.com/yudaocode/SpringBoot-Labs) | [](https://gitee.com/zhijiantianya/yudao-cloud) [](https://github.com/yudaocode/SpringBoot-Labs) | 系统学习 Spring Boot & Cloud 专栏 |
+
+### 前端项目
+
+| 项目 | Star | 简介 |
+|----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
+| [yudao-ui-admin-vue3](https://gitee.com/yudaocode/yudao-ui-admin-vue3) | [](https://gitee.com/yudaocode/yudao-ui-admin-vue3) [](https://github.com/yudaocode/yudao-ui-admin-vue3) | 基于 Vue3 + element-plus 实现的管理后台 |
+| [yudao-ui-admin-vben](https://gitee.com/yudaocode/yudao-ui-admin-vben) | [](https://gitee.com/yudaocode/yudao-ui-admin-vben) [](https://github.com/yudaocode/yudao-ui-admin-vben) | 基于 Vue3 + vben(ant-design-vue) 实现的管理后台 |
+| [yudao-mall-uniapp](https://gitee.com/yudaocode/yudao-mall-uniapp) | [](https://gitee.com/yudaocode/yudao-mall-uniapp) [](https://github.com/yudaocode/yudao-mall-uniapp) | 基于 uni-app 实现的商城小程序 |
+| [yudao-ui-admin-vue2](https://gitee.com/yudaocode/yudao-ui-admin-vue2) | [](https://gitee.com/yudaocode/yudao-ui-admin-vue2) [](https://github.com/yudaocode/yudao-ui-admin-vue2) | 基于 Vue2 + element-ui 实现的管理后台 |
+| [yudao-ui-admin-uniapp](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) | [](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) [](https://github.com/yudaocode/yudao-ui-admin-uniapp) | 基于 Vue2 + element-ui 实现的管理后台 |
+| [yudao-ui-go-view](https://gitee.com/yudaocode/yudao-ui-go-view) | [](https://gitee.com/yudaocode/yudao-ui-go-view) [](https://github.com/yudaocode/yudao-ui-go-view) | 基于 Vue3 + naive-ui 实现的大屏报表 |
+
+## 😎 开源协议
+
+**为什么推荐使用本项目?**
+
+① 本项目采用比 Apache 2.0 更宽松的 [MIT License](https://gitee.com/zhijiantianya/ruoyi-vue-pro/blob/master/LICENSE) 开源协议,个人与企业可 100% 免费使用,不用保留类作者、Copyright 信息。
+
+② 代码全部开源,不会像其他项目一样,只开源部分代码,让你无法了解整个项目的架构设计。[国产开源项目对比](https://www.yuque.com/xiatian-bsgny/lm0ec1/wqf8mn)
+
+
+
+③ 代码整洁、架构整洁,遵循《阿里巴巴 Java 开发手册》规范,代码注释详细,113770 行 Java 代码,42462 行代码注释。
+
+## 🤝 项目外包
+
+我们也是接外包滴,如果你有项目想要外包,可以微信联系【**Aix9975**】。
+
+团队包含专业的项目经理、架构师、前端工程师、后端工程师、测试工程师、运维工程师,可以提供全流程的外包服务。
+
+项目可以是商城、SCRM 系统、OA 系统、物流系统、ERP 系统、CMS 系统、HIS 系统、支付系统、IM 聊天、微信公众号、微信小程序等等。
+
+## 🐼 内置功能
+
+系统内置多种多种业务功能,可以用于快速你的业务系统:
+
+
+
+* 通用模块(必选):系统功能、基础设施
+* 通用模块(可选):工作流程、支付系统、数据报表、会员中心
+* 业务系统(按需):ERP 系统、CRM 系统、商城系统、微信公众号、AI 大模型
+
+> 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。
+>
+> * 额外新增的功能,我们使用 🚀 标记。
+> * 重新实现的功能,我们使用 ⭐️ 标记。
+
+🙂 所有功能,都通过 **单元测试** 保证高质量。
+
+### 系统功能
+
+| | 功能 | 描述 |
+|-----|-------|---------------------------------|
+| | 用户管理 | 用户是系统操作者,该功能主要完成系统用户配置 |
+| ⭐️ | 在线用户 | 当前系统中活跃用户状态监控,支持手动踢下线 |
+| | 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 |
+| | 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等,本地缓存提供性能 |
+| | 部门管理 | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限 |
+| | 岗位管理 | 配置系统用户所属担任职务 |
+| 🚀 | 租户管理 | 配置系统租户,支持 SaaS 场景下的多租户功能 |
+| 🚀 | 租户套餐 | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限 |
+| | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 |
+| 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 |
+| 🚀 | 邮件管理 | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台 |
+| 🚀 | 站内信 | 系统内的消息通知,提供站内信模版、站内信消息 |
+| 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
+| ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 |
+| 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 |
+| | 通知公告 | 系统通知公告信息发布维护 |
+| 🚀 | 敏感词 | 配置系统敏感词,支持标签分组 |
+| 🚀 | 应用管理 | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 |
+| 🚀 | 地区管理 | 展示省份、城市、区镇等城市信息,支持 IP 对应城市 |
+
+
+
+### 工作流程
+
+
+
+基于 Flowable 构建,可支持信创(国产)数据库,满足中国特色流程操作:
+
+| BPMN 设计器 | 钉钉/飞书设计器 |
+|------------------------------|--------------------------------|
+|  |  |
+
+> 历经头部企业生产验证,工作流引擎须标配仿钉钉/飞书 + BPMN 双设计器!!!
+>
+> 前者支持轻量配置简单流程,后者实现复杂场景深度编排
+
+| 功能列表 | 功能描述 | 是否完成 |
+|------------|-------------------------------------------------------------------------------------|------|
+| SIMPLE 设计器 | 仿钉钉/飞书设计器,支持拖拽搭建表单流程,10 分钟快速完成审批流程配置 | ✅ |
+| BPMN 设计器 | 基于 BPMN 标准开发,适配复杂业务场景,满足多层级审批及流程自动化需求 | ✅ |
+| 会签 | 同一个审批节点设置多个人(如 A、B、C 三人,三人会同时收到待办任务),需全部同意之后,审批才可到下一审批节点 | ✅ |
+| 或签 | 同一个审批节点设置多个人,任意一个人处理后,就能进入下一个节点 | ✅ |
+| 依次审批 | (顺序会签)同一个审批节点设置多个人(如 A、B、C 三人),三人按顺序依次收到待办,即 A 先审批,A 提交后 B 才能审批,需全部同意之后,审批才可到下一审批节点 | ✅ |
+| 抄送 | 将审批结果通知给抄送人,同一个审批默认排重,不重复抄送给同一人 | ✅ |
+| 驳回 | (退回)将审批重置发送给某节点,重新审批。可驳回至发起人、上一节点、任意节点 | ✅ |
+| 转办 | A 转给其 B 审批,B 审批后,进入下一节点 | ✅ |
+| 委派 | A 转给其 B 审批,B 审批后,转给 A,A 继续审批后进入下一节点 | ✅ |
+| 加签 | 允许当前审批人根据需要,自行增加当前节点的审批人,支持向前、向后加签 | ✅ |
+| 减签 | (取消加签)在当前审批人操作之前,减少审批人 | ✅ |
+| 撤销 | (取消流程)流程发起人,可以对流程进行撤销处理 | ✅ |
+| 终止 | 系统管理员,在任意节点终止流程实例 | ✅ |
+| 表单权限 | 支持拖拉拽配置表单,每个审批节点可配置只读、编辑、隐藏权限 | ✅ |
+| 超时审批 | 配置超时审批时间,超时后自动触发审批通过、不通过、驳回等操作 | ✅ |
+| 自动提醒 | 配置提醒时间,到达时间后自动触发短信、邮箱、站内信等通知提醒,支持自定义重复提醒频次 | ✅ |
+| 父子流程 | 主流程设置子流程节点,子流程节点会自动触发子流程。子流程结束后,主流程才会执行(继续往下下执行),支持同步子流程、异步子流程 | ✅ |
+| 条件分支 | (排它分支)用于在流程中实现决策,即根据条件选择一个分支执行 | ✅ |
+| 并行分支 | 允许将流程分成多条分支,不进行条件判断,所有分支都会执行 | ✅ |
+| 包容分支 | (条件分支 + 并行分支的结合体)允许基于条件选择多条分支执行,但如果没有任何一个分支满足条件,则可以选择默认分支 | ✅ |
+| 路由分支 | 根据条件选择一个分支执行(重定向到指定配置节点),也可以选择默认分支执行(继续往下执行) | ✅ |
+| 触发节点 | 执行到该节点,触发 HTTP 请求、HTTP 回调、更新数据、删除数据等 | ✅ |
+| 延迟节点 | 执行到该节点,审批等待一段时间再执行,支持固定时长、固定日期等 | ✅ |
+| 拓展设置 | 流程前置/后置通知,节点(任务)前置、后置通知,流程报表,自动审批去重,自定流程编号、标题、摘要,流程报表等 | ✅ |
+
+### 支付系统
+
+| | 功能 | 描述 |
+|-----|------|---------------------------|
+| 🚀 | 应用信息 | 配置商户的应用信息,对接支付宝、微信等多个支付渠道 |
+| 🚀 | 支付订单 | 查看用户发起的支付宝、微信等的【支付】订单 |
+| 🚀 | 退款订单 | 查看用户发起的支付宝、微信等的【退款】订单 |
+| 🚀 | 回调通知 | 查看支付回调业务的【支付】【退款】的通知结果 |
+| 🚀 | 接入示例 | 提供接入支付系统的【支付】【退款】的功能实战 |
+
+### 基础设施
+
+| | 功能 | 描述 |
+|-----|-----------|----------------------------------------------|
+| 🚀 | 代码生成 | 前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载 |
+| 🚀 | 系统接口 | 基于 Swagger 自动生成相关的 RESTful API 接口文档 |
+| 🚀 | 数据库文档 | 基于 Screw 自动生成数据库文档,支持导出 Word、HTML、MD 格式 |
+| | 表单构建 | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件 |
+| 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 |
+| ⭐️ | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 |
+| 🚀 | 文件服务 | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等 |
+| 🚀 | WebSocket | 提供 WebSocket 接入示例,支持一对一、一对多发送方式 |
+| 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
+| | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 |
+| | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
+| 🚀 | 消息队列 | 基于 Redis 实现消息队列,Stream 提供集群消费,Pub/Sub 提供广播消费 |
+| 🚀 | Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
+| 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 |
+| 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 |
+| 🚀 | 服务保障 | 基于 Redis 实现分布式锁、幂等、限流功能,满足高并发场景 |
+| 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 |
+| 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 |
+
+
+
+### 数据报表
+
+| | 功能 | 描述 |
+|-----|-------|--------------------|
+| 🚀 | 报表设计器 | 支持数据报表、图形报表、打印设计等 |
+| 🚀 | 大屏设计器 | 拖拽生成数据大屏,内置几十种图表组件 |
+
+### 微信公众号
+
+| | 功能 | 描述 |
+|-----|--------|-------------------------------|
+| 🚀 | 账号管理 | 配置接入的微信公众号,可支持多个公众号 |
+| 🚀 | 数据统计 | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据 |
+| 🚀 | 粉丝管理 | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 |
+| 🚀 | 消息管理 | 查看粉丝发送的消息列表,可主动回复粉丝消息 |
+| 🚀 | 自动回复 | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 |
+| 🚀 | 标签管理 | 对公众号的标签进行创建、查询、修改、删除等操作 |
+| 🚀 | 菜单管理 | 自定义公众号的菜单,也可以从公众号同步菜单 |
+| 🚀 | 素材管理 | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 |
+| 🚀 | 图文草稿箱 | 新增常用的图文素材到草稿箱,可发布到公众号 |
+| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作 |
+
+### 商城系统
+
+演示地址:
+
+
+
+
+
+### 会员中心
+
+| | 功能 | 描述 |
+|-----|------|----------------------------------|
+| 🚀 | 会员管理 | 会员是 C 端的消费者,该功能用于会员的搜索与管理 |
+| 🚀 | 会员标签 | 对会员的标签进行创建、查询、修改、删除等操作 |
+| 🚀 | 会员等级 | 对会员的等级、成长值进行管理,可用于订单折扣等会员权益 |
+| 🚀 | 会员分组 | 对会员进行分组,用于用户画像、内容推送等运营手段 |
+| 🚀 | 积分签到 | 回馈给签到、消费等行为的积分,会员可订单抵现、积分兑换等途径消耗 |
+
+### ERP 系统
+
+演示地址:
+
+
+
+### CRM 系统
+
+演示地址:
+
+
+
+### AI 大模型
+
+演示地址:
+
+
+
+
+
+## 🐨 技术栈
+
+### 模块
+
+| 项目 | 说明 |
+|-----------------------|--------------------|
+| `yudao-dependencies` | Maven 依赖版本管理 |
+| `yudao-framework` | Java 框架拓展 |
+| `yudao-server` | 管理后台 + 用户 APP 的服务端 |
+| `yudao-module-system` | 系统功能的 Module 模块 |
+| `yudao-module-member` | 会员中心的 Module 模块 |
+| `yudao-module-infra` | 基础设施的 Module 模块 |
+| `yudao-module-bpm` | 工作流程的 Module 模块 |
+| `yudao-module-pay` | 支付系统的 Module 模块 |
+| `yudao-module-mall` | 商城系统的 Module 模块 |
+| `yudao-module-erp` | ERP 系统的 Module 模块 |
+| `yudao-module-crm` | CRM 系统的 Module 模块 |
+| `yudao-module-ai` | AI 大模型的 Module 模块 |
+| `yudao-module-mp` | 微信公众号的 Module 模块 |
+| `yudao-module-report` | 大屏报表 Module 模块 |
+
+### 框架
+
+| 框架 | 说明 | 版本 | 学习指南 |
+|---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------|
+| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 3.4.5 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
+| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | |
+| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.23 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
+| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.7 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
+| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 4.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
+| [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 /7.0 | |
+| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.32.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
+| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 6.1.10 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
+| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 6.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
+| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 8.0.1 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
+| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 7.0.0 | [文档](https://doc.iocoder.cn/bpm/) |
+| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
+| [Springdoc](https://springdoc.org/) | Swagger 文档 | 2.3.0 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
+| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 9.0.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
+| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 3.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
+| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.17.1 | |
+| [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.6.3 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
+| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.34 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
+| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.10.1 | - |
+| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 5.7.0 | - |
+
+## 🐷 演示图
+
+### 系统功能
+
+| 模块 | biu | biu | biu |
+|----------|-----------------------------|---------------------------|--------------------------|
+| 登录 & 首页 |  |  |  |
+| 用户 & 应用 |  |  |  |
+| 租户 & 套餐 |  |  | - |
+| 部门 & 岗位 |  |  | - |
+| 菜单 & 角色 |  |  | - |
+| 审计日志 |  |  | - |
+| 短信 |  |  |  |
+| 字典 & 敏感词 |  |  |  |
+| 错误码 & 通知 |  |  | - |
+
+### 工作流程
+
+| 模块 | biu | biu | biu |
+|---------|---------------------------------|---------------------------------|---------------------------------|
+| 流程模型 |  |  |  |
+| 表单 & 分组 |  |  | - |
+| 我的流程 |  |  |  |
+| 待办 & 已办 |  |  |  |
+| OA 请假 |  |  |  |
+
+### 基础设施
+
+| 模块 | biu | biu | biu |
+|---------------|-------------------------------|-----------------------------|---------------------------|
+| 代码生成 |  |  | - |
+| 文档 |  |  | - |
+| 文件 & 配置 |  |  |  |
+| 定时任务 |  |  | - |
+| API 日志 |  |  | - |
+| MySQL & Redis |  |  | - |
+| 监控平台 |  |  |  |
+
+### 支付系统
+
+| 模块 | biu | biu | biu |
+|---------|---------------------------|---------------------------------|---------------------------------|
+| 商家 & 应用 |  |  |  |
+| 支付 & 退款 |  |  | --- |
+### 数据报表
+
+| 模块 | biu | biu | biu |
+|-------|---------------------------------|---------------------------------|---------------------------------------|
+| 报表设计器 |  |  |  |
+| 大屏设计器 |  |  |  |
+
+### 移动端(管理后台)
+
+| biu | biu | biu |
+|----------------------------------|----------------------------------|----------------------------------|
+|  |  |  |
+|  |  |  |
+|  |  |  |
+
+目前已经实现登录、我的、工作台、编辑资料、头像修改、密码修改、常见问题、关于我们等基础功能。
diff --git a/ruoyi-vue-pro-master-jdk17/lombok.config b/ruoyi-vue-pro-master-jdk17/lombok.config
new file mode 100644
index 0000000..a8e8ce6
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/lombok.config
@@ -0,0 +1,4 @@
+config.stopBubbling = true
+lombok.tostring.callsuper=CALL
+lombok.equalsandhashcode.callsuper=CALL
+lombok.accessors.chain=true
diff --git a/ruoyi-vue-pro-master-jdk17/pom.xml b/ruoyi-vue-pro-master-jdk17/pom.xml
new file mode 100644
index 0000000..36a4802
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/pom.xml
@@ -0,0 +1,172 @@
+
+
+ 4.0.0
+ cn.iocoder.boot
+ yudao
+ ${revision}
+ pom
+
+ yudao-dependencies
+ yudao-framework
+
+ yudao-server
+
+ yudao-module-system
+ yudao-module-infra
+ yudao-module-member
+ yudao-module-bpm
+ yudao-module-report
+ yudao-module-iot
+
+
+ ${project.artifactId}
+ 芋道项目基础脚手架
+ https://github.com/YunaiV/ruoyi-vue-pro
+
+
+ 2.6.1-SNAPSHOT
+
+ 21
+ ${java.version}
+ ${java.version}
+ 3.2.2
+ 3.14.0
+ 1.6.0
+
+ 1.18.38
+ 3.4.5
+ 1.6.3
+ UTF-8
+
+
+
+
+
+ cn.iocoder.boot
+ yudao-dependencies
+ ${revision}
+ pom
+ import
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring.boot.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ 0.2.0
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+ false
+
+ -parameters
+
+
+
+
+ org.codehaus.mojo
+ flatten-maven-plugin
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ flatten-maven-plugin
+ ${flatten-maven-plugin.version}
+
+ oss
+ true
+
+
+
+
+ flatten
+
+ flatten
+ process-resources
+
+
+
+ clean
+
+ flatten.clean
+ clean
+
+
+
+
+
+
+
+
+
+ huaweicloud
+ huawei
+ https://mirrors.huaweicloud.com/repository/maven/
+
+
+ aliyunmaven
+ aliyun
+ https://maven.aliyun.com/repository/public
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+ false
+
+
+
+ spring-snapshots
+ Spring Snapshots
+ https://repo.spring.io/snapshot
+
+ false
+
+
+
+
+
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-dependencies/pom.xml b/ruoyi-vue-pro-master-jdk17/yudao-dependencies/pom.xml
new file mode 100644
index 0000000..3f05da8
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-dependencies/pom.xml
@@ -0,0 +1,674 @@
+
+
+ 4.0.0
+
+ cn.iocoder.boot
+ yudao-dependencies
+ ${revision}
+ pom
+
+ ${project.artifactId}
+ 基础 bom 文件,管理整个项目的依赖版本
+ https://github.com/YunaiV/ruoyi-vue-pro
+
+
+ 2.6.1-SNAPSHOT
+ 1.6.0
+
+ 3.4.5
+
+ 2.8.3
+ 4.6.0
+
+ 1.2.24
+ 3.5.19
+ 3.5.10.1
+ 4.3.1
+ 1.4.13
+ 3.0.6
+ 3.41.0
+ 8.1.3.140
+ 8.6.0
+ 5.1.0
+ 3.3.3
+
+ 2.3.2
+
+ 2.2.7
+
+ 9.0.0
+ 3.4.5
+ 0.33.0
+
+ 8.0.2.RELEASE
+ 1.1.8
+ 5.2.0
+
+ 7.0.1
+
+ 1.4.0
+ 1.18.3
+ 1.18.38
+ 1.6.3
+ 5.8.35
+ 6.0.0-M19
+ 4.0.3
+ 2.4.1
+ 1.2.83
+ 33.4.8-jre
+ 2.14.5
+ 3.11.1
+ 0.1.55
+ 3.1.0
+ 2.7.0
+ 3.0.6
+ 4.1.118.Final
+ 1.2.5
+ 0.9.0
+ 4.5.13
+
+ 2.17.0
+ 1.27.1
+ 2.30.14
+ 1.16.7
+ 1.4.0
+ 2.0.0
+ 1.9.5
+ 4.7.5.B
+
+
+
+
+
+
+ io.netty
+ netty-bom
+ ${netty.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring.boot.version}
+ pom
+ import
+
+
+
+
+ io.github.mouzt
+ bizlog-sdk
+ ${bizlog-sdk.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-biz-tenant
+ ${revision}
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-biz-data-permission
+ ${revision}
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-biz-ip
+ ${revision}
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring.boot.version}
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-web
+ ${revision}
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-security
+ ${revision}
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-websocket
+ ${revision}
+
+
+
+ com.github.xingfudeshi
+ knife4j-openapi3-jakarta-spring-boot-starter
+ ${knife4j.version}
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-api
+ ${springdoc.version}
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-mybatis
+ ${revision}
+
+
+
+ com.alibaba
+ druid-spring-boot-3-starter
+ ${druid.version}
+
+
+
+ org.mybatis
+ mybatis
+ ${mybatis.version}
+
+
+ com.baomidou
+ mybatis-plus-spring-boot3-starter
+ ${mybatis-plus.version}
+
+
+ com.baomidou
+ mybatis-plus-jsqlparser
+ ${mybatis-plus.version}
+
+
+ com.baomidou
+ mybatis-plus-generator
+ ${mybatis-plus.version}
+
+
+ com.baomidou
+ dynamic-datasource-spring-boot3-starter
+ ${dynamic-datasource.version}
+
+
+ com.github.yulichang
+ mybatis-plus-join-boot-starter
+ ${mybatis-plus-join.version}
+
+
+
+ com.fhs-opensource
+ easy-trans-spring-boot-starter
+ ${easy-trans.version}
+
+
+ org.springframework
+ spring-context
+
+
+ org.springframework.cloud
+ spring-cloud-commons
+
+
+
+
+ com.fhs-opensource
+ easy-trans-mybatis-plus-extend
+ ${easy-trans.version}
+
+
+ com.fhs-opensource
+ easy-trans-anno
+ ${easy-trans.version}
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-redis
+ ${revision}
+
+
+
+ org.redisson
+ redisson-spring-boot-starter
+ ${redisson.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+
+ com.dameng
+ DmJdbcDriver18
+ ${dm8.jdbc.version}
+
+
+
+ org.opengauss
+ opengauss-jdbc
+ ${opengauss.jdbc.version}
+
+
+
+ cn.com.kingbase
+ kingbase8
+ ${kingbase.jdbc.version}
+
+
+
+ com.taosdata.jdbc
+ taos-jdbcdriver
+ ${taos.version}
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-job
+ ${revision}
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-mq
+ ${revision}
+
+
+ org.apache.rocketmq
+ rocketmq-spring-boot-starter
+ ${rocketmq-spring.version}
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-protection
+ ${revision}
+
+
+
+ com.baomidou
+ lock4j-redisson-spring-boot-starter
+ ${lock4j.version}
+
+
+ redisson-spring-boot-starter
+ org.redisson
+
+
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-monitor
+ ${revision}
+
+
+
+ org.apache.skywalking
+ apm-toolkit-trace
+ ${skywalking.version}
+
+
+ org.apache.skywalking
+ apm-toolkit-logback-1.x
+ ${skywalking.version}
+
+
+ org.apache.skywalking
+ apm-toolkit-opentracing
+ ${skywalking.version}
+
+
+
+
+
+
+
+
+
+
+
+
+ io.opentracing
+ opentracing-api
+ ${opentracing.version}
+
+
+ io.opentracing
+ opentracing-util
+ ${opentracing.version}
+
+
+ io.opentracing
+ opentracing-noop
+ ${opentracing.version}
+
+
+
+ de.codecentric
+ spring-boot-admin-starter-server
+ ${spring-boot-admin.version}
+
+
+ de.codecentric
+ spring-boot-admin-server-cloud
+
+
+
+
+ de.codecentric
+ spring-boot-admin-starter-client
+ ${spring-boot-admin.version}
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-test
+ ${revision}
+ test
+
+
+
+ org.mockito
+ mockito-inline
+ ${mockito-inline.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring.boot.version}
+
+
+ asm
+ org.ow2.asm
+
+
+ org.mockito
+ mockito-core
+
+
+
+
+
+
+ org.flowable
+ flowable-spring-boot-starter-process
+ ${flowable.version}
+
+
+ org.flowable
+ flowable-spring-boot-starter-actuator
+ ${flowable.version}
+
+
+
+
+
+ cn.iocoder.boot
+ yudao-common
+ ${revision}
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-excel
+ ${revision}
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+ org.mapstruct
+ mapstruct
+ ${mapstruct.version}
+
+
+ org.mapstruct
+ mapstruct-jdk8
+ ${mapstruct.version}
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+ cn.hutool
+ hutool-all
+ ${hutool-5.version}
+
+
+ org.dromara.hutool
+ hutool-extra
+ ${hutool-6.version}
+
+
+
+ com.alibaba
+ easyexcel
+ ${easyexcel.version}
+
+
+ commons-io
+ commons-io
+ ${commons-io.version}
+
+
+ org.apache.commons
+ commons-compress
+ ${commons-compress.version}
+
+
+ org.apache.tika
+ tika-core
+ ${tika-core.version}
+
+
+
+ org.apache.velocity
+ velocity-engine-core
+ ${velocity.version}
+
+
+
+ com.alibaba
+ fastjson
+ ${fastjson.version}
+
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+
+ com.alibaba
+ transmittable-thread-local
+ ${transmittable-thread-local.version}
+
+
+
+ commons-net
+ commons-net
+ ${commons-net.version}
+
+
+
+ com.jcraft
+ jsch
+ ${jsch.version}
+
+
+
+ com.anji-plus
+ captcha-spring-boot-starter
+ ${anji-plus-captcha.version}
+
+
+
+ org.lionsoul
+ ip2region
+ ${ip2region.version}
+
+
+
+ org.jsoup
+ jsoup
+ ${jsoup.version}
+
+
+
+
+ software.amazon.awssdk
+ s3
+ ${awssdk.version}
+
+
+
+ me.zhyd.oauth
+ JustAuth
+ ${justauth.version}
+
+
+ com.xkcoding.justauth
+ justauth-spring-boot-starter
+ ${justauth-starter.version}
+
+
+
+ cn.hutool
+ hutool-core
+
+
+
+
+
+ com.github.binarywang
+ weixin-java-pay
+ ${weixin-java.version}
+
+
+ com.github.binarywang
+ wx-java-mp-spring-boot-starter
+ ${weixin-java.version}
+
+
+ com.github.binarywang
+ wx-java-miniapp-spring-boot-starter
+ ${weixin-java.version}
+
+
+
+
+ org.jeecgframework.jimureport
+ jimureport-spring-boot3-starter-fastjson2
+ ${jimureport.version}
+
+
+ org.jeecgframework.jimureport
+ jimubi-spring-boot3-starter
+ ${jimubi.version}
+
+
+ com.github.jsqlparser
+ jsqlparser
+
+
+
+
+
+
+ org.pf4j
+ pf4j-spring
+ ${pf4j-spring.version}
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+
+
+
+
+ io.vertx
+ vertx-core
+ ${vertx.version}
+
+
+ io.vertx
+ vertx-web
+ ${vertx.version}
+
+
+ io.vertx
+ vertx-mqtt
+ ${vertx.version}
+
+
+
+
+ org.eclipse.paho
+ org.eclipse.paho.client.mqttv3
+ ${mqtt.version}
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ flatten-maven-plugin
+ ${flatten-maven-plugin.version}
+
+ bom
+ true
+
+
+
+
+ flatten
+
+ flatten
+ process-resources
+
+
+
+ clean
+
+ flatten.clean
+ clean
+
+
+
+
+
+
+
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/pom.xml b/ruoyi-vue-pro-master-jdk17/yudao-framework/pom.xml
new file mode 100644
index 0000000..76e6740
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/pom.xml
@@ -0,0 +1,46 @@
+
+
+ 4.0.0
+
+ yudao
+ cn.iocoder.boot
+ ${revision}
+
+ pom
+
+ yudao-common
+ yudao-spring-boot-starter-mybatis
+ yudao-spring-boot-starter-redis
+ yudao-spring-boot-starter-web
+ yudao-spring-boot-starter-security
+ yudao-spring-boot-starter-websocket
+
+ yudao-spring-boot-starter-monitor
+ yudao-spring-boot-starter-protection
+ yudao-spring-boot-starter-job
+ yudao-spring-boot-starter-mq
+
+ yudao-spring-boot-starter-excel
+ yudao-spring-boot-starter-test
+
+ yudao-spring-boot-starter-biz-tenant
+ yudao-spring-boot-starter-biz-data-permission
+ yudao-spring-boot-starter-biz-ip
+
+
+ yudao-framework
+
+ 该包是技术组件,每个子包,代表一个组件。每个组件包括两部分:
+ 1. core 包:是该组件的核心封装
+ 2. config 包:是该组件基于 Spring 的配置
+
+ 技术组件,也分成两类:
+ 1. 框架组件:和我们熟悉的 MyBatis、Redis 等等的拓展
+ 2. 业务组件:和业务相关的组件的封装,例如说数据字典、操作日志等等。
+ 如果是业务组件,Maven 名字会包含 biz
+
+ https://github.com/YunaiV/ruoyi-vue-pro
+
+
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/pom.xml b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/pom.xml
new file mode 100644
index 0000000..b28da08
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/pom.xml
@@ -0,0 +1,149 @@
+
+
+
+ cn.iocoder.boot
+ yudao-framework
+ ${revision}
+
+ 4.0.0
+ yudao-common
+ jar
+
+ ${project.artifactId}
+ 定义基础 pojo 类、枚举、工具类等等
+ https://github.com/YunaiV/ruoyi-vue-pro
+
+
+
+
+ org.springframework
+ spring-core
+ provided
+
+
+ org.springframework
+ spring-expression
+ provided
+
+
+ org.springframework
+ spring-aop
+ provided
+
+
+ org.aspectj
+ aspectjweaver
+ provided
+
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+
+ org.springframework
+ spring-web
+ provided
+
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ provided
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-api
+ provided
+
+
+
+
+ org.apache.skywalking
+ apm-toolkit-trace
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+ org.mapstruct
+ mapstruct
+
+
+ org.mapstruct
+ mapstruct-jdk8
+
+
+ org.mapstruct
+ mapstruct-processor
+
+
+
+ com.google.guava
+ guava
+ provided
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ provided
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ provided
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ provided
+
+
+
+ org.slf4j
+ slf4j-api
+ provided
+
+
+
+ jakarta.validation
+ jakarta.validation-api
+ provided
+
+
+
+ cn.hutool
+ hutool-all
+
+
+
+ com.alibaba
+ transmittable-thread-local
+
+
+
+ com.fhs-opensource
+ easy-trans-anno
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/ApiAccessLogCommonApi.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/ApiAccessLogCommonApi.java
new file mode 100644
index 0000000..efa9ca1
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/ApiAccessLogCommonApi.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.framework.common.biz.infra.logger;
+
+import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO;
+import jakarta.validation.Valid;
+import org.springframework.scheduling.annotation.Async;
+
+/**
+ * API 访问日志的 API 接口
+ *
+ * @author 芋道源码
+ */
+public interface ApiAccessLogCommonApi {
+
+ /**
+ * 创建 API 访问日志
+ *
+ * @param createDTO 创建信息
+ */
+ void createApiAccessLog(@Valid ApiAccessLogCreateReqDTO createDTO);
+
+ /**
+ * 【异步】创建 API 访问日志
+ *
+ * @param createDTO 访问日志 DTO
+ */
+ @Async
+ default void createApiAccessLogAsync(ApiAccessLogCreateReqDTO createDTO) {
+ createApiAccessLog(createDTO);
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/ApiErrorLogCommonApi.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/ApiErrorLogCommonApi.java
new file mode 100644
index 0000000..98743be
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/ApiErrorLogCommonApi.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.framework.common.biz.infra.logger;
+
+import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO;
+
+import jakarta.validation.Valid;
+import org.springframework.scheduling.annotation.Async;
+
+/**
+ * API 错误日志的 API 接口
+ *
+ * @author 芋道源码
+ */
+public interface ApiErrorLogCommonApi {
+
+ /**
+ * 创建 API 错误日志
+ *
+ * @param createDTO 创建信息
+ */
+ void createApiErrorLog(@Valid ApiErrorLogCreateReqDTO createDTO);
+
+ /**
+ * 【异步】创建 API 异常日志
+ *
+ * @param createDTO 异常日志 DTO
+ */
+ @Async
+ default void createApiErrorLogAsync(ApiErrorLogCreateReqDTO createDTO) {
+ createApiErrorLog(createDTO);
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/dto/ApiAccessLogCreateReqDTO.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/dto/ApiAccessLogCreateReqDTO.java
new file mode 100644
index 0000000..b579cd3
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/dto/ApiAccessLogCreateReqDTO.java
@@ -0,0 +1,103 @@
+package cn.iocoder.yudao.framework.common.biz.infra.logger.dto;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * API 访问日志
+ *
+ * @author 芋道源码
+ */
+@Data
+public class ApiAccessLogCreateReqDTO {
+
+ /**
+ * 链路追踪编号
+ */
+ private String traceId;
+ /**
+ * 用户编号
+ */
+ private Long userId;
+ /**
+ * 用户类型
+ */
+ private Integer userType;
+ /**
+ * 应用名
+ */
+ @NotNull(message = "应用名不能为空")
+ private String applicationName;
+
+ /**
+ * 请求方法名
+ */
+ @NotNull(message = "http 请求方法不能为空")
+ private String requestMethod;
+ /**
+ * 访问地址
+ */
+ @NotNull(message = "访问地址不能为空")
+ private String requestUrl;
+ /**
+ * 请求参数
+ */
+ private String requestParams;
+ /**
+ * 响应结果
+ */
+ private String responseBody;
+ /**
+ * 用户 IP
+ */
+ @NotNull(message = "ip 不能为空")
+ private String userIp;
+ /**
+ * 浏览器 UA
+ */
+ @NotNull(message = "User-Agent 不能为空")
+ private String userAgent;
+
+ /**
+ * 操作模块
+ */
+ private String operateModule;
+ /**
+ * 操作名
+ */
+ private String operateName;
+ /**
+ * 操作分类
+ *
+ * 枚举,参见 OperateTypeEnum 类
+ */
+ private Integer operateType;
+
+ /**
+ * 开始请求时间
+ */
+ @NotNull(message = "开始请求时间不能为空")
+ private LocalDateTime beginTime;
+ /**
+ * 结束请求时间
+ */
+ @NotNull(message = "结束请求时间不能为空")
+ private LocalDateTime endTime;
+ /**
+ * 执行时长,单位:毫秒
+ */
+ @NotNull(message = "执行时长不能为空")
+ private Integer duration;
+ /**
+ * 结果码
+ */
+ @NotNull(message = "错误码不能为空")
+ private Integer resultCode;
+ /**
+ * 结果提示
+ */
+ private String resultMsg;
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/dto/ApiErrorLogCreateReqDTO.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/dto/ApiErrorLogCreateReqDTO.java
new file mode 100644
index 0000000..ae5cd1d
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/logger/dto/ApiErrorLogCreateReqDTO.java
@@ -0,0 +1,107 @@
+package cn.iocoder.yudao.framework.common.biz.infra.logger.dto;
+
+import lombok.Data;
+
+import jakarta.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+/**
+ * API 错误日志
+ *
+ * @author 芋道源码
+ */
+@Data
+public class ApiErrorLogCreateReqDTO {
+
+ /**
+ * 链路编号
+ */
+ private String traceId;
+ /**
+ * 账号编号
+ */
+ private Long userId;
+ /**
+ * 用户类型
+ */
+ private Integer userType;
+ /**
+ * 应用名
+ */
+ @NotNull(message = "应用名不能为空")
+ private String applicationName;
+
+ /**
+ * 请求方法名
+ */
+ @NotNull(message = "http 请求方法不能为空")
+ private String requestMethod;
+ /**
+ * 访问地址
+ */
+ @NotNull(message = "访问地址不能为空")
+ private String requestUrl;
+ /**
+ * 请求参数
+ */
+ @NotNull(message = "请求参数不能为空")
+ private String requestParams;
+ /**
+ * 用户 IP
+ */
+ @NotNull(message = "ip 不能为空")
+ private String userIp;
+ /**
+ * 浏览器 UA
+ */
+ @NotNull(message = "User-Agent 不能为空")
+ private String userAgent;
+
+ /**
+ * 异常时间
+ */
+ @NotNull(message = "异常时间不能为空")
+ private LocalDateTime exceptionTime;
+ /**
+ * 异常名
+ */
+ @NotNull(message = "异常名不能为空")
+ private String exceptionName;
+ /**
+ * 异常发生的类全名
+ */
+ @NotNull(message = "异常发生的类全名不能为空")
+ private String exceptionClassName;
+ /**
+ * 异常发生的类文件
+ */
+ @NotNull(message = "异常发生的类文件不能为空")
+ private String exceptionFileName;
+ /**
+ * 异常发生的方法名
+ */
+ @NotNull(message = "异常发生的方法名不能为空")
+ private String exceptionMethodName;
+ /**
+ * 异常发生的方法所在行
+ */
+ @NotNull(message = "异常发生的方法所在行不能为空")
+ private Integer exceptionLineNumber;
+ /**
+ * 异常的栈轨迹异常的栈轨迹
+ */
+ @NotNull(message = "异常的栈轨迹不能为空")
+ private String exceptionStackTrace;
+ /**
+ * 异常导致的根消息
+ */
+ @NotNull(message = "异常导致的根消息不能为空")
+ private String exceptionRootCauseMessage;
+ /**
+ * 异常导致的消息
+ */
+ @NotNull(message = "异常导致的消息不能为空")
+ private String exceptionMessage;
+
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/package-info.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/package-info.java
new file mode 100644
index 0000000..d7d8a3b
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/infra/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 针对 infra 模块的 api 包
+ */
+package cn.iocoder.yudao.framework.common.biz.infra;
\ No newline at end of file
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/package-info.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/package-info.java
new file mode 100644
index 0000000..2134e48
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 特殊:用于 framework 下,starter 需要调用 biz 业务模块的接口定义!
+ */
+package cn.iocoder.yudao.framework.common.biz;
\ No newline at end of file
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/dict/DictDataCommonApi.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/dict/DictDataCommonApi.java
new file mode 100644
index 0000000..92c87af
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/dict/DictDataCommonApi.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.framework.common.biz.system.dict;
+
+import cn.iocoder.yudao.framework.common.biz.system.dict.dto.DictDataRespDTO;
+
+import java.util.List;
+
+/**
+ * 字典数据 API 接口
+ *
+ * @author 芋道源码
+ */
+public interface DictDataCommonApi {
+
+ /**
+ * 获得指定字典类型的字典数据列表
+ *
+ * @param dictType 字典类型
+ * @return 字典数据列表
+ */
+ List getDictDataList(String dictType);
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/dict/dto/DictDataRespDTO.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/dict/dto/DictDataRespDTO.java
new file mode 100644
index 0000000..bf32e7e
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/dict/dto/DictDataRespDTO.java
@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.framework.common.biz.system.dict.dto;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import lombok.Data;
+
+/**
+ * 字典数据 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class DictDataRespDTO {
+
+ /**
+ * 字典标签
+ */
+ private String label;
+ /**
+ * 字典值
+ */
+ private String value;
+ /**
+ * 字典类型
+ */
+ private String dictType;
+ /**
+ * 状态
+ *
+ * 枚举 {@link CommonStatusEnum}
+ */
+ private Integer status;
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/logger/OperateLogCommonApi.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/logger/OperateLogCommonApi.java
new file mode 100644
index 0000000..96753e0
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/logger/OperateLogCommonApi.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.framework.common.biz.system.logger;
+
+import cn.iocoder.yudao.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO;
+import jakarta.validation.Valid;
+import org.springframework.scheduling.annotation.Async;
+
+/**
+ * 操作日志 API 接口
+ *
+ * @author 芋道源码
+ */
+public interface OperateLogCommonApi {
+
+ /**
+ * 创建操作日志
+ *
+ * @param createReqDTO 请求
+ */
+ void createOperateLog(@Valid OperateLogCreateReqDTO createReqDTO);
+
+ /**
+ * 【异步】创建操作日志
+ *
+ * @param createReqDTO 请求
+ */
+ @Async
+ default void createOperateLogAsync(OperateLogCreateReqDTO createReqDTO) {
+ createOperateLog(createReqDTO);
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/logger/dto/OperateLogCreateReqDTO.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/logger/dto/OperateLogCreateReqDTO.java
new file mode 100644
index 0000000..a2d7990
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/logger/dto/OperateLogCreateReqDTO.java
@@ -0,0 +1,84 @@
+package cn.iocoder.yudao.framework.common.biz.system.logger.dto;
+
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * 系统操作日志 Create Request DTO
+ *
+ * @author HUIHUI
+ */
+@Data
+public class OperateLogCreateReqDTO {
+
+ /**
+ * 链路追踪编号
+ *
+ * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
+ */
+ private String traceId;
+ /**
+ * 用户编号
+ *
+ * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
+ */
+ @NotNull(message = "用户编号不能为空")
+ private Long userId;
+ /**
+ * 用户类型
+ *
+ * 关联 {@link UserTypeEnum}
+ */
+ @NotNull(message = "用户类型不能为空")
+ private Integer userType;
+ /**
+ * 操作模块类型
+ */
+ @NotEmpty(message = "操作模块类型不能为空")
+ private String type;
+ /**
+ * 操作名
+ */
+ @NotEmpty(message = "操作名不能为空")
+ private String subType;
+ /**
+ * 操作模块业务编号
+ */
+ @NotNull(message = "操作模块业务编号不能为空")
+ private Long bizId;
+ /**
+ * 操作内容,记录整个操作的明细
+ * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
+ */
+ @NotEmpty(message = "操作内容不能为空")
+ private String action;
+ /**
+ * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
+ * 例如说,记录订单编号,{ orderId: "1"}
+ */
+ private String extra;
+
+ /**
+ * 请求方法名
+ */
+ @NotEmpty(message = "请求方法名不能为空")
+ private String requestMethod;
+ /**
+ * 请求地址
+ */
+ @NotEmpty(message = "请求地址不能为空")
+ private String requestUrl;
+ /**
+ * 用户 IP
+ */
+ @NotEmpty(message = "用户 IP 不能为空")
+ private String userIp;
+ /**
+ * 浏览器 UA
+ */
+ @NotEmpty(message = "浏览器 UA 不能为空")
+ private String userAgent;
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/OAuth2TokenCommonApi.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/OAuth2TokenCommonApi.java
new file mode 100644
index 0000000..c23d936
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/OAuth2TokenCommonApi.java
@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.framework.common.biz.system.oauth2;
+
+import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
+import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
+import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO;
+
+import jakarta.validation.Valid;
+
+/**
+ * OAuth2.0 Token API 接口
+ *
+ * @author 芋道源码
+ */
+public interface OAuth2TokenCommonApi {
+
+ /**
+ * 创建访问令牌
+ *
+ * @param reqDTO 访问令牌的创建信息
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenRespDTO createAccessToken(@Valid OAuth2AccessTokenCreateReqDTO reqDTO);
+
+ /**
+ * 校验访问令牌
+ *
+ * @param accessToken 访问令牌
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken);
+
+ /**
+ * 移除访问令牌
+ *
+ * @param accessToken 访问令牌
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenRespDTO removeAccessToken(String accessToken);
+
+ /**
+ * 刷新访问令牌
+ *
+ * @param refreshToken 刷新令牌
+ * @param clientId 客户端编号
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, String clientId);
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java
new file mode 100644
index 0000000..a3c70ee
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.framework.common.biz.system.oauth2.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * OAuth2.0 访问令牌的校验 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class OAuth2AccessTokenCheckRespDTO implements Serializable {
+
+ /**
+ * 用户编号
+ */
+ private Long userId;
+ /**
+ * 用户类型
+ */
+ private Integer userType;
+ /**
+ * 用户信息
+ */
+ private Map userInfo;
+ /**
+ * 租户编号
+ */
+ private Long tenantId;
+ /**
+ * 授权范围的数组
+ */
+ private List scopes;
+ /**
+ * 过期时间
+ */
+ private LocalDateTime expiresTime;
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCreateReqDTO.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCreateReqDTO.java
new file mode 100644
index 0000000..72dcd9b
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenCreateReqDTO.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.framework.common.biz.system.oauth2.dto;
+
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import lombok.Data;
+
+import jakarta.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * OAuth2.0 访问令牌创建 Request DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class OAuth2AccessTokenCreateReqDTO implements Serializable {
+
+ /**
+ * 用户编号
+ */
+ @NotNull(message = "用户编号不能为空")
+ private Long userId;
+ /**
+ * 用户类型
+ */
+ @NotNull(message = "用户类型不能为空")
+ @InEnum(value = UserTypeEnum.class, message = "用户类型必须是 {value}")
+ private Integer userType;
+ /**
+ * 客户端编号
+ */
+ @NotNull(message = "客户端编号不能为空")
+ private String clientId;
+ /**
+ * 授权范围
+ */
+ private List scopes;
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenRespDTO.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenRespDTO.java
new file mode 100644
index 0000000..c7b49f6
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/oauth2/dto/OAuth2AccessTokenRespDTO.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.framework.common.biz.system.oauth2.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * OAuth2.0 访问令牌的信息 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+@Accessors(chain = true)
+public class OAuth2AccessTokenRespDTO implements Serializable {
+
+ /**
+ * 访问令牌
+ */
+ private String accessToken;
+ /**
+ * 刷新令牌
+ */
+ private String refreshToken;
+ /**
+ * 用户编号
+ */
+ private Long userId;
+ /**
+ * 用户类型
+ */
+ private Integer userType;
+ /**
+ * 过期时间
+ */
+ private LocalDateTime expiresTime;
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/package-info.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/package-info.java
new file mode 100644
index 0000000..9589ee1
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 针对 system 模块的 api 包
+ */
+package cn.iocoder.yudao.framework.common.biz.system;
\ No newline at end of file
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/permission/PermissionCommonApi.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/permission/PermissionCommonApi.java
new file mode 100644
index 0000000..f842ed0
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/permission/PermissionCommonApi.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.framework.common.biz.system.permission;
+
+import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
+
+/**
+ * 权限 API 接口
+ *
+ * @author 芋道源码
+ */
+public interface PermissionCommonApi {
+
+ /**
+ * 判断是否有权限,任一一个即可
+ *
+ * @param userId 用户编号
+ * @param permissions 权限
+ * @return 是否
+ */
+ boolean hasAnyPermissions(Long userId, String... permissions);
+
+ /**
+ * 判断是否有角色,任一一个即可
+ *
+ * @param userId 用户编号
+ * @param roles 角色数组
+ * @return 是否
+ */
+ boolean hasAnyRoles(Long userId, String... roles);
+
+ /**
+ * 获得登陆用户的部门数据权限
+ *
+ * @param userId 用户编号
+ * @return 部门数据权限
+ */
+ DeptDataPermissionRespDTO getDeptDataPermission(Long userId);
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/permission/dto/DeptDataPermissionRespDTO.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/permission/dto/DeptDataPermissionRespDTO.java
new file mode 100644
index 0000000..952c84d
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/permission/dto/DeptDataPermissionRespDTO.java
@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.framework.common.biz.system.permission.dto;
+
+import lombok.Data;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * 部门的数据权限 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class DeptDataPermissionRespDTO {
+
+ /**
+ * 是否可查看全部数据
+ */
+ private Boolean all;
+ /**
+ * 是否可查看自己的数据
+ */
+ private Boolean self;
+ /**
+ * 可查看的部门编号数组
+ */
+ private Set deptIds;
+
+ public DeptDataPermissionRespDTO() {
+ this.all = false;
+ this.self = false;
+ this.deptIds = new HashSet<>();
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/tenant/TenantCommonApi.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/tenant/TenantCommonApi.java
new file mode 100644
index 0000000..809597e
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/biz/system/tenant/TenantCommonApi.java
@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.framework.common.biz.system.tenant;
+
+import java.util.List;
+
+/**
+ * 多租户的 API 接口
+ *
+ * @author 芋道源码
+ */
+public interface TenantCommonApi {
+
+ /**
+ * 获得所有租户
+ *
+ * @return 租户编号数组
+ */
+ List getTenantIdList();
+
+ /**
+ * 校验租户是否合法
+ *
+ * @param id 租户编号
+ */
+ void validateTenant(Long id);
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/ArrayValuable.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/ArrayValuable.java
new file mode 100644
index 0000000..b83451f
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/ArrayValuable.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.framework.common.core;
+
+/**
+ * 可生成 T 数组的接口
+ *
+ * @author HUIHUI
+ */
+public interface ArrayValuable {
+
+ /**
+ * @return 数组
+ */
+ T[] array();
+
+}
\ No newline at end of file
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/KeyValue.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/KeyValue.java
new file mode 100644
index 0000000..07a8f39
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/KeyValue.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.framework.common.core;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * Key Value 的键值对
+ *
+ * @author 芋道源码
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class KeyValue implements Serializable {
+
+ private K key;
+ private V value;
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java
new file mode 100644
index 0000000..f2a63dc
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.framework.common.enums;
+
+import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 通用状态枚举
+ *
+ * @author 芋道源码
+ */
+@Getter
+@AllArgsConstructor
+public enum CommonStatusEnum implements ArrayValuable {
+
+ ENABLE(0, "开启"),
+ DISABLE(1, "关闭");
+
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);
+
+ /**
+ * 状态值
+ */
+ private final Integer status;
+ /**
+ * 状态名
+ */
+ private final String name;
+
+ @Override
+ public Integer[] array() {
+ return ARRAYS;
+ }
+
+ public static boolean isEnable(Integer status) {
+ return ObjUtil.equal(ENABLE.status, status);
+ }
+
+ public static boolean isDisable(Integer status) {
+ return ObjUtil.equal(DISABLE.status, status);
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java
new file mode 100644
index 0000000..8d6a791
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.framework.common.enums;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 时间间隔的枚举
+ *
+ * @author dhb52
+ */
+@Getter
+@AllArgsConstructor
+public enum DateIntervalEnum implements ArrayValuable {
+
+ DAY(1, "天"),
+ WEEK(2, "周"),
+ MONTH(3, "月"),
+ QUARTER(4, "季度"),
+ YEAR(5, "年")
+ ;
+
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(DateIntervalEnum::getInterval).toArray(Integer[]::new);
+
+ /**
+ * 类型
+ */
+ private final Integer interval;
+ /**
+ * 名称
+ */
+ private final String name;
+
+ @Override
+ public Integer[] array() {
+ return ARRAYS;
+ }
+
+ public static DateIntervalEnum valueOf(Integer interval) {
+ return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values());
+ }
+
+}
\ No newline at end of file
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DocumentEnum.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DocumentEnum.java
new file mode 100644
index 0000000..a96ee24
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DocumentEnum.java
@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.framework.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 文档地址
+ *
+ * @author 芋道源码
+ */
+@Getter
+@AllArgsConstructor
+public enum DocumentEnum {
+
+ REDIS_INSTALL("https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I4VCSJ", "Redis 安装文档"),
+ TENANT("https://doc.iocoder.cn", "SaaS 多租户文档");
+
+ private final String url;
+ private final String memo;
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/RpcConstants.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/RpcConstants.java
new file mode 100644
index 0000000..b1c53db
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/RpcConstants.java
@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.framework.common.enums;
+
+/**
+ * RPC 相关的枚举
+ *
+ * 虽然放在 yudao-spring-boot-starter-rpc 会相对合适,但是每个 API 模块需要使用到,所以暂时只好放在此处
+ *
+ * @author 芋道源码
+ */
+public class RpcConstants {
+
+ /**
+ * RPC API 的前缀
+ */
+ public static final String RPC_API_PREFIX = "/rpc-api";
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java
new file mode 100644
index 0000000..33482d4
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.framework.common.enums;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * 终端的枚举
+ *
+ * @author 芋道源码
+ */
+@RequiredArgsConstructor
+@Getter
+public enum TerminalEnum implements ArrayValuable {
+
+ UNKNOWN(0, "未知"), // 目的:在无法解析到 terminal 时,使用它
+ WECHAT_MINI_PROGRAM(10, "微信小程序"),
+ WECHAT_WAP(11, "微信公众号"),
+ H5(20, "H5 网页"),
+ APP(31, "手机 App"),
+ ;
+
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(TerminalEnum::getTerminal).toArray(Integer[]::new);
+
+ /**
+ * 终端
+ */
+ private final Integer terminal;
+ /**
+ * 终端名
+ */
+ private final String name;
+
+ @Override
+ public Integer[] array() {
+ return ARRAYS;
+ }
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java
new file mode 100644
index 0000000..e00ec0b
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.framework.common.enums;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 全局用户类型枚举
+ */
+@AllArgsConstructor
+@Getter
+public enum UserTypeEnum implements ArrayValuable {
+
+ MEMBER(1, "会员"), // 面向 c 端,普通用户
+ ADMIN(2, "管理员"); // 面向 b 端,管理后台
+
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);
+
+ /**
+ * 类型
+ */
+ private final Integer value;
+ /**
+ * 类型名
+ */
+ private final String name;
+
+ public static UserTypeEnum valueOf(Integer value) {
+ return ArrayUtil.firstMatch(userType -> userType.getValue().equals(value), UserTypeEnum.values());
+ }
+
+ @Override
+ public Integer[] array() {
+ return ARRAYS;
+ }
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java
new file mode 100644
index 0000000..11a5ee0
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.framework.common.enums;
+
+/**
+ * Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
+ *
+ * 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enums 包下
+ *
+ * @author 芋道源码
+ */
+public interface WebFilterOrderEnum {
+
+ int CORS_FILTER = Integer.MIN_VALUE;
+
+ int TRACE_FILTER = CORS_FILTER + 1;
+
+ int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500;
+
+ // OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等
+
+ int TENANT_CONTEXT_FILTER = - 104; // 需要保证在 ApiAccessLogFilter 前面
+
+ int API_ACCESS_LOG_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面
+
+ int XSS_FILTER = -102; // 需要保证在 RequestBodyCacheFilter 后面
+
+ // Spring Security Filter 默认为 -100,可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类
+
+ int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面
+
+ int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面
+
+ int DEMO_FILTER = Integer.MAX_VALUE;
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ErrorCode.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ErrorCode.java
new file mode 100644
index 0000000..45feaec
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ErrorCode.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.framework.common.exception;
+
+import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
+import cn.iocoder.yudao.framework.common.exception.enums.ServiceErrorCodeRange;
+import lombok.Data;
+
+/**
+ * 错误码对象
+ *
+ * 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants}
+ * 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange}
+ *
+ * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备
+ */
+@Data
+public class ErrorCode {
+
+ /**
+ * 错误码
+ */
+ private final Integer code;
+ /**
+ * 错误提示
+ */
+ private final String msg;
+
+ public ErrorCode(Integer code, String message) {
+ this.code = code;
+ this.msg = message;
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ServerException.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ServerException.java
new file mode 100644
index 0000000..fac56d5
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ServerException.java
@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.framework.common.exception;
+
+import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 服务器异常 Exception
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public final class ServerException extends RuntimeException {
+
+ /**
+ * 全局错误码
+ *
+ * @see GlobalErrorCodeConstants
+ */
+ private Integer code;
+ /**
+ * 错误提示
+ */
+ private String message;
+
+ /**
+ * 空构造方法,避免反序列化问题
+ */
+ public ServerException() {
+ }
+
+ public ServerException(ErrorCode errorCode) {
+ this.code = errorCode.getCode();
+ this.message = errorCode.getMsg();
+ }
+
+ public ServerException(Integer code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ public Integer getCode() {
+ return code;
+ }
+
+ public ServerException setCode(Integer code) {
+ this.code = code;
+ return this;
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+
+ public ServerException setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ServiceException.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ServiceException.java
new file mode 100644
index 0000000..5c6967e
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/ServiceException.java
@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.framework.common.exception;
+
+import cn.iocoder.yudao.framework.common.exception.enums.ServiceErrorCodeRange;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 业务逻辑异常 Exception
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public final class ServiceException extends RuntimeException {
+
+ /**
+ * 业务错误码
+ *
+ * @see ServiceErrorCodeRange
+ */
+ private Integer code;
+ /**
+ * 错误提示
+ */
+ private String message;
+
+ /**
+ * 空构造方法,避免反序列化问题
+ */
+ public ServiceException() {
+ }
+
+ public ServiceException(ErrorCode errorCode) {
+ this.code = errorCode.getCode();
+ this.message = errorCode.getMsg();
+ }
+
+ public ServiceException(Integer code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ public Integer getCode() {
+ return code;
+ }
+
+ public ServiceException setCode(Integer code) {
+ this.code = code;
+ return this;
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+
+ public ServiceException setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/GlobalErrorCodeConstants.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/GlobalErrorCodeConstants.java
new file mode 100644
index 0000000..edf31f2
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/GlobalErrorCodeConstants.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.framework.common.exception.enums;
+
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+
+/**
+ * 全局错误码枚举
+ * 0-999 系统异常编码保留
+ *
+ * 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
+ * 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的
+ * 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。
+ *
+ * @author 芋道源码
+ */
+public interface GlobalErrorCodeConstants {
+
+ ErrorCode SUCCESS = new ErrorCode(0, "成功");
+
+ // ========== 客户端错误段 ==========
+
+ ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确");
+ ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录");
+ ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限");
+ ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到");
+ ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确");
+ ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许
+ ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试");
+
+ // ========== 服务端错误段 ==========
+
+ ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
+ ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启");
+ ErrorCode ERROR_CONFIGURATION = new ErrorCode(502, "错误的配置项");
+
+ // ========== 自定义错误段 ==========
+ ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求
+ ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作");
+
+ ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java
new file mode 100644
index 0000000..950ab25
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java
@@ -0,0 +1,48 @@
+package cn.iocoder.yudao.framework.common.exception.enums;
+
+/**
+ * 业务异常的错误码区间,解决:解决各模块错误码定义,避免重复,在此只声明不做实际使用
+ *
+ * 一共 10 位,分成四段
+ *
+ * 第一段,1 位,类型
+ * 1 - 业务级别异常
+ * x - 预留
+ * 第二段,3 位,系统类型
+ * 001 - 用户系统
+ * 002 - 商品系统
+ * 003 - 订单系统
+ * 004 - 支付系统
+ * 005 - 优惠劵系统
+ * ... - ...
+ * 第三段,3 位,模块
+ * 不限制规则。
+ * 一般建议,每个系统里面,可能有多个模块,可以再去做分段。以用户系统为例子:
+ * 001 - OAuth2 模块
+ * 002 - User 模块
+ * 003 - MobileCode 模块
+ * 第四段,3 位,错误码
+ * 不限制规则。
+ * 一般建议,每个模块自增。
+ *
+ * @author 芋道源码
+ */
+public class ServiceErrorCodeRange {
+
+ // 模块 infra 错误码区间 [1-001-000-000 ~ 1-002-000-000)
+ // 模块 system 错误码区间 [1-002-000-000 ~ 1-003-000-000)
+ // 模块 report 错误码区间 [1-003-000-000 ~ 1-004-000-000)
+ // 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000)
+ // 模块 mp 错误码区间 [1-006-000-000 ~ 1-007-000-000)
+ // 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000)
+ // 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000)
+
+ // 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000)
+ // 模块 trade 错误码区间 [1-011-000-000 ~ 1-012-000-000)
+ // 模块 promotion 错误码区间 [1-013-000-000 ~ 1-014-000-000)
+
+ // 模块 crm 错误码区间 [1-020-000-000 ~ 1-021-000-000)
+
+ // 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000)
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/util/ServiceExceptionUtil.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/util/ServiceExceptionUtil.java
new file mode 100644
index 0000000..03c0859
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/util/ServiceExceptionUtil.java
@@ -0,0 +1,77 @@
+package cn.iocoder.yudao.framework.common.exception.util;
+
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
+import com.google.common.annotations.VisibleForTesting;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * {@link ServiceException} 工具类
+ *
+ * 目的在于,格式化异常信息提示。
+ * 考虑到 String.format 在参数不正确时会报错,因此使用 {} 作为占位符,并使用 {@link #doFormat(int, String, Object...)} 方法来格式化
+ *
+ */
+@Slf4j
+public class ServiceExceptionUtil {
+
+ // ========== 和 ServiceException 的集成 ==========
+
+ public static ServiceException exception(ErrorCode errorCode) {
+ return exception0(errorCode.getCode(), errorCode.getMsg());
+ }
+
+ public static ServiceException exception(ErrorCode errorCode, Object... params) {
+ return exception0(errorCode.getCode(), errorCode.getMsg(), params);
+ }
+
+ public static ServiceException exception0(Integer code, String messagePattern, Object... params) {
+ String message = doFormat(code, messagePattern, params);
+ return new ServiceException(code, message);
+ }
+
+ public static ServiceException invalidParamException(String messagePattern, Object... params) {
+ return exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), messagePattern, params);
+ }
+
+ // ========== 格式化方法 ==========
+
+ /**
+ * 将错误编号对应的消息使用 params 进行格式化。
+ *
+ * @param code 错误编号
+ * @param messagePattern 消息模版
+ * @param params 参数
+ * @return 格式化后的提示
+ */
+ @VisibleForTesting
+ public static String doFormat(int code, String messagePattern, Object... params) {
+ StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
+ int i = 0;
+ int j;
+ int l;
+ for (l = 0; l < params.length; l++) {
+ j = messagePattern.indexOf("{}", i);
+ if (j == -1) {
+ log.error("[doFormat][参数过多:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
+ if (i == 0) {
+ return messagePattern;
+ } else {
+ sbuf.append(messagePattern.substring(i));
+ return sbuf.toString();
+ }
+ } else {
+ sbuf.append(messagePattern, i, j);
+ sbuf.append(params[l]);
+ i = j + 2;
+ }
+ }
+ if (messagePattern.indexOf("{}", i) != -1) {
+ log.error("[doFormat][参数过少:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
+ }
+ sbuf.append(messagePattern.substring(i));
+ return sbuf.toString();
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/package-info.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/package-info.java
new file mode 100644
index 0000000..f3f2574
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 基础的通用类,和框架无关
+ *
+ * 例如说,CommonResult 为通用返回
+ */
+package cn.iocoder.yudao.framework.common;
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java
new file mode 100644
index 0000000..ac74103
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java
@@ -0,0 +1,121 @@
+package cn.iocoder.yudao.framework.common.pojo;
+
+import cn.hutool.core.lang.Assert;
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
+import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * 通用返回
+ *
+ * @param 数据泛型
+ */
+@Data
+public class CommonResult implements Serializable {
+
+ /**
+ * 错误码
+ *
+ * @see ErrorCode#getCode()
+ */
+ private Integer code;
+ /**
+ * 返回数据
+ */
+ private T data;
+ /**
+ * 错误提示,用户可阅读
+ *
+ * @see ErrorCode#getMsg() ()
+ */
+ private String msg;
+
+ /**
+ * 将传入的 result 对象,转换成另外一个泛型结果的对象
+ *
+ * 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
+ *
+ * @param result 传入的 result 对象
+ * @param 返回的泛型
+ * @return 新的 CommonResult 对象
+ */
+ public static CommonResult error(CommonResult> result) {
+ return error(result.getCode(), result.getMsg());
+ }
+
+ public static CommonResult error(Integer code, String message) {
+ Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), code, "code 必须是错误的!");
+ CommonResult result = new CommonResult<>();
+ result.code = code;
+ result.msg = message;
+ return result;
+ }
+
+ public static CommonResult error(ErrorCode errorCode, Object... params) {
+ Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), errorCode.getCode(), "code 必须是错误的!");
+ CommonResult result = new CommonResult<>();
+ result.code = errorCode.getCode();
+ result.msg = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), params);
+ return result;
+ }
+
+ public static CommonResult error(ErrorCode errorCode) {
+ return error(errorCode.getCode(), errorCode.getMsg());
+ }
+
+ public static CommonResult success(T data) {
+ CommonResult result = new CommonResult<>();
+ result.code = GlobalErrorCodeConstants.SUCCESS.getCode();
+ result.data = data;
+ result.msg = "";
+ return result;
+ }
+
+ public static boolean isSuccess(Integer code) {
+ return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode());
+ }
+
+ @JsonIgnore // 避免 jackson 序列化
+ public boolean isSuccess() {
+ return isSuccess(code);
+ }
+
+ @JsonIgnore // 避免 jackson 序列化
+ public boolean isError() {
+ return !isSuccess();
+ }
+
+ // ========= 和 Exception 异常体系集成 =========
+
+ /**
+ * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常
+ */
+ public void checkError() throws ServiceException {
+ if (isSuccess()) {
+ return;
+ }
+ // 业务异常
+ throw new ServiceException(code, msg);
+ }
+
+ /**
+ * 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常
+ * 如果没有,则返回 {@link #data} 数据
+ */
+ @JsonIgnore // 避免 jackson 序列化
+ public T getCheckedData() {
+ checkError();
+ return data;
+ }
+
+ public static CommonResult error(ServiceException serviceException) {
+ return error(serviceException.getCode(), serviceException.getMessage());
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageParam.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageParam.java
new file mode 100644
index 0000000..268d117
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageParam.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.framework.common.pojo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.NotNull;
+import java.io.Serializable;
+
+@Schema(description="分页参数")
+@Data
+public class PageParam implements Serializable {
+
+ private static final Integer PAGE_NO = 1;
+ private static final Integer PAGE_SIZE = 10;
+
+ /**
+ * 每页条数 - 不分页
+ *
+ * 例如说,导出接口,可以设置 {@link #pageSize} 为 -1 不分页,查询所有数据。
+ */
+ public static final Integer PAGE_SIZE_NONE = -1;
+
+ @Schema(description = "页码,从 1 开始", requiredMode = Schema.RequiredMode.REQUIRED,example = "1")
+ @NotNull(message = "页码不能为空")
+ @Min(value = 1, message = "页码最小值为 1")
+ private Integer pageNo = PAGE_NO;
+
+ @Schema(description = "每页条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ @NotNull(message = "每页条数不能为空")
+ @Min(value = 1, message = "每页条数最小值为 1")
+ @Max(value = 100, message = "每页条数最大值为 100")
+ private Integer pageSize = PAGE_SIZE;
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageResult.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageResult.java
new file mode 100644
index 0000000..ff9087a
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageResult.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.framework.common.pojo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+@Schema(description = "分页结果")
+@Data
+public final class PageResult implements Serializable {
+
+ @Schema(description = "数据", requiredMode = Schema.RequiredMode.REQUIRED)
+ private List list;
+
+ @Schema(description = "总量", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Long total;
+
+ public PageResult() {
+ }
+
+ public PageResult(List list, Long total) {
+ this.list = list;
+ this.total = total;
+ }
+
+ public PageResult(Long total) {
+ this.list = new ArrayList<>();
+ this.total = total;
+ }
+
+ public static PageResult empty() {
+ return new PageResult<>(0L);
+ }
+
+ public static PageResult empty(Long total) {
+ return new PageResult<>(total);
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortablePageParam.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortablePageParam.java
new file mode 100644
index 0000000..2365c41
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortablePageParam.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.framework.common.pojo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.List;
+
+@Schema(description = "可排序的分页参数")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SortablePageParam extends PageParam {
+
+ @Schema(description = "排序字段")
+ private List sortingFields;
+
+}
\ No newline at end of file
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortingField.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortingField.java
new file mode 100644
index 0000000..07a68b0
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortingField.java
@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.framework.common.pojo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 排序字段 DTO
+ *
+ * 类名加了 ing 的原因是,避免和 ES SortField 重名。
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SortingField implements Serializable {
+
+ /**
+ * 顺序 - 升序
+ */
+ public static final String ORDER_ASC = "asc";
+ /**
+ * 顺序 - 降序
+ */
+ public static final String ORDER_DESC = "desc";
+
+ /**
+ * 字段
+ */
+ private String field;
+ /**
+ * 顺序
+ */
+ private String order;
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java
new file mode 100644
index 0000000..12a6e17
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java
@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.framework.common.util.cache;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+import java.time.Duration;
+import java.util.concurrent.Executors;
+
+/**
+ * Cache 工具类
+ *
+ * @author 芋道源码
+ */
+public class CacheUtils {
+
+ /**
+ * 构建异步刷新的 LoadingCache 对象
+ *
+ * 注意:如果你的缓存和 ThreadLocal 有关系,要么自己处理 ThreadLocal 的传递,要么使用 {@link #buildCache(Duration, CacheLoader)} 方法
+ *
+ * 或者简单理解:
+ * 1、和“人”相关的,使用 {@link #buildCache(Duration, CacheLoader)} 方法
+ * 2、和“全局”、“系统”相关的,使用当前缓存方法
+ *
+ * @param duration 过期时间
+ * @param loader CacheLoader 对象
+ * @return LoadingCache 对象
+ */
+ public static LoadingCache buildAsyncReloadingCache(Duration duration, CacheLoader loader) {
+ return CacheBuilder.newBuilder()
+ // 只阻塞当前数据加载线程,其他线程返回旧值
+ .refreshAfterWrite(duration)
+ // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
+ .build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 芋艿:可能要思考下,未来要不要做成可配置
+ }
+
+ /**
+ * 构建同步刷新的 LoadingCache 对象
+ *
+ * @param duration 过期时间
+ * @param loader CacheLoader 对象
+ * @return LoadingCache 对象
+ */
+ public static LoadingCache buildCache(Duration duration, CacheLoader loader) {
+ return CacheBuilder.newBuilder().refreshAfterWrite(duration).build(loader);
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/ArrayUtils.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/ArrayUtils.java
new file mode 100644
index 0000000..4285b8f
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/ArrayUtils.java
@@ -0,0 +1,58 @@
+package cn.iocoder.yudao.framework.common.util.collection;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.collection.IterUtil;
+import cn.hutool.core.util.ArrayUtil;
+
+import java.util.Collection;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+
+/**
+ * Array 工具类
+ *
+ * @author 芋道源码
+ */
+public class ArrayUtils {
+
+ /**
+ * 将 object 和 newElements 合并成一个数组
+ *
+ * @param object 对象
+ * @param newElements 数组
+ * @param 泛型
+ * @return 结果数组
+ */
+ @SafeVarargs
+ public static Consumer[] append(Consumer object, Consumer... newElements) {
+ if (object == null) {
+ return newElements;
+ }
+ Consumer[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length);
+ result[0] = object;
+ System.arraycopy(newElements, 0, result, 1, newElements.length);
+ return result;
+ }
+
+ public static V[] toArray(Collection from, Function mapper) {
+ return toArray(convertList(from, mapper));
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T[] toArray(Collection from) {
+ if (CollectionUtil.isEmpty(from)) {
+ return (T[]) (new Object[0]);
+ }
+ return ArrayUtil.toArray(from, (Class) IterUtil.getElementType(from.iterator()));
+ }
+
+ public static T get(T[] array, int index) {
+ if (null == array || index >= array.length) {
+ return null;
+ }
+ return array[index];
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
new file mode 100644
index 0000000..aa523b9
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
@@ -0,0 +1,352 @@
+package cn.iocoder.yudao.framework.common.util.collection;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static cn.hutool.core.convert.Convert.toCollection;
+import static java.util.Arrays.asList;
+
+/**
+ * Collection 工具类
+ *
+ * @author 芋道源码
+ */
+public class CollectionUtils {
+
+ public static boolean containsAny(Object source, Object... targets) {
+ return asList(targets).contains(source);
+ }
+
+ public static boolean isAnyEmpty(Collection>... collections) {
+ return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty);
+ }
+
+ public static boolean anyMatch(Collection from, Predicate predicate) {
+ return from.stream().anyMatch(predicate);
+ }
+
+ public static List filterList(Collection from, Predicate predicate) {
+ if (CollUtil.isEmpty(from)) {
+ return new ArrayList<>();
+ }
+ return from.stream().filter(predicate).collect(Collectors.toList());
+ }
+
+ public static List distinct(Collection from, Function keyMapper) {
+ if (CollUtil.isEmpty(from)) {
+ return new ArrayList<>();
+ }
+ return distinct(from, keyMapper, (t1, t2) -> t1);
+ }
+
+ public static List distinct(Collection from, Function keyMapper, BinaryOperator cover) {
+ if (CollUtil.isEmpty(from)) {
+ return new ArrayList<>();
+ }
+ return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values());
+ }
+
+ public static List convertList(T[] from, Function func) {
+ if (ArrayUtil.isEmpty(from)) {
+ return new ArrayList<>();
+ }
+ return convertList(Arrays.asList(from), func);
+ }
+
+ public static List convertList(Collection from, Function func) {
+ if (CollUtil.isEmpty(from)) {
+ return new ArrayList<>();
+ }
+ return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());
+ }
+
+ public static List convertList(Collection from, Function func, Predicate filter) {
+ if (CollUtil.isEmpty(from)) {
+ return new ArrayList<>();
+ }
+ return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
+ }
+
+ public static PageResult convertPage(PageResult from, Function func) {
+ if (ArrayUtil.isEmpty(from)) {
+ return new PageResult<>(from.getTotal());
+ }
+ return new PageResult<>(convertList(from.getList(), func), from.getTotal());
+ }
+
+ public static List convertListByFlatMap(Collection from,
+ Function> func) {
+ if (CollUtil.isEmpty(from)) {
+ return new ArrayList<>();
+ }
+ return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
+ }
+
+ public static List convertListByFlatMap(Collection from,
+ Function super T, ? extends U> mapper,
+ Function> func) {
+ if (CollUtil.isEmpty(from)) {
+ return new ArrayList<>();
+ }
+ return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
+ }
+
+ public static List mergeValuesFromMap(Map> map) {
+ return map.values()
+ .stream()
+ .flatMap(List::stream)
+ .collect(Collectors.toList());
+ }
+
+ public static Set convertSet(Collection from) {
+ return convertSet(from, v -> v);
+ }
+
+ public static Set convertSet(Collection from, Function func) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashSet<>();
+ }
+ return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet());
+ }
+
+ public static Set convertSet(Collection from, Function func, Predicate filter) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashSet<>();
+ }
+ return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet());
+ }
+
+ public static Map convertMapByFilter(Collection from, Predicate filter, Function keyFunc) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashMap<>();
+ }
+ return from.stream().filter(filter).collect(Collectors.toMap(keyFunc, v -> v));
+ }
+
+ public static Set convertSetByFlatMap(Collection from,
+ Function> func) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashSet<>();
+ }
+ return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
+ }
+
+ public static Set convertSetByFlatMap(Collection from,
+ Function super T, ? extends U> mapper,
+ Function> func) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashSet<>();
+ }
+ return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
+ }
+
+ public static Map convertMap(Collection from, Function keyFunc) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashMap<>();
+ }
+ return convertMap(from, keyFunc, Function.identity());
+ }
+
+ public static Map convertMap(Collection from, Function keyFunc, Supplier extends Map> supplier) {
+ if (CollUtil.isEmpty(from)) {
+ return supplier.get();
+ }
+ return convertMap(from, keyFunc, Function.identity(), supplier);
+ }
+
+ public static Map convertMap(Collection from, Function keyFunc, Function valueFunc) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashMap<>();
+ }
+ return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1);
+ }
+
+ public static Map convertMap(Collection from, Function keyFunc, Function valueFunc, BinaryOperator mergeFunction) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashMap<>();
+ }
+ return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new);
+ }
+
+ public static Map convertMap(Collection from, Function keyFunc, Function valueFunc, Supplier extends Map> supplier) {
+ if (CollUtil.isEmpty(from)) {
+ return supplier.get();
+ }
+ return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier);
+ }
+
+ public static Map convertMap(Collection from, Function keyFunc, Function valueFunc, BinaryOperator mergeFunction, Supplier extends Map> supplier) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashMap<>();
+ }
+ return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier));
+ }
+
+ public static Map> convertMultiMap(Collection from, Function keyFunc) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashMap<>();
+ }
+ return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList())));
+ }
+
+ public static Map> convertMultiMap(Collection from, Function keyFunc, Function valueFunc) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashMap<>();
+ }
+ return from.stream()
+ .collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList())));
+ }
+
+ // 暂时没想好名字,先以 2 结尾噶
+ public static Map> convertMultiMap2(Collection from, Function keyFunc, Function valueFunc) {
+ if (CollUtil.isEmpty(from)) {
+ return new HashMap<>();
+ }
+ return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));
+ }
+
+ public static Map convertImmutableMap(Collection from, Function keyFunc) {
+ if (CollUtil.isEmpty(from)) {
+ return Collections.emptyMap();
+ }
+ ImmutableMap.Builder builder = ImmutableMap.builder();
+ from.forEach(item -> builder.put(keyFunc.apply(item), item));
+ return builder.build();
+ }
+
+ /**
+ * 对比老、新两个列表,找出新增、修改、删除的数据
+ *
+ * @param oldList 老列表
+ * @param newList 新列表
+ * @param sameFunc 对比函数,返回 true 表示相同,返回 false 表示不同
+ * 注意,same 是通过每个元素的“标识”,判断它们是不是同一个数据
+ * @return [新增列表、修改列表、删除列表]
+ */
+ public static List> diffList(Collection oldList, Collection newList,
+ BiFunction sameFunc) {
+ List createList = new LinkedList<>(newList); // 默认都认为是新增的,后续会进行移除
+ List updateList = new ArrayList<>();
+ List deleteList = new ArrayList<>();
+
+ // 通过以 oldList 为主遍历,找出 updateList 和 deleteList
+ for (T oldObj : oldList) {
+ // 1. 寻找是否有匹配的
+ T foundObj = null;
+ for (Iterator iterator = createList.iterator(); iterator.hasNext(); ) {
+ T newObj = iterator.next();
+ // 1.1 不匹配,则直接跳过
+ if (!sameFunc.apply(oldObj, newObj)) {
+ continue;
+ }
+ // 1.2 匹配,则移除,并结束寻找
+ iterator.remove();
+ foundObj = newObj;
+ break;
+ }
+ // 2. 匹配添加到 updateList;不匹配则添加到 deleteList 中
+ if (foundObj != null) {
+ updateList.add(foundObj);
+ } else {
+ deleteList.add(oldObj);
+ }
+ }
+ return asList(createList, updateList, deleteList);
+ }
+
+ public static boolean containsAny(Collection> source, Collection> candidates) {
+ return org.springframework.util.CollectionUtils.containsAny(source, candidates);
+ }
+
+ public static T getFirst(List from) {
+ return !CollectionUtil.isEmpty(from) ? from.get(0) : null;
+ }
+
+ public static T findFirst(Collection from, Predicate predicate) {
+ return findFirst(from, predicate, Function.identity());
+ }
+
+ public static U findFirst(Collection from, Predicate predicate, Function func) {
+ if (CollUtil.isEmpty(from)) {
+ return null;
+ }
+ return from.stream().filter(predicate).findFirst().map(func).orElse(null);
+ }
+
+ public static > V getMaxValue(Collection from, Function valueFunc) {
+ if (CollUtil.isEmpty(from)) {
+ return null;
+ }
+ assert !from.isEmpty(); // 断言,避免告警
+ T t = from.stream().max(Comparator.comparing(valueFunc)).get();
+ return valueFunc.apply(t);
+ }
+
+ public static > V getMinValue(List from, Function valueFunc) {
+ if (CollUtil.isEmpty(from)) {
+ return null;
+ }
+ assert from.size() > 0; // 断言,避免告警
+ T t = from.stream().min(Comparator.comparing(valueFunc)).get();
+ return valueFunc.apply(t);
+ }
+
+ public static > T getMinObject(List from, Function valueFunc) {
+ if (CollUtil.isEmpty(from)) {
+ return null;
+ }
+ assert from.size() > 0; // 断言,避免告警
+ return from.stream().min(Comparator.comparing(valueFunc)).get();
+ }
+
+ public static > V getSumValue(Collection from, Function valueFunc,
+ BinaryOperator accumulator) {
+ return getSumValue(from, valueFunc, accumulator, null);
+ }
+
+ public static > V getSumValue(Collection from, Function valueFunc,
+ BinaryOperator accumulator, V defaultValue) {
+ if (CollUtil.isEmpty(from)) {
+ return defaultValue;
+ }
+ assert !from.isEmpty(); // 断言,避免告警
+ return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue);
+ }
+
+ public static void addIfNotNull(Collection coll, T item) {
+ if (item == null) {
+ return;
+ }
+ coll.add(item);
+ }
+
+ public static Collection singleton(T obj) {
+ return obj == null ? Collections.emptyList() : Collections.singleton(obj);
+ }
+
+ public static List newArrayList(List> list) {
+ return list.stream().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());
+ }
+
+ /**
+ * 转换为 LinkedHashSet
+ *
+ * @param 元素类型
+ * @param elementType 集合中元素类型
+ * @param value 被转换的值
+ * @return {@link LinkedHashSet}
+ */
+ @SuppressWarnings("unchecked")
+ public static LinkedHashSet toLinkedHashSet(Class elementType, Object value) {
+ return (LinkedHashSet) toCollection(LinkedHashSet.class, elementType, value);
+ }
+
+}
\ No newline at end of file
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/MapUtils.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/MapUtils.java
new file mode 100644
index 0000000..a59b53f
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/MapUtils.java
@@ -0,0 +1,68 @@
+package cn.iocoder.yudao.framework.common.util.collection;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.core.KeyValue;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * Map 工具类
+ *
+ * @author 芋道源码
+ */
+public class MapUtils {
+
+ /**
+ * 从哈希表表中,获得 keys 对应的所有 value 数组
+ *
+ * @param multimap 哈希表
+ * @param keys keys
+ * @return value 数组
+ */
+ public static List getList(Multimap multimap, Collection keys) {
+ List result = new ArrayList<>();
+ keys.forEach(k -> {
+ Collection values = multimap.get(k);
+ if (CollectionUtil.isEmpty(values)) {
+ return;
+ }
+ result.addAll(values);
+ });
+ return result;
+ }
+
+ /**
+ * 从哈希表查找到 key 对应的 value,然后进一步处理
+ * key 为 null 时, 不处理
+ * 注意,如果查找到的 value 为 null 时,不进行处理
+ *
+ * @param map 哈希表
+ * @param key key
+ * @param consumer 进一步处理的逻辑
+ */
+ public static void findAndThen(Map map, K key, Consumer consumer) {
+ if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) {
+ return;
+ }
+ V value = map.get(key);
+ if (value == null) {
+ return;
+ }
+ consumer.accept(value);
+ }
+
+ public static Map convertMap(List> keyValues) {
+ Map map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size());
+ keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue()));
+ return map;
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/SetUtils.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/SetUtils.java
new file mode 100644
index 0000000..e8eba66
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/SetUtils.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.framework.common.util.collection;
+
+import cn.hutool.core.collection.CollUtil;
+
+import java.util.Set;
+
+/**
+ * Set 工具类
+ *
+ * @author 芋道源码
+ */
+public class SetUtils {
+
+ @SafeVarargs
+ public static Set asSet(T... objs) {
+ return CollUtil.newHashSet(objs);
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
new file mode 100644
index 0000000..b51a838
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
@@ -0,0 +1,149 @@
+package cn.iocoder.yudao.framework.common.util.date;
+
+import cn.hutool.core.date.LocalDateTimeUtil;
+
+import java.time.*;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * 时间工具类
+ *
+ * @author 芋道源码
+ */
+public class DateUtils {
+
+ /**
+ * 时区 - 默认
+ */
+ public static final String TIME_ZONE_DEFAULT = "GMT+8";
+
+ /**
+ * 秒转换成毫秒
+ */
+ public static final long SECOND_MILLIS = 1000;
+
+ public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd";
+
+ public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
+
+ /**
+ * 将 LocalDateTime 转换成 Date
+ *
+ * @param date LocalDateTime
+ * @return LocalDateTime
+ */
+ public static Date of(LocalDateTime date) {
+ if (date == null) {
+ return null;
+ }
+ // 将此日期时间与时区相结合以创建 ZonedDateTime
+ ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());
+ // 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳
+ Instant instant = zonedDateTime.toInstant();
+ // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
+ return Date.from(instant);
+ }
+
+ /**
+ * 将 Date 转换成 LocalDateTime
+ *
+ * @param date Date
+ * @return LocalDateTime
+ */
+ public static LocalDateTime of(Date date) {
+ if (date == null) {
+ return null;
+ }
+ // 转为时间戳
+ Instant instant = date.toInstant();
+ // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
+ return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
+ }
+
+ public static Date addTime(Duration duration) {
+ return new Date(System.currentTimeMillis() + duration.toMillis());
+ }
+
+ public static boolean isExpired(LocalDateTime time) {
+ LocalDateTime now = LocalDateTime.now();
+ return now.isAfter(time);
+ }
+
+ /**
+ * 创建指定时间
+ *
+ * @param year 年
+ * @param mouth 月
+ * @param day 日
+ * @return 指定时间
+ */
+ public static Date buildTime(int year, int mouth, int day) {
+ return buildTime(year, mouth, day, 0, 0, 0);
+ }
+
+ /**
+ * 创建指定时间
+ *
+ * @param year 年
+ * @param mouth 月
+ * @param day 日
+ * @param hour 小时
+ * @param minute 分钟
+ * @param second 秒
+ * @return 指定时间
+ */
+ public static Date buildTime(int year, int mouth, int day,
+ int hour, int minute, int second) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.set(Calendar.YEAR, year);
+ calendar.set(Calendar.MONTH, mouth - 1);
+ calendar.set(Calendar.DAY_OF_MONTH, day);
+ calendar.set(Calendar.HOUR_OF_DAY, hour);
+ calendar.set(Calendar.MINUTE, minute);
+ calendar.set(Calendar.SECOND, second);
+ calendar.set(Calendar.MILLISECOND, 0); // 一般情况下,都是 0 毫秒
+ return calendar.getTime();
+ }
+
+ public static Date max(Date a, Date b) {
+ if (a == null) {
+ return b;
+ }
+ if (b == null) {
+ return a;
+ }
+ return a.compareTo(b) > 0 ? a : b;
+ }
+
+ public static LocalDateTime max(LocalDateTime a, LocalDateTime b) {
+ if (a == null) {
+ return b;
+ }
+ if (b == null) {
+ return a;
+ }
+ return a.isAfter(b) ? a : b;
+ }
+
+ /**
+ * 是否今天
+ *
+ * @param date 日期
+ * @return 是否
+ */
+ public static boolean isToday(LocalDateTime date) {
+ return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
+ }
+
+ /**
+ * 是否昨天
+ *
+ * @param date 日期
+ * @return 是否
+ */
+ public static boolean isYesterday(LocalDateTime date) {
+ return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now().minusDays(1));
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
new file mode 100644
index 0000000..8d18479
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
@@ -0,0 +1,328 @@
+package cn.iocoder.yudao.framework.common.util.date;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.date.TemporalAccessorUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
+
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAdjusters;
+import java.util.ArrayList;
+import java.util.List;
+
+import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN;
+import static cn.hutool.core.date.DatePattern.createFormatter;
+
+/**
+ * 时间工具类,用于 {@link LocalDateTime}
+ *
+ * @author 芋道源码
+ */
+public class LocalDateTimeUtils {
+
+ /**
+ * 空的 LocalDateTime 对象,主要用于 DB 唯一索引的默认值
+ */
+ public static LocalDateTime EMPTY = buildTime(1970, 1, 1);
+
+ public static DateTimeFormatter UTC_MS_WITH_XXX_OFFSET_FORMATTER = createFormatter(UTC_MS_WITH_XXX_OFFSET_PATTERN);
+
+ /**
+ * 解析时间
+ *
+ * 相比 {@link LocalDateTimeUtil#parse(CharSequence)} 方法来说,会尽量去解析,直到成功
+ *
+ * @param time 时间
+ * @return 时间字符串
+ */
+ public static LocalDateTime parse(String time) {
+ try {
+ return LocalDateTimeUtil.parse(time, DatePattern.NORM_DATE_PATTERN);
+ } catch (DateTimeParseException e) {
+ return LocalDateTimeUtil.parse(time);
+ }
+ }
+
+ public static LocalDateTime addTime(Duration duration) {
+ return LocalDateTime.now().plus(duration);
+ }
+
+ public static LocalDateTime minusTime(Duration duration) {
+ return LocalDateTime.now().minus(duration);
+ }
+
+ public static boolean beforeNow(LocalDateTime date) {
+ return date.isBefore(LocalDateTime.now());
+ }
+
+ public static boolean afterNow(LocalDateTime date) {
+ return date.isAfter(LocalDateTime.now());
+ }
+
+ /**
+ * 创建指定时间
+ *
+ * @param year 年
+ * @param mouth 月
+ * @param day 日
+ * @return 指定时间
+ */
+ public static LocalDateTime buildTime(int year, int mouth, int day) {
+ return LocalDateTime.of(year, mouth, day, 0, 0, 0);
+ }
+
+ public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1,
+ int year2, int mouth2, int day2) {
+ return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
+ }
+
+ /**
+ * 判指定断时间,是否在该时间范围内
+ *
+ * @param startTime 开始时间
+ * @param endTime 结束时间
+ * @param time 指定时间
+ * @return 是否
+ */
+ public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) {
+ if (startTime == null || endTime == null || time == null) {
+ return false;
+ }
+ return LocalDateTimeUtil.isIn(parse(time), startTime, endTime);
+ }
+
+ /**
+ * 判断当前时间是否在该时间范围内
+ *
+ * @param startTime 开始时间
+ * @param endTime 结束时间
+ * @return 是否
+ */
+ public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) {
+ if (startTime == null || endTime == null) {
+ return false;
+ }
+ return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime);
+ }
+
+ /**
+ * 判断当前时间是否在该时间范围内
+ *
+ * @param startTime 开始时间
+ * @param endTime 结束时间
+ * @return 是否
+ */
+ public static boolean isBetween(String startTime, String endTime) {
+ if (startTime == null || endTime == null) {
+ return false;
+ }
+ LocalDate nowDate = LocalDate.now();
+ return LocalDateTimeUtil.isIn(LocalDateTime.now(),
+ LocalDateTime.of(nowDate, LocalTime.parse(startTime)),
+ LocalDateTime.of(nowDate, LocalTime.parse(endTime)));
+ }
+
+ /**
+ * 判断时间段是否重叠
+ *
+ * @param startTime1 开始 time1
+ * @param endTime1 结束 time1
+ * @param startTime2 开始 time2
+ * @param endTime2 结束 time2
+ * @return 重叠:true 不重叠:false
+ */
+ public static boolean isOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {
+ LocalDate nowDate = LocalDate.now();
+ return LocalDateTimeUtil.isOverlap(LocalDateTime.of(nowDate, startTime1), LocalDateTime.of(nowDate, endTime1),
+ LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2));
+ }
+
+ /**
+ * 获取指定日期所在的月份的开始时间
+ * 例如:2023-09-30 00:00:00,000
+ *
+ * @param date 日期
+ * @return 月份的开始时间
+ */
+ public static LocalDateTime beginOfMonth(LocalDateTime date) {
+ return date.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
+ }
+
+ /**
+ * 获取指定日期所在的月份的最后时间
+ * 例如:2023-09-30 23:59:59,999
+ *
+ * @param date 日期
+ * @return 月份的结束时间
+ */
+ public static LocalDateTime endOfMonth(LocalDateTime date) {
+ return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
+ }
+
+ /**
+ * 获得指定日期所在季度
+ *
+ * @param date 日期
+ * @return 所在季度
+ */
+ public static int getQuarterOfYear(LocalDateTime date) {
+ return (date.getMonthValue() - 1) / 3 + 1;
+ }
+
+ /**
+ * 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负
+ *
+ * @param dateTime 日期
+ * @return 相差天数
+ */
+ public static Long between(LocalDateTime dateTime) {
+ return LocalDateTimeUtil.between(dateTime, LocalDateTime.now(), ChronoUnit.DAYS);
+ }
+
+ /**
+ * 获取今天的开始时间
+ *
+ * @return 今天
+ */
+ public static LocalDateTime getToday() {
+ return LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
+ }
+
+ /**
+ * 获取昨天的开始时间
+ *
+ * @return 昨天
+ */
+ public static LocalDateTime getYesterday() {
+ return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().minusDays(1));
+ }
+
+ /**
+ * 获取本月的开始时间
+ *
+ * @return 本月
+ */
+ public static LocalDateTime getMonth() {
+ return beginOfMonth(LocalDateTime.now());
+ }
+
+ /**
+ * 获取本年的开始时间
+ *
+ * @return 本年
+ */
+ public static LocalDateTime getYear() {
+ return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);
+ }
+
+ public static List getDateRangeList(LocalDateTime startTime,
+ LocalDateTime endTime,
+ Integer interval) {
+ // 1.1 找到枚举
+ DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);
+ Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval);
+ // 1.2 将时间对齐
+ startTime = LocalDateTimeUtil.beginOfDay(startTime);
+ endTime = LocalDateTimeUtil.endOfDay(endTime);
+
+ // 2. 循环,生成时间范围
+ List timeRanges = new ArrayList<>();
+ switch (intervalEnum) {
+ case DAY:
+ while (startTime.isBefore(endTime)) {
+ timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)});
+ startTime = startTime.plusDays(1);
+ }
+ break;
+ case WEEK:
+ while (startTime.isBefore(endTime)) {
+ LocalDateTime endOfWeek = startTime.with(DayOfWeek.SUNDAY).plusDays(1).minusNanos(1);
+ timeRanges.add(new LocalDateTime[]{startTime, endOfWeek});
+ startTime = endOfWeek.plusNanos(1);
+ }
+ break;
+ case MONTH:
+ while (startTime.isBefore(endTime)) {
+ LocalDateTime endOfMonth = startTime.with(TemporalAdjusters.lastDayOfMonth()).plusDays(1).minusNanos(1);
+ timeRanges.add(new LocalDateTime[]{startTime, endOfMonth});
+ startTime = endOfMonth.plusNanos(1);
+ }
+ break;
+ case QUARTER:
+ while (startTime.isBefore(endTime)) {
+ int quarterOfYear = getQuarterOfYear(startTime);
+ LocalDateTime quarterEnd = quarterOfYear == 4
+ ? startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1)
+ : startTime.withMonth(quarterOfYear * 3 + 1).withDayOfMonth(1).minusNanos(1);
+ timeRanges.add(new LocalDateTime[]{startTime, quarterEnd});
+ startTime = quarterEnd.plusNanos(1);
+ }
+ break;
+ case YEAR:
+ while (startTime.isBefore(endTime)) {
+ LocalDateTime endOfYear = startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1);
+ timeRanges.add(new LocalDateTime[]{startTime, endOfYear});
+ startTime = endOfYear.plusNanos(1);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid interval: " + interval);
+ }
+ // 3. 兜底,最后一个时间,需要保持在 endTime 之前
+ LocalDateTime[] lastTimeRange = CollUtil.getLast(timeRanges);
+ if (lastTimeRange != null) {
+ lastTimeRange[1] = endTime;
+ }
+ return timeRanges;
+ }
+
+ /**
+ * 格式化时间范围
+ *
+ * @param startTime 开始时间
+ * @param endTime 结束时间
+ * @param interval 时间间隔
+ * @return 时间范围
+ */
+ public static String formatDateRange(LocalDateTime startTime, LocalDateTime endTime, Integer interval) {
+ // 1. 找到枚举
+ DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);
+ Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval);
+
+ // 2. 循环,生成时间范围
+ switch (intervalEnum) {
+ case DAY:
+ return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN);
+ case WEEK:
+ return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN)
+ + StrUtil.format("(第 {} 周)", LocalDateTimeUtil.weekOfYear(startTime));
+ case MONTH:
+ return LocalDateTimeUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN);
+ case QUARTER:
+ return StrUtil.format("{}-Q{}", startTime.getYear(), getQuarterOfYear(startTime));
+ case YEAR:
+ return LocalDateTimeUtil.format(startTime, DatePattern.NORM_YEAR_PATTERN);
+ default:
+ throw new IllegalArgumentException("Invalid interval: " + interval);
+ }
+ }
+
+ /**
+ * 将给定的 {@link LocalDateTime} 转换为自 Unix 纪元时间(1970-01-01T00:00:00Z)以来的秒数。
+ *
+ * @param sourceDateTime 需要转换的本地日期时间,不能为空
+ * @return 自 1970-01-01T00:00:00Z 起的秒数(epoch second)
+ * @throws NullPointerException 如果 {@code sourceDateTime} 为 {@code null}
+ * @throws DateTimeException 如果转换过程中发生时间超出范围或其他时间处理异常
+ */
+ public static Long toEpochSecond(LocalDateTime sourceDateTime) {
+ return TemporalAccessorUtil.toInstant(sourceDateTime).getEpochSecond();
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java
new file mode 100644
index 0000000..e1e0ac0
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java
@@ -0,0 +1,175 @@
+package cn.iocoder.yudao.framework.common.util.http;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.map.TableMap;
+import cn.hutool.core.net.url.UrlBuilder;
+import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.util.StringUtils;
+import org.springframework.web.util.UriComponents;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import java.net.URI;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+/**
+ * HTTP 工具类
+ *
+ * @author 芋道源码
+ */
+public class HttpUtils {
+
+ /**
+ * 编码 URL 参数
+ *
+ * @param value 参数
+ * @return 编码后的参数
+ */
+ public static String encodeUtf8(String value) {
+ return URLEncoder.encode(value, StandardCharsets.UTF_8);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static String replaceUrlQuery(String url, String key, String value) {
+ UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());
+ // 先移除
+ TableMap query = (TableMap)
+ ReflectUtil.getFieldValue(builder.getQuery(), "query");
+ query.remove(key);
+ // 后添加
+ builder.addQuery(key, value);
+ return builder.build();
+ }
+
+ private String append(String base, Map query, boolean fragment) {
+ return append(base, query, null, fragment);
+ }
+
+ /**
+ * 拼接 URL
+ *
+ * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 append 方法
+ *
+ * @param base 基础 URL
+ * @param query 查询参数
+ * @param keys query 的 key,对应的原本的 key 的映射。例如说 query 里有个 key 是 xx,实际它的 key 是 extra_xx,则通过 keys 里添加这个映射
+ * @param fragment URL 的 fragment,即拼接到 # 中
+ * @return 拼接后的 URL
+ */
+ public static String append(String base, Map query, Map keys, boolean fragment) {
+ UriComponentsBuilder template = UriComponentsBuilder.newInstance();
+ UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base);
+ URI redirectUri;
+ try {
+ // assume it's encoded to start with (if it came in over the wire)
+ redirectUri = builder.build(true).toUri();
+ } catch (Exception e) {
+ // ... but allow client registrations to contain hard-coded non-encoded values
+ redirectUri = builder.build().toUri();
+ builder = UriComponentsBuilder.fromUri(redirectUri);
+ }
+ template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost())
+ .userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath());
+
+ if (fragment) {
+ StringBuilder values = new StringBuilder();
+ if (redirectUri.getFragment() != null) {
+ String append = redirectUri.getFragment();
+ values.append(append);
+ }
+ for (String key : query.keySet()) {
+ if (values.length() > 0) {
+ values.append("&");
+ }
+ String name = key;
+ if (keys != null && keys.containsKey(key)) {
+ name = keys.get(key);
+ }
+ values.append(name).append("={").append(key).append("}");
+ }
+ if (values.length() > 0) {
+ template.fragment(values.toString());
+ }
+ UriComponents encoded = template.build().expand(query).encode();
+ builder.fragment(encoded.getFragment());
+ } else {
+ for (String key : query.keySet()) {
+ String name = key;
+ if (keys != null && keys.containsKey(key)) {
+ name = keys.get(key);
+ }
+ template.queryParam(name, "{" + key + "}");
+ }
+ template.fragment(redirectUri.getFragment());
+ UriComponents encoded = template.build().expand(query).encode();
+ builder.query(encoded.getQuery());
+ }
+ return builder.build().toUriString();
+ }
+
+ public static String[] obtainBasicAuthorization(HttpServletRequest request) {
+ String clientId;
+ String clientSecret;
+ // 先从 Header 中获取
+ String authorization = request.getHeader("Authorization");
+ authorization = StrUtil.subAfter(authorization, "Basic ", true);
+ if (StringUtils.hasText(authorization)) {
+ authorization = Base64.decodeStr(authorization);
+ clientId = StrUtil.subBefore(authorization, ":", false);
+ clientSecret = StrUtil.subAfter(authorization, ":", false);
+ // 再从 Param 中获取
+ } else {
+ clientId = request.getParameter("client_id");
+ clientSecret = request.getParameter("client_secret");
+ }
+
+ // 如果两者非空,则返回
+ if (StrUtil.isNotEmpty(clientId) && StrUtil.isNotEmpty(clientSecret)) {
+ return new String[]{clientId, clientSecret};
+ }
+ return null;
+ }
+
+ /**
+ * HTTP post 请求,基于 {@link cn.hutool.http.HttpUtil} 实现
+ *
+ * 为什么要封装该方法,因为 HttpUtil 默认封装的方法,没有允许传递 headers 参数
+ *
+ * @param url URL
+ * @param headers 请求头
+ * @param requestBody 请求体
+ * @return 请求结果
+ */
+ public static String post(String url, Map headers, String requestBody) {
+ try (HttpResponse response = HttpRequest.post(url)
+ .addHeaders(headers)
+ .body(requestBody)
+ .execute()) {
+ return response.body();
+ }
+ }
+
+ /**
+ * HTTP get 请求,基于 {@link cn.hutool.http.HttpUtil} 实现
+ *
+ * 为什么要封装该方法,因为 HttpUtil 默认封装的方法,没有允许传递 headers 参数
+ *
+ * @param url URL
+ * @param headers 请求头
+ * @return 请求结果
+ */
+ public static String get(String url, Map headers) {
+ try (HttpResponse response = HttpRequest.get(url)
+ .addHeaders(headers)
+ .execute()) {
+ return response.body();
+ }
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/FileUtils.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/FileUtils.java
new file mode 100644
index 0000000..63732f1
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/FileUtils.java
@@ -0,0 +1,61 @@
+package cn.iocoder.yudao.framework.common.util.io;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.IdUtil;
+import lombok.SneakyThrows;
+
+import java.io.File;
+
+/**
+ * 文件工具类
+ *
+ * @author 芋道源码
+ */
+public class FileUtils {
+
+ /**
+ * 创建临时文件
+ * 该文件会在 JVM 退出时,进行删除
+ *
+ * @param data 文件内容
+ * @return 文件
+ */
+ @SneakyThrows
+ public static File createTempFile(String data) {
+ File file = createTempFile();
+ // 写入内容
+ FileUtil.writeUtf8String(data, file);
+ return file;
+ }
+
+ /**
+ * 创建临时文件
+ * 该文件会在 JVM 退出时,进行删除
+ *
+ * @param data 文件内容
+ * @return 文件
+ */
+ @SneakyThrows
+ public static File createTempFile(byte[] data) {
+ File file = createTempFile();
+ // 写入内容
+ FileUtil.writeBytes(data, file);
+ return file;
+ }
+
+ /**
+ * 创建临时文件,无内容
+ * 该文件会在 JVM 退出时,进行删除
+ *
+ * @return 文件
+ */
+ @SneakyThrows
+ public static File createTempFile() {
+ // 创建文件,通过 UUID 保证唯一
+ File file = File.createTempFile(IdUtil.simpleUUID(), null);
+ // 标记 JVM 退出时,自动删除
+ file.deleteOnExit();
+ return file;
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/IoUtils.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/IoUtils.java
new file mode 100644
index 0000000..4a19f47
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/IoUtils.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.framework.common.util.io;
+
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.io.InputStream;
+
+/**
+ * IO 工具类,用于 {@link cn.hutool.core.io.IoUtil} 缺失的方法
+ *
+ * @author 芋道源码
+ */
+public class IoUtils {
+
+ /**
+ * 从流中读取 UTF8 编码的内容
+ *
+ * @param in 输入流
+ * @param isClose 是否关闭
+ * @return 内容
+ * @throws IORuntimeException IO 异常
+ */
+ public static String readUtf8(InputStream in, boolean isClose) throws IORuntimeException {
+ return StrUtil.utf8Str(IoUtil.read(in, isClose));
+ }
+
+}
diff --git a/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java
new file mode 100644
index 0000000..8bb8765
--- /dev/null
+++ b/ruoyi-vue-pro-master-jdk17/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java
@@ -0,0 +1,210 @@
+package cn.iocoder.yudao.framework.common.util.json;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JSON 工具类
+ *
+ * @author 芋道源码
+ */
+@Slf4j
+public class JsonUtils {
+
+ private static ObjectMapper objectMapper = new ObjectMapper();
+
+ static {
+ objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+ objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值
+ objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
+ }
+
+ /**
+ * 初始化 objectMapper 属性
+ *
+ * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean
+ *
+ * @param objectMapper ObjectMapper 对象
+ */
+ public static void init(ObjectMapper objectMapper) {
+ JsonUtils.objectMapper = objectMapper;
+ }
+
+ @SneakyThrows
+ public static String toJsonString(Object object) {
+ return objectMapper.writeValueAsString(object);
+ }
+
+ @SneakyThrows
+ public static byte[] toJsonByte(Object object) {
+ return objectMapper.writeValueAsBytes(object);
+ }
+
+ @SneakyThrows
+ public static String toJsonPrettyString(Object object) {
+ return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
+ }
+
+ public static T parseObject(String text, Class