Ver código fonte

第一版后端,权限全部放开

sir lin 3 meses atrás
pai
commit
ea1e4767cf
100 arquivos alterados com 7382 adições e 138 exclusões
  1. 0 51
      .gitee/ISSUE_TEMPLATE/bug.yml
  2. 0 5
      .gitee/ISSUE_TEMPLATE/config.yml
  3. 0 43
      .gitee/ISSUE_TEMPLATE/feature.yml
  4. 0 7
      .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
  5. 3 3
      .run/ruoyi-monitor-admin.run.xml
  6. 3 3
      .run/ruoyi-server.run.xml
  7. 3 3
      .run/ruoyi-snailjob-server.run.xml
  8. 5 5
      README.md
  9. 41 18
      pom.xml
  10. 0 0
      rlzc-admin/Dockerfile
  11. 159 0
      rlzc-admin/pom.xml
  12. 38 0
      rlzc-admin/src/main/java/cn/rlzc/RlzcApplication.java
  13. 18 0
      rlzc-admin/src/main/java/cn/rlzc/RlzcServletInitializer.java
  14. 239 0
      rlzc-admin/src/main/java/cn/rlzc/web/controller/AuthController.java
  15. 154 0
      rlzc-admin/src/main/java/cn/rlzc/web/controller/CaptchaController.java
  16. 28 0
      rlzc-admin/src/main/java/cn/rlzc/web/controller/IndexController.java
  17. 25 0
      rlzc-admin/src/main/java/cn/rlzc/web/domain/vo/CaptchaVo.java
  18. 25 0
      rlzc-admin/src/main/java/cn/rlzc/web/domain/vo/LoginTenantVo.java
  19. 54 0
      rlzc-admin/src/main/java/cn/rlzc/web/domain/vo/LoginVo.java
  20. 31 0
      rlzc-admin/src/main/java/cn/rlzc/web/domain/vo/TenantListVo.java
  21. 165 0
      rlzc-admin/src/main/java/cn/rlzc/web/listener/UserActionListener.java
  22. 46 0
      rlzc-admin/src/main/java/cn/rlzc/web/service/IAuthStrategy.java
  23. 251 0
      rlzc-admin/src/main/java/cn/rlzc/web/service/SysLoginService.java
  24. 115 0
      rlzc-admin/src/main/java/cn/rlzc/web/service/SysRegisterService.java
  25. 102 0
      rlzc-admin/src/main/java/cn/rlzc/web/service/impl/EmailAuthStrategy.java
  26. 123 0
      rlzc-admin/src/main/java/cn/rlzc/web/service/impl/PasswordAuthStrategy.java
  27. 102 0
      rlzc-admin/src/main/java/cn/rlzc/web/service/impl/SmsAuthStrategy.java
  28. 131 0
      rlzc-admin/src/main/java/cn/rlzc/web/service/impl/SocialAuthStrategy.java
  29. 111 0
      rlzc-admin/src/main/java/cn/rlzc/web/service/impl/XcxAuthStrategy.java
  30. 278 0
      rlzc-admin/src/main/resources/application-dev.yml
  31. 267 0
      rlzc-admin/src/main/resources/application-prod.yml
  32. 296 0
      rlzc-admin/src/main/resources/application.yml
  33. 0 0
      rlzc-admin/src/main/resources/banner.txt
  34. 0 0
      rlzc-admin/src/main/resources/i18n/messages.properties
  35. 0 0
      rlzc-admin/src/main/resources/i18n/messages_en_US.properties
  36. 0 0
      rlzc-admin/src/main/resources/i18n/messages_zh_CN.properties
  37. 0 0
      rlzc-admin/src/main/resources/ip2region.xdb
  38. 0 0
      rlzc-admin/src/main/resources/logback-plus.xml
  39. BIN
      rlzc-admin/src/main/resources/upload/doc/250410101502/wKrZg6.doc
  40. BIN
      rlzc-admin/src/main/resources/upload/logo/250410/R2caFk/图片1.png
  41. BIN
      rlzc-admin/src/main/resources/upload/logo/250410/nb3iyT/2.jpg
  42. BIN
      rlzc-admin/src/main/resources/upload/logo/250410/viszeK/微信截图_20250409164730.png
  43. BIN
      rlzc-admin/src/main/resources/upload/logo/250413/LquPCb/log.png
  44. BIN
      rlzc-admin/src/main/resources/upload/sysnotice/250410/ISbbbn/安装和简单使用Milvus.doc
  45. 1500 0
      rlzc-admin/src/main/resources/upload/sysnotice/250410/j6azR3/题库.txt
  46. BIN
      rlzc-admin/src/main/resources/upload/sysnotice/250410/sgjMRY/处罚法.docx
  47. 45 0
      rlzc-admin/src/test/java/cn/rlzc/test/AssertUnitTest.java
  48. 70 0
      rlzc-admin/src/test/java/cn/rlzc/test/DemoUnitTest.java
  49. 72 0
      rlzc-admin/src/test/java/cn/rlzc/test/ParamUnitTest.java
  50. 54 0
      rlzc-admin/src/test/java/cn/rlzc/test/TagUnitTest.java
  51. 0 0
      rlzc-admin/zhFonts/.uuid
  52. 0 0
      rlzc-admin/zhFonts/SIMSUN.TTC
  53. 0 0
      rlzc-admin/zhFonts/fonts.dir
  54. 0 0
      rlzc-admin/zhFonts/fonts.scale
  55. 46 0
      rlzc-common/pom.xml
  56. 185 0
      rlzc-common/rlzc-common-bom/pom.xml
  57. 99 0
      rlzc-common/rlzc-common-core/pom.xml
  58. 17 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/config/ApplicationConfig.java
  59. 52 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/config/AsyncConfig.java
  60. 87 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/config/ThreadPoolConfig.java
  61. 41 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/config/ValidatorConfig.java
  62. 30 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/config/properties/ThreadPoolProperties.java
  63. 30 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/CacheConstants.java
  64. 88 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/CacheNames.java
  65. 76 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/Constants.java
  66. 34 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/GlobalConstants.java
  67. 93 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/HttpStatus.java
  68. 59 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/RegexConstants.java
  69. 80 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/SystemConstants.java
  70. 35 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/TenantConstants.java
  71. 110 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/R.java
  72. 71 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/CompleteTaskDTO.java
  73. 36 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/DeptDTO.java
  74. 41 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/DictDataDTO.java
  75. 41 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/DictTypeDTO.java
  76. 30 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/FlowCopyDTO.java
  77. 46 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/OssDTO.java
  78. 46 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/PostDTO.java
  79. 42 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/RoleDTO.java
  80. 45 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/StartProcessDTO.java
  81. 30 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/StartProcessReturnDTO.java
  82. 101 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/TaskAssigneeDTO.java
  83. 73 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/UserDTO.java
  84. 72 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/UserOnlineDTO.java
  85. 44 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/event/ProcessCreateTaskEvent.java
  86. 34 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/event/ProcessDeleteEvent.java
  87. 50 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/event/ProcessEvent.java
  88. 31 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/EmailLoginBody.java
  89. 48 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/LoginBody.java
  90. 148 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/LoginUser.java
  91. 31 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/PasswordLoginBody.java
  92. 33 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/RegisterBody.java
  93. 29 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/SmsLoginBody.java
  94. 35 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/SocialLoginBody.java
  95. 56 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/TaskAssigneeBody.java
  96. 28 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/XcxLoginBody.java
  97. 27 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/XcxLoginUser.java
  98. 215 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/enums/BusinessStatusEnum.java
  99. 37 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/enums/DeviceType.java
  100. 146 0
      rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/enums/FormatsType.java

+ 0 - 51
.gitee/ISSUE_TEMPLATE/bug.yml

@@ -1,51 +0,0 @@
-name: Bug 反馈
-description: 当你使用过程中发现了一个 Bug,导致应用崩溃或抛出异常,或者有一个组件存在问题,或者某些地方看起来不对劲,请在这里反馈。
-title: "[Bug]: "
-labels: ["bug"]
-body:
-  - type: textarea
-    id: version
-    attributes:
-      label: 版本
-      description: 你当前正在使用我们软件的哪个版本(pom文件内的版本号)?
-      value: |
-        注意: 未填写版本号不予处理直接关闭或删除
-        jdk版本(带上尾号):
-        框架版本(项目启动时输出的版本号):
-        其他依赖版本(你觉得有必要的):
-    validations:
-      required: true
-  - type: checkboxes
-    attributes:
-      label: 功能不好用不会用是否已经看过项目文档?
-      options:
-        - label: https://plus-doc.dromara.org
-          required: true
-  - type: checkboxes
-    attributes:
-      label: 这个问题是否已经存在?
-      options:
-        - label: 我已经搜索过现有的问题 (https://gitee.com/dromara/RuoYi-Vue-Plus/issues)
-          required: true
-  - type: textarea
-    attributes:
-      label: 希望结果
-      description: 想知道你觉得怎么样是正常或者合理的。
-    validations:
-      required: true
-  - type: markdown
-    attributes:
-      label: 如何复现
-      description: 请详细告诉我们如何复现你遇到的问题。
-      value: |
-        如涉及代码,可提供一个最小代码示例,并使用```附上它,或者截图均可,越详细越好。<br>
-        大多数问题都是:代码编写错误问题,逻辑问题,或者用法错误等问题。
-    validations:
-      required: true
-  - type: textarea
-    attributes:
-      label: 相关代码与报错信息(请勿发混乱格式)
-      description: 如果可以的话,上传任何关于 bug 的截图。
-      value: |
-        [在这里上传图片]
-

+ 0 - 5
.gitee/ISSUE_TEMPLATE/config.yml

@@ -1,5 +0,0 @@
-blank_issues_enabled: false
-contact_links:
-  - name: RuoYi-Vue-Plus 文档中心
-    url: https://plus-doc.dromara.org
-    about: 提供 RuoYi-Vue-Plus 搭建使用指南、平台基本开发使用方式、介绍、基础知识和常见问题解答

+ 0 - 43
.gitee/ISSUE_TEMPLATE/feature.yml

@@ -1,43 +0,0 @@
-name: 功能建议
-description: 对本项目提出一个功能建议。
-title: "[功能建议]: "
-labels: ["enhancement"]
-body:
-  - type: markdown
-    attributes:
-      value: |
-        感谢提出功能建议,我们将仔细考虑!请持续关注该issues,在加入计划后我们会有贡献者设置为负责人,同时状态成为进行中。
-  - type: textarea
-    id: related-problem
-    attributes:
-      label: 你的功能建议是否和某个问题相关?
-      description: 清晰并简洁地描述问题是什么,例如,当我...时,我总是感到困扰。
-    validations:
-      required: false
-  - type: textarea
-    id: desired-solution
-    attributes:
-      label: 你希望看到什么解决方案?
-      description: 清晰并简洁地描述你希望发生的事情。
-    validations:
-      required: true
-  - type: textarea
-    id: alternatives
-    attributes:
-      label: 你考虑过哪些替代方案?
-      description: 清晰并简洁地描述你考虑过的任何替代解决方案或功能。
-    validations:
-      required: false
-  - type: textarea
-    id: additional-context
-    attributes:
-      label: 你有其他上下文或截图吗?
-      description: 在此处添加有关功能请求的任何其他上下文或截图。
-    validations:
-      required: false
-  - type: checkboxes
-    attributes:
-      label: 意向参与贡献
-      options:
-        - label: 我有意向参与具体功能的开发实现并将代码贡献回到上游社区。
-          required: false

+ 0 - 7
.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md

@@ -1,7 +0,0 @@
-### 更改目的 解决了什么问题(请提交到dev分支)
-
-
-### 改动逻辑 这么写的思路(让作者更好的理解你的意图)
-
-
-### 测试 都做了哪些测试(未经过测试不采纳)

+ 3 - 3
.run/ruoyi-monitor-admin.run.xml

@@ -1,10 +1,10 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
+  <configuration default="false" name="rlzc-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
     <deployment type="dockerfile">
       <settings>
-        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.3.1" />
+        <option name="imageTag" value="rlzc/rlzc-monitor-admin:5.3.1" />
         <option name="buildOnly" value="true" />
-        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
+        <option name="sourceFilePath" value="rlzc-extend/rlzc-monitor-admin/Dockerfile" />
       </settings>
     </deployment>
     <method v="2" />

+ 3 - 3
.run/ruoyi-server.run.xml

@@ -1,10 +1,10 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
+  <configuration default="false" name="rlzc-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
     <deployment type="dockerfile">
       <settings>
-        <option name="imageTag" value="ruoyi/ruoyi-server:5.3.1" />
+        <option name="imageTag" value="rlzc/rlzc-server:5.3.1" />
         <option name="buildOnly" value="true" />
-        <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
+        <option name="sourceFilePath" value="rlzc-admin/Dockerfile" />
       </settings>
     </deployment>
     <method v="2" />

+ 3 - 3
.run/ruoyi-snailjob-server.run.xml

@@ -1,10 +1,10 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
+  <configuration default="false" name="rlzc-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
     <deployment type="dockerfile">
       <settings>
-        <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.3.1" />
+        <option name="imageTag" value="rlzc/rlzc-snailjob-server:5.3.1" />
         <option name="buildOnly" value="true" />
-        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
+        <option name="sourceFilePath" value="rlzc-extend/rlzc-snailjob-server/Dockerfile" />
       </settings>
     </deployment>
     <method v="2" />

+ 5 - 5
README.md

@@ -23,7 +23,7 @@
 > 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
 
 > 官方前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)<br>
-> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)
+> 成员前端项目地址: 基于vben5 [rlzc-plus-vben5](https://gitee.com/dapppp/rlzc-plus-vben5)
 
 > 文档地址: [plus-doc](https://plus-doc.dromara.org)
 
@@ -120,14 +120,14 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
 
 使用框架前请仔细阅读文档重点注意事项
 <br>
->[初始化项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init)
->>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init)
+>[初始化项目 必看](https://plus-doc.dromara.org/#/rlzc-vue-plus/quickstart/init)
+>>[https://plus-doc.dromara.org/#/rlzc-vue-plus/quickstart/init](https://plus-doc.dromara.org/#/rlzc-vue-plus/quickstart/init)
 >
 >[专栏与视频 入门必看](https://plus-doc.dromara.org/#/common/column)
 >>[https://plus-doc.dromara.org/#/common/column](https://plus-doc.dromara.org/#/common/column)
 >
->[部署项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
->>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
+>[部署项目 必看](https://plus-doc.dromara.org/#/rlzc-vue-plus/quickstart/deploy)
+>>[https://plus-doc.dromara.org/#/rlzc-vue-plus/quickstart/deploy](https://plus-doc.dromara.org/#/rlzc-vue-plus/quickstart/deploy)
 >
 >[如何加群](https://plus-doc.dromara.org/#/common/add_group)
 >>[https://plus-doc.dromara.org/#/common/add_group](https://plus-doc.dromara.org/#/common/add_group)

+ 41 - 18
pom.xml

@@ -4,8 +4,8 @@
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
-    <groupId>org.dromara</groupId>
-    <artifactId>ruoyi-vue-plus</artifactId>
+    <groupId>cn.rlzc</groupId>
+    <artifactId>rlzc-vue-plus</artifactId>
     <version>${revision}</version>
 
     <name>RuoYi-Vue-Plus</name>
@@ -140,8 +140,8 @@
 
             <!-- common 的依赖配置-->
             <dependency>
-                <groupId>org.dromara</groupId>
-                <artifactId>ruoyi-common-bom</artifactId>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-bom</artifactId>
                 <version>${revision}</version>
                 <type>pom</type>
                 <scope>import</scope>
@@ -334,44 +334,67 @@
             </dependency>
 
             <dependency>
-                <groupId>org.dromara</groupId>
-                <artifactId>ruoyi-system</artifactId>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-system</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-ai</artifactId>
                 <version>${revision}</version>
             </dependency>
 
             <dependency>
-                <groupId>org.dromara</groupId>
-                <artifactId>ruoyi-job</artifactId>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-job</artifactId>
                 <version>${revision}</version>
             </dependency>
 
             <dependency>
-                <groupId>org.dromara</groupId>
-                <artifactId>ruoyi-generator</artifactId>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-generator</artifactId>
                 <version>${revision}</version>
             </dependency>
 
             <dependency>
-                <groupId>org.dromara</groupId>
-                <artifactId>ruoyi-demo</artifactId>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-demo</artifactId>
                 <version>${revision}</version>
             </dependency>
 
             <!--  工作流模块  -->
             <dependency>
-                <groupId>org.dromara</groupId>
-                <artifactId>ruoyi-workflow</artifactId>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-workflow</artifactId>
                 <version>${revision}</version>
             </dependency>
 
+
+            <dependency>
+                <groupId>com.jboltai</groupId>
+                <artifactId>jbolt_ai</artifactId>
+                <version>3.5.0</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.apache.logging.log4j</groupId>
+                        <artifactId>log4j-slf4j2-impl</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+<!--            <dependency>-->
+<!--                <groupId>io.milvus</groupId>-->
+<!--                <artifactId>milvus-sdk-java</artifactId>-->
+<!--                <version>2.5.4</version>-->
+<!--            </dependency>-->
         </dependencies>
     </dependencyManagement>
 
     <modules>
-        <module>ruoyi-admin</module>
-        <module>ruoyi-common</module>
-        <module>ruoyi-extend</module>
-        <module>ruoyi-modules</module>
+        <module>rlzc-admin</module>
+        <module>rlzc-common</module>
+        <module>rlzc-extend</module>
+        <module>rlzc-modules</module>
+
     </modules>
     <packaging>pom</packaging>
 

+ 0 - 0
ruoyi-admin/Dockerfile → rlzc-admin/Dockerfile


+ 159 - 0
rlzc-admin/pom.xml

@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>rlzc-vue-plus</artifactId>
+        <groupId>cn.rlzc</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+    <artifactId>rlzc-admin</artifactId>
+
+    <description>
+        web服务入口
+    </description>
+
+    <dependencies>
+
+        <!-- Mysql驱动包 -->
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+        </dependency>
+
+
+<!--        &lt;!&ndash; mp支持的数据库均支持 只需要增加对应的jdbc依赖即可 &ndash;&gt;-->
+<!--        &lt;!&ndash; Oracle &ndash;&gt;-->
+<!--        <dependency>-->
+<!--            <groupId>com.oracle.database.jdbc</groupId>-->
+<!--            <artifactId>ojdbc8</artifactId>-->
+<!--        </dependency>-->
+<!--        &lt;!&ndash; 兼容oracle低版本 &ndash;&gt;-->
+<!--        <dependency>-->
+<!--            <groupId>com.oracle.database.nls</groupId>-->
+<!--            <artifactId>orai18n</artifactId>-->
+<!--        </dependency>-->
+<!--        &lt;!&ndash; PostgreSql &ndash;&gt;-->
+<!--        <dependency>-->
+<!--            <groupId>org.postgresql</groupId>-->
+<!--            <artifactId>postgresql</artifactId>-->
+<!--        </dependency>-->
+<!--        &lt;!&ndash; SqlServer &ndash;&gt;-->
+<!--        <dependency>-->
+<!--            <groupId>com.microsoft.sqlserver</groupId>-->
+<!--            <artifactId>mssql-jdbc</artifactId>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>cn.rlzc</groupId>
+            <artifactId>rlzc-common-doc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.rlzc</groupId>
+            <artifactId>rlzc-common-social</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.rlzc</groupId>
+            <artifactId>rlzc-common-ratelimiter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.rlzc</groupId>
+            <artifactId>rlzc-common-mail</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.rlzc</groupId>
+            <artifactId>rlzc-system</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.rlzc</groupId>
+            <artifactId>rlzc-ai</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.rlzc</groupId>
+            <artifactId>rlzc-job</artifactId>
+        </dependency>
+
+        <!-- 代码生成-->
+        <dependency>
+            <groupId>cn.rlzc</groupId>
+            <artifactId>rlzc-generator</artifactId>
+        </dependency>
+
+        <!--  demo模块  -->
+        <dependency>
+            <groupId>cn.rlzc</groupId>
+            <artifactId>rlzc-demo</artifactId>
+        </dependency>
+
+        <!--  工作流模块  -->
+        <dependency>
+            <groupId>cn.rlzc</groupId>
+            <artifactId>rlzc-workflow</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>de.codecentric</groupId>
+            <artifactId>spring-boot-admin-starter-client</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- skywalking 整合 logback -->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.skywalking</groupId>-->
+<!--            <artifactId>apm-toolkit-logback-1.x</artifactId>-->
+<!--            <version>${与你的agent探针版本保持一致}</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.skywalking</groupId>-->
+<!--            <artifactId>apm-toolkit-trace</artifactId>-->
+<!--            <version>${与你的agent探针版本保持一致}</version>-->
+<!--        </dependency>-->
+
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>${maven-jar-plugin.version}</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <version>${maven-war-plugin.version}</version>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                    <warName>${project.artifactId}</warName>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 38 - 0
rlzc-admin/src/main/java/cn/rlzc/RlzcApplication.java

@@ -0,0 +1,38 @@
+package cn.rlzc;
+
+import org.mybatis.logging.Logger;
+import org.mybatis.logging.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * 启动程序
+ *
+ * @author Lion Li
+ */
+
+@SpringBootApplication
+public class RlzcApplication {
+    public static void main(String[] args) {
+
+        SpringApplication.run(RlzcApplication.class, args);
+        System.out.println(
+            "       █                  █               █                █        \n" +
+            "        █    █       █    █               █ █              █        \n" +
+            "   ████████████   █████   █               █  █         █   █        \n" +
+            "   █         █            █   █     ████████████   ██████  █        \n" +
+            "   █         █        █ ████████    █     █           █    █    █   \n" +
+            "   █         █   ███████  █   █     █     █           █ ██████████  \n" +
+            "   ███████████      █     █   █     █     █    █      █    █    █   \n" +
+            "   █                █     █   █     █████  █   █      █    █    █   \n" +
+            "   █         █     █      █   █     █   █  █  █       █    █    █   \n" +
+            "   ████████████    █  █   █   █     █   █  █  █       █    █    █   \n" +
+            "   ██        █    █    █ █    █     █   █   ██        ███ █     █   \n" +
+            "  █ █        █   ███████ █    █     █ █ █   █      ████   █     █   \n" +
+            "  █ █        █    █     █     █     █  █   ██   █   █     █     █   \n" +
+            " █  █        █          █     █    █      █  █  █        █  █   █   \n" +
+            "█   ██████████         █   █ █    █      █    █ █       █    █ █    \n" +
+            "    █        █        █     █                  ██      █      █     \n");
+    }
+
+}

+ 18 - 0
rlzc-admin/src/main/java/cn/rlzc/RlzcServletInitializer.java

@@ -0,0 +1,18 @@
+package cn.rlzc;
+
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+/**
+ * web容器中进行部署
+ *
+ * @author Lion Li
+ */
+public class RlzcServletInitializer extends SpringBootServletInitializer {
+
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+        return application.sources(RlzcApplication.class);
+    }
+
+}

+ 239 - 0
rlzc-admin/src/main/java/cn/rlzc/web/controller/AuthController.java

@@ -0,0 +1,239 @@
+package cn.rlzc.web.controller;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.dev33.satoken.exception.NotLoginException;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.model.AuthResponse;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthRequest;
+import me.zhyd.oauth.utils.AuthStateUtils;
+import cn.rlzc.common.core.constant.SystemConstants;
+import cn.rlzc.common.core.domain.R;
+import cn.rlzc.common.core.domain.model.LoginBody;
+import cn.rlzc.common.core.domain.model.RegisterBody;
+import cn.rlzc.common.core.domain.model.SocialLoginBody;
+import cn.rlzc.common.core.utils.*;
+import cn.rlzc.common.encrypt.annotation.ApiEncrypt;
+import cn.rlzc.common.json.utils.JsonUtils;
+import cn.rlzc.common.satoken.utils.LoginHelper;
+import cn.rlzc.common.social.config.properties.SocialLoginConfigProperties;
+import cn.rlzc.common.social.config.properties.SocialProperties;
+import cn.rlzc.common.social.utils.SocialUtils;
+import cn.rlzc.common.sse.dto.SseMessageDto;
+import cn.rlzc.common.sse.utils.SseMessageUtils;
+import cn.rlzc.common.tenant.helper.TenantHelper;
+import cn.rlzc.system.domain.bo.SysTenantBo;
+import cn.rlzc.system.domain.vo.SysClientVo;
+import cn.rlzc.system.domain.vo.SysTenantVo;
+import cn.rlzc.system.service.ISysClientService;
+import cn.rlzc.system.service.ISysConfigService;
+import cn.rlzc.system.service.ISysSocialService;
+import cn.rlzc.system.service.ISysTenantService;
+import cn.rlzc.web.domain.vo.LoginTenantVo;
+import cn.rlzc.web.domain.vo.LoginVo;
+import cn.rlzc.web.domain.vo.TenantListVo;
+import cn.rlzc.web.service.IAuthStrategy;
+import cn.rlzc.web.service.SysLoginService;
+import cn.rlzc.web.service.SysRegisterService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 认证
+ *
+ * @author Lion Li
+ */
+@Slf4j
+@SaIgnore
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/auth")
+public class AuthController {
+
+    private final SocialProperties socialProperties;
+    private final SysLoginService loginService;
+    private final SysRegisterService registerService;
+    private final ISysConfigService configService;
+    private final ISysTenantService tenantService;
+    private final ISysSocialService socialUserService;
+    private final ISysClientService clientService;
+    private final ScheduledExecutorService scheduledExecutorService;
+
+
+    /**
+     * 登录方法
+     *
+     * @param body 登录信息
+     * @return 结果
+     */
+    @ApiEncrypt
+    @PostMapping("/login")
+    public R<LoginVo> login(@RequestBody String body) {
+        LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        // 授权类型和客户端id
+        String clientId = loginBody.getClientId();
+        String grantType = loginBody.getGrantType();
+        SysClientVo client = clientService.queryByClientId(clientId);
+        // 查询不到 client 或 client 内不包含 grantType
+        if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
+            log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
+            return R.fail(MessageUtils.message("auth.grant.type.error"));
+        } else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
+            return R.fail(MessageUtils.message("auth.grant.type.blocked"));
+        }
+        // 校验租户
+        loginService.checkTenant(loginBody.getTenantId());
+        // 登录
+        LoginVo loginVo = IAuthStrategy.login(body, client, grantType);
+
+        Long userId = LoginHelper.getUserId();
+        scheduledExecutorService.schedule(() -> {
+            SseMessageDto dto = new SseMessageDto();
+            dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统");
+            dto.setUserIds(List.of(userId));
+            SseMessageUtils.publishMessage(dto);
+        }, 5, TimeUnit.SECONDS);
+        return R.ok(loginVo);
+    }
+
+    /**
+     * 获取跳转URL
+     *
+     * @param source 登录来源
+     * @return 结果
+     */
+    @GetMapping("/binding/{source}")
+    public R<String> authBinding(@PathVariable("source") String source,
+                                 @RequestParam String tenantId, @RequestParam String domain) {
+        SocialLoginConfigProperties obj = socialProperties.getType().get(source);
+        if (ObjectUtil.isNull(obj)) {
+            return R.fail(source + "平台账号暂不支持");
+        }
+        AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
+        Map<String, String> map = new HashMap<>();
+        map.put("tenantId", tenantId);
+        map.put("domain", domain);
+        map.put("state", AuthStateUtils.createState());
+        String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8));
+        return R.ok("操作成功", authorizeUrl);
+    }
+
+    /**
+     * 前端回调绑定授权(需要token)
+     *
+     * @param loginBody 请求体
+     * @return 结果
+     */
+    @PostMapping("/social/callback")
+    public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
+        // 校验token
+        StpUtil.checkLogin();
+        // 获取第三方登录信息
+        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
+                loginBody.getSource(), loginBody.getSocialCode(),
+                loginBody.getSocialState(), socialProperties);
+        AuthUser authUserData = response.getData();
+        // 判断授权响应是否成功
+        if (!response.ok()) {
+            return R.fail(response.getMsg());
+        }
+        loginService.socialRegister(authUserData);
+        return R.ok();
+    }
+
+
+    /**
+     * 取消授权(需要token)
+     *
+     * @param socialId socialId
+     */
+    @DeleteMapping(value = "/unlock/{socialId}")
+    public R<Void> unlockSocial(@PathVariable Long socialId) {
+        // 校验token
+        StpUtil.checkLogin();
+        Boolean rows = socialUserService.deleteWithValidById(socialId);
+        return rows ? R.ok() : R.fail("取消授权失败");
+    }
+
+
+    /**
+     * 退出登录
+     */
+    @PostMapping("/logout")
+    public R<Void> logout() {
+        loginService.logout();
+        return R.ok("退出成功");
+    }
+
+    /**
+     * 用户注册
+     */
+    @ApiEncrypt
+    @PostMapping("/register")
+    public R<Void> register(@Validated @RequestBody RegisterBody user) {
+        if (!configService.selectRegisterEnabled(user.getTenantId())) {
+            return R.fail("当前系统没有开启注册功能!");
+        }
+        registerService.register(user);
+        return R.ok();
+    }
+
+    /**
+     * 登录页面租户下拉框
+     *
+     * @return 租户列表
+     */
+    @GetMapping("/tenant/list")
+    public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
+        // 返回对象
+        LoginTenantVo result = new LoginTenantVo();
+        boolean enable = TenantHelper.isEnable();
+        result.setTenantEnabled(enable);
+        // 如果未开启租户这直接返回
+        if (!enable) {
+            return R.ok(result);
+        }
+
+        List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
+        List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class);
+        try {
+            // 如果只超管返回所有租户
+            if (LoginHelper.isSuperAdmin()) {
+                result.setVoList(voList);
+                return R.ok(result);
+            }
+        } catch (NotLoginException ignored) {
+        }
+
+        // 获取域名
+        String host;
+        String referer = request.getHeader("referer");
+        if (StringUtils.isNotBlank(referer)) {
+            // 这里从referer中取值是为了本地使用hosts添加虚拟域名,方便本地环境调试
+            host = referer.split("//")[1].split("/")[0];
+        } else {
+            host = new URL(request.getRequestURL().toString()).getHost();
+        }
+        // 根据域名进行筛选
+        List<TenantListVo> list = StreamUtils.filter(voList, vo ->
+            StringUtils.equalsIgnoreCase(vo.getDomain(), host));
+        result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
+        return R.ok(result);
+    }
+
+}

+ 154 - 0
rlzc-admin/src/main/java/cn/rlzc/web/controller/CaptchaController.java

@@ -0,0 +1,154 @@
+package cn.rlzc.web.controller;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.hutool.captcha.AbstractCaptcha;
+import cn.hutool.captcha.generator.CodeGenerator;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.RandomUtil;
+import jakarta.validation.constraints.NotBlank;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import cn.rlzc.common.core.constant.Constants;
+import cn.rlzc.common.core.constant.GlobalConstants;
+import cn.rlzc.common.core.domain.R;
+import cn.rlzc.common.core.exception.ServiceException;
+import cn.rlzc.common.core.utils.SpringUtils;
+import cn.rlzc.common.core.utils.StringUtils;
+import cn.rlzc.common.core.utils.reflect.ReflectUtils;
+import cn.rlzc.common.mail.config.properties.MailProperties;
+import cn.rlzc.common.mail.utils.MailUtils;
+import cn.rlzc.common.ratelimiter.annotation.RateLimiter;
+import cn.rlzc.common.ratelimiter.enums.LimitType;
+import cn.rlzc.common.redis.utils.RedisUtils;
+import cn.rlzc.common.web.config.properties.CaptchaProperties;
+import cn.rlzc.common.web.enums.CaptchaType;
+import cn.rlzc.web.domain.vo.CaptchaVo;
+import org.dromara.sms4j.api.SmsBlend;
+import org.dromara.sms4j.api.entity.SmsResponse;
+import org.dromara.sms4j.core.factory.SmsFactory;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.Duration;
+import java.util.LinkedHashMap;
+
+/**
+ * 验证码操作处理
+ *
+ * @author Lion Li
+ */
+@SaIgnore
+@Slf4j
+@Validated
+@RequiredArgsConstructor
+@RestController
+public class CaptchaController {
+
+    private final CaptchaProperties captchaProperties;
+    private final MailProperties mailProperties;
+
+    /**
+     * 短信验证码
+     *
+     * @param phonenumber 用户手机号
+     */
+    @RateLimiter(key = "#phonenumber", time = 60, count = 1)
+    @GetMapping("/resource/sms/code")
+    public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
+        String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
+        String code = RandomUtil.randomNumbers(4);
+        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
+        // 验证码模板id 自行处理 (查数据库或写死均可)
+        String templateId = "";
+        LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
+        map.put("code", code);
+        SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
+        SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
+        if (!smsResponse.isSuccess()) {
+            log.error("验证码短信发送异常 => {}", smsResponse);
+            return R.fail(smsResponse.getData().toString());
+        }
+        return R.ok();
+    }
+
+    /**
+     * 邮箱验证码
+     *
+     * @param email 邮箱
+     */
+    @GetMapping("/resource/email/code")
+    public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
+        if (!mailProperties.getEnabled()) {
+            return R.fail("当前系统没有开启邮箱功能!");
+        }
+        SpringUtils.getAopProxy(this).emailCodeImpl(email);
+        return R.ok();
+    }
+
+    /**
+     * 邮箱验证码
+     * 独立方法避免验证码关闭之后仍然走限流
+     */
+    @RateLimiter(key = "#email", time = 60, count = 1)
+    public void emailCodeImpl(String email) {
+        String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
+        String code = RandomUtil.randomNumbers(4);
+        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
+        try {
+            MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
+        } catch (Exception e) {
+            log.error("验证码短信发送异常 => {}", e.getMessage());
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 生成验证码
+     */
+    @GetMapping("/auth/code")
+    public R<CaptchaVo> getCode() {
+        boolean captchaEnabled = captchaProperties.getEnable();
+        if (!captchaEnabled) {
+            CaptchaVo captchaVo = new CaptchaVo();
+            captchaVo.setCaptchaEnabled(false);
+            return R.ok(captchaVo);
+        }
+        return R.ok(SpringUtils.getAopProxy(this).getCodeImpl());
+    }
+
+    /**
+     * 生成验证码
+     * 独立方法避免验证码关闭之后仍然走限流
+     */
+    @RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
+    public CaptchaVo getCodeImpl() {
+        // 保存验证码信息
+        String uuid = IdUtil.simpleUUID();
+        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
+        // 生成验证码
+        CaptchaType captchaType = captchaProperties.getType();
+        boolean isMath = CaptchaType.MATH == captchaType;
+        Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
+        CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
+        AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
+        captcha.setGenerator(codeGenerator);
+        captcha.createCode();
+        // 如果是数学验证码,使用SpEL表达式处理验证码结果
+        String code = captcha.getCode();
+        if (isMath) {
+            ExpressionParser parser = new SpelExpressionParser();
+            Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
+            code = exp.getValue(String.class);
+        }
+        RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
+        CaptchaVo captchaVo = new CaptchaVo();
+        captchaVo.setUuid(uuid);
+        captchaVo.setImg(captcha.getImageBase64());
+        return captchaVo;
+    }
+
+}

+ 28 - 0
rlzc-admin/src/main/java/cn/rlzc/web/controller/IndexController.java

@@ -0,0 +1,28 @@
+package cn.rlzc.web.controller;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import lombok.RequiredArgsConstructor;
+import cn.rlzc.common.core.utils.SpringUtils;
+import cn.rlzc.common.core.utils.StringUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 首页
+ *
+ * @author Lion Li
+ */
+@SaIgnore
+@RequiredArgsConstructor
+@RestController
+public class IndexController {
+
+    /**
+     * 访问首页,提示语
+     */
+    @GetMapping("/")
+    public String index() {
+        return StringUtils.format("欢迎使用{}后台管理框架,请通过前端地址访问。", SpringUtils.getApplicationName());
+    }
+
+}

+ 25 - 0
rlzc-admin/src/main/java/cn/rlzc/web/domain/vo/CaptchaVo.java

@@ -0,0 +1,25 @@
+package cn.rlzc.web.domain.vo;
+
+import lombok.Data;
+
+/**
+ * 验证码信息
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class CaptchaVo {
+
+    /**
+     * 是否开启验证码
+     */
+    private Boolean captchaEnabled = true;
+
+    private String uuid;
+
+    /**
+     * 验证码图片
+     */
+    private String img;
+
+}

+ 25 - 0
rlzc-admin/src/main/java/cn/rlzc/web/domain/vo/LoginTenantVo.java

@@ -0,0 +1,25 @@
+package cn.rlzc.web.domain.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 登录租户对象
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class LoginTenantVo {
+
+    /**
+     * 租户开关
+     */
+    private Boolean tenantEnabled;
+
+    /**
+     * 租户对象列表
+     */
+    private List<TenantListVo> voList;
+
+}

+ 54 - 0
rlzc-admin/src/main/java/cn/rlzc/web/domain/vo/LoginVo.java

@@ -0,0 +1,54 @@
+package cn.rlzc.web.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 登录验证信息
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class LoginVo {
+
+    /**
+     * 授权令牌
+     */
+    @JsonProperty("access_token")
+    private String accessToken;
+
+    /**
+     * 刷新令牌
+     */
+    @JsonProperty("refresh_token")
+    private String refreshToken;
+
+    /**
+     * 授权令牌 access_token 的有效期
+     */
+    @JsonProperty("expire_in")
+    private Long expireIn;
+
+    /**
+     * 刷新令牌 refresh_token 的有效期
+     */
+    @JsonProperty("refresh_expire_in")
+    private Long refreshExpireIn;
+
+    /**
+     * 应用id
+     */
+    @JsonProperty("client_id")
+    private String clientId;
+
+    /**
+     * 令牌权限
+     */
+    private String scope;
+
+    /**
+     * 用户 openid
+     */
+    private String openid;
+
+}

+ 31 - 0
rlzc-admin/src/main/java/cn/rlzc/web/domain/vo/TenantListVo.java

@@ -0,0 +1,31 @@
+package cn.rlzc.web.domain.vo;
+
+import cn.rlzc.system.domain.vo.SysTenantVo;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+/**
+ * 租户列表
+ *
+ * @author Lion Li
+ */
+@Data
+@AutoMapper(target = SysTenantVo.class)
+public class TenantListVo {
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 企业名称
+     */
+    private String companyName;
+
+    /**
+     * 域名
+     */
+    private String domain;
+
+}

+ 165 - 0
rlzc-admin/src/main/java/cn/rlzc/web/listener/UserActionListener.java

@@ -0,0 +1,165 @@
+package cn.rlzc.web.listener;
+
+import cn.dev33.satoken.config.SaTokenConfig;
+import cn.dev33.satoken.listener.SaTokenListener;
+import cn.dev33.satoken.stp.SaLoginModel;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import cn.rlzc.common.core.constant.CacheConstants;
+import cn.rlzc.common.core.constant.Constants;
+import cn.rlzc.common.core.domain.dto.UserOnlineDTO;
+import cn.rlzc.common.core.utils.MessageUtils;
+import cn.rlzc.common.core.utils.ServletUtils;
+import cn.rlzc.common.core.utils.SpringUtils;
+import cn.rlzc.common.core.utils.ip.AddressUtils;
+import cn.rlzc.common.log.event.LogininforEvent;
+import cn.rlzc.common.redis.utils.RedisUtils;
+import cn.rlzc.common.satoken.utils.LoginHelper;
+import cn.rlzc.common.tenant.helper.TenantHelper;
+import cn.rlzc.web.service.SysLoginService;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+
+/**
+ * 用户行为 侦听器的实现
+ *
+ * @author Lion Li
+ */
+@RequiredArgsConstructor
+@Component
+@Slf4j
+public class UserActionListener implements SaTokenListener {
+
+    private final SaTokenConfig tokenConfig;
+    private final SysLoginService loginService;
+
+    /**
+     * 每次登录时触发
+     */
+    @Override
+    public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
+        UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
+        String ip = ServletUtils.getClientIP();
+        UserOnlineDTO dto = new UserOnlineDTO();
+        dto.setIpaddr(ip);
+        dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
+        dto.setBrowser(userAgent.getBrowser().getName());
+        dto.setOs(userAgent.getOs().getName());
+        dto.setLoginTime(System.currentTimeMillis());
+        dto.setTokenId(tokenValue);
+        String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY);
+        String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY);
+        dto.setUserName(username);
+        dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY));
+        dto.setDeviceType(loginModel.getDevice());
+        dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY));
+        TenantHelper.dynamic(tenantId, () -> {
+            if(tokenConfig.getTimeout() == -1) {
+                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
+            } else {
+                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
+            }
+        });
+        // 记录登录日志
+        LogininforEvent logininforEvent = new LogininforEvent();
+        logininforEvent.setTenantId(tenantId);
+        logininforEvent.setUsername(username);
+        logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
+        logininforEvent.setMessage(MessageUtils.message("user.login.success"));
+        logininforEvent.setRequest(ServletUtils.getRequest());
+        SpringUtils.context().publishEvent(logininforEvent);
+        // 更新登录信息
+        loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip);
+        log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
+    }
+
+    /**
+     * 每次注销时触发
+     */
+    @Override
+    public void doLogout(String loginType, Object loginId, String tokenValue) {
+        String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
+        TenantHelper.dynamic(tenantId, () -> {
+            RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
+        });
+        log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue);
+    }
+
+    /**
+     * 每次被踢下线时触发
+     */
+    @Override
+    public void doKickout(String loginType, Object loginId, String tokenValue) {
+        String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
+        TenantHelper.dynamic(tenantId, () -> {
+            RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
+        });
+        log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue);
+    }
+
+    /**
+     * 每次被顶下线时触发
+     */
+    @Override
+    public void doReplaced(String loginType, Object loginId, String tokenValue) {
+        String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
+        TenantHelper.dynamic(tenantId, () -> {
+            RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
+        });
+        log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue);
+    }
+
+    /**
+     * 每次被封禁时触发
+     */
+    @Override
+    public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
+    }
+
+    /**
+     * 每次被解封时触发
+     */
+    @Override
+    public void doUntieDisable(String loginType, Object loginId, String service) {
+    }
+
+    /**
+     * 每次打开二级认证时触发
+     */
+    @Override
+    public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
+    }
+
+    /**
+     * 每次创建Session时触发
+     */
+    @Override
+    public void doCloseSafe(String loginType, String tokenValue, String service) {
+    }
+
+    /**
+     * 每次创建Session时触发
+     */
+    @Override
+    public void doCreateSession(String id) {
+    }
+
+    /**
+     * 每次注销Session时触发
+     */
+    @Override
+    public void doLogoutSession(String id) {
+    }
+
+    /**
+     * 每次Token续期时触发
+     */
+    @Override
+    public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
+    }
+}

+ 46 - 0
rlzc-admin/src/main/java/cn/rlzc/web/service/IAuthStrategy.java

@@ -0,0 +1,46 @@
+package cn.rlzc.web.service;
+
+
+import cn.rlzc.common.core.exception.ServiceException;
+import cn.rlzc.common.core.utils.SpringUtils;
+import cn.rlzc.system.domain.SysClient;
+import cn.rlzc.system.domain.vo.SysClientVo;
+import cn.rlzc.web.domain.vo.LoginVo;
+
+/**
+ * 授权策略
+ *
+ * @author Michelle.Chung
+ */
+public interface IAuthStrategy {
+
+    String BASE_NAME = "AuthStrategy";
+
+    /**
+     * 登录
+     *
+     * @param body      登录对象
+     * @param client    授权管理视图对象
+     * @param grantType 授权类型
+     * @return 登录验证信息
+     */
+    static LoginVo login(String body, SysClientVo client, String grantType) {
+        // 授权类型和客户端id
+        String beanName = grantType + BASE_NAME;
+        if (!SpringUtils.containsBean(beanName)) {
+            throw new ServiceException("授权类型不正确!");
+        }
+        IAuthStrategy instance = SpringUtils.getBean(beanName);
+        return instance.login(body, client);
+    }
+
+    /**
+     * 登录
+     *
+     * @param body   登录对象
+     * @param client 授权管理视图对象
+     * @return 登录验证信息
+     */
+    LoginVo login(String body, SysClientVo client);
+
+}

+ 251 - 0
rlzc-admin/src/main/java/cn/rlzc/web/service/SysLoginService.java

@@ -0,0 +1,251 @@
+package cn.rlzc.web.service;
+
+import cn.dev33.satoken.exception.NotLoginException;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Opt;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.lock.annotation.Lock4j;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.model.AuthUser;
+import cn.rlzc.common.core.constant.CacheConstants;
+import cn.rlzc.common.core.constant.Constants;
+import cn.rlzc.common.core.constant.SystemConstants;
+import cn.rlzc.common.core.constant.TenantConstants;
+import cn.rlzc.common.core.domain.dto.PostDTO;
+import cn.rlzc.common.core.domain.dto.RoleDTO;
+import cn.rlzc.common.core.domain.model.LoginUser;
+import cn.rlzc.common.core.enums.LoginType;
+import cn.rlzc.common.core.exception.ServiceException;
+import cn.rlzc.common.core.exception.user.UserException;
+import cn.rlzc.common.core.utils.*;
+import cn.rlzc.common.log.event.LogininforEvent;
+import cn.rlzc.common.mybatis.helper.DataPermissionHelper;
+import cn.rlzc.common.redis.utils.RedisUtils;
+import cn.rlzc.common.satoken.utils.LoginHelper;
+import cn.rlzc.common.tenant.exception.TenantException;
+import cn.rlzc.common.tenant.helper.TenantHelper;
+import cn.rlzc.system.domain.SysUser;
+import cn.rlzc.system.domain.bo.SysSocialBo;
+import cn.rlzc.system.domain.vo.*;
+import cn.rlzc.system.mapper.SysUserMapper;
+import cn.rlzc.system.service.*;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.time.Duration;
+import java.util.Date;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * 登录校验方法
+ *
+ * @author Lion Li
+ */
+@RequiredArgsConstructor
+@Slf4j
+@Service
+public class SysLoginService {
+
+    @Value("${user.password.maxRetryCount}")
+    private Integer maxRetryCount;
+
+    @Value("${user.password.lockTime}")
+    private Integer lockTime;
+
+    private final ISysTenantService tenantService;
+    private final ISysPermissionService permissionService;
+    private final ISysSocialService sysSocialService;
+    private final ISysRoleService roleService;
+    private final ISysDeptService deptService;
+    private final ISysPostService postService;
+    private final SysUserMapper userMapper;
+
+
+    /**
+     * 绑定第三方用户
+     *
+     * @param authUserData 授权响应实体
+     */
+    @Lock4j
+    public void socialRegister(AuthUser authUserData) {
+        String authId = authUserData.getSource() + authUserData.getUuid();
+        // 第三方用户信息
+        SysSocialBo bo = BeanUtil.toBean(authUserData, SysSocialBo.class);
+        BeanUtil.copyProperties(authUserData.getToken(), bo);
+        Long userId = LoginHelper.getUserId();
+        bo.setUserId(userId);
+        bo.setAuthId(authId);
+        bo.setOpenId(authUserData.getUuid());
+        bo.setUserName(authUserData.getUsername());
+        bo.setNickName(authUserData.getNickname());
+        List<SysSocialVo> checkList = sysSocialService.selectByAuthId(authId);
+        if (CollUtil.isNotEmpty(checkList)) {
+            throw new ServiceException("此三方账号已经被绑定!");
+        }
+        // 查询是否已经绑定用户
+        SysSocialBo params = new SysSocialBo();
+        params.setUserId(userId);
+        params.setSource(bo.getSource());
+        List<SysSocialVo> list = sysSocialService.queryList(params);
+        if (CollUtil.isEmpty(list)) {
+            // 没有绑定用户, 新增用户信息
+            sysSocialService.insertByBo(bo);
+        } else {
+            // 更新用户信息
+            bo.setId(list.get(0).getId());
+            sysSocialService.updateByBo(bo);
+            // 如果要绑定的平台账号已经被绑定过了 是否抛异常自行决断
+            // throw new ServiceException("此平台账号已经被绑定!");
+        }
+    }
+
+
+    /**
+     * 退出登录
+     */
+    public void logout() {
+        try {
+            LoginUser loginUser = LoginHelper.getLoginUser();
+            if (ObjectUtil.isNull(loginUser)) {
+                return;
+            }
+            if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
+                // 超级管理员 登出清除动态租户
+                TenantHelper.clearDynamic();
+            }
+            recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
+        } catch (NotLoginException ignored) {
+        } finally {
+            try {
+                StpUtil.logout();
+            } catch (NotLoginException ignored) {
+            }
+        }
+    }
+
+    /**
+     * 记录登录信息
+     *
+     * @param tenantId 租户ID
+     * @param username 用户名
+     * @param status   状态
+     * @param message  消息内容
+     */
+    public void recordLogininfor(String tenantId, String username, String status, String message) {
+        LogininforEvent logininforEvent = new LogininforEvent();
+        logininforEvent.setTenantId(tenantId);
+        logininforEvent.setUsername(username);
+        logininforEvent.setStatus(status);
+        logininforEvent.setMessage(message);
+        logininforEvent.setRequest(ServletUtils.getRequest());
+        SpringUtils.context().publishEvent(logininforEvent);
+    }
+
+    /**
+     * 构建登录用户
+     */
+    public LoginUser buildLoginUser(SysUserVo user) {
+        LoginUser loginUser = new LoginUser();
+        Long userId = user.getUserId();
+        loginUser.setTenantId(user.getTenantId());
+        loginUser.setUserId(userId);
+        loginUser.setDeptId(user.getDeptId());
+        loginUser.setUsername(user.getUserName());
+        loginUser.setNickname(user.getNickName());
+        loginUser.setUserType(user.getUserType());
+        loginUser.setMenuPermission(permissionService.getMenuPermission(userId));
+        loginUser.setRolePermission(permissionService.getRolePermission(userId));
+        if (ObjectUtil.isNotNull(user.getDeptId())) {
+            Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById);
+            loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY));
+            loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY));
+        }
+        List<SysRoleVo> roles = roleService.selectRolesByUserId(userId);
+        List<SysPostVo> posts = postService.selectPostsByUserId(userId);
+        loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
+        loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class));
+        return loginUser;
+    }
+
+    /**
+     * 记录登录信息
+     *
+     * @param userId 用户ID
+     */
+    public void recordLoginInfo(Long userId, String ip) {
+        SysUser sysUser = new SysUser();
+        sysUser.setUserId(userId);
+        sysUser.setLoginIp(ip);
+        sysUser.setLoginDate(DateUtils.getNowDate());
+        sysUser.setUpdateBy(userId);
+        DataPermissionHelper.ignore(() -> userMapper.updateById(sysUser));
+    }
+
+    /**
+     * 登录校验
+     */
+    public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
+        String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;
+        String loginFail = Constants.LOGIN_FAIL;
+
+        // 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip)
+        int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0);
+        // 锁定时间内登录 则踢出
+        if (errorNumber >= maxRetryCount) {
+            recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
+            throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
+        }
+
+        if (supplier.get()) {
+            // 错误次数递增
+            errorNumber++;
+            RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
+            // 达到规定错误次数 则锁定登录
+            if (errorNumber >= maxRetryCount) {
+                recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
+                throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
+            } else {
+                // 未达到规定错误次数
+                recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
+                throw new UserException(loginType.getRetryLimitCount(), errorNumber);
+            }
+        }
+
+        // 登录成功 清空错误次数
+        RedisUtils.deleteObject(errorKey);
+    }
+
+    /**
+     * 校验租户
+     *
+     * @param tenantId 租户ID
+     */
+    public void checkTenant(String tenantId) {
+        if (!TenantHelper.isEnable()) {
+            return;
+        }
+        if (StringUtils.isBlank(tenantId)) {
+            throw new TenantException("tenant.number.not.blank");
+        }
+        if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
+            return;
+        }
+        SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
+        if (ObjectUtil.isNull(tenant)) {
+            log.info("登录租户:{} 不存在.", tenantId);
+            throw new TenantException("tenant.not.exists");
+        } else if (SystemConstants.DISABLE.equals(tenant.getStatus())) {
+            log.info("登录租户:{} 已被停用.", tenantId);
+            throw new TenantException("tenant.blocked");
+        } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
+            && new Date().after(tenant.getExpireTime())) {
+            log.info("登录租户:{} 已超过有效期.", tenantId);
+            throw new TenantException("tenant.expired");
+        }
+    }
+
+}

+ 115 - 0
rlzc-admin/src/main/java/cn/rlzc/web/service/SysRegisterService.java

@@ -0,0 +1,115 @@
+package cn.rlzc.web.service;
+
+import cn.dev33.satoken.secure.BCrypt;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import cn.rlzc.common.core.constant.Constants;
+import cn.rlzc.common.core.constant.GlobalConstants;
+import cn.rlzc.common.core.domain.model.RegisterBody;
+import cn.rlzc.common.core.enums.UserType;
+import cn.rlzc.common.core.exception.user.CaptchaException;
+import cn.rlzc.common.core.exception.user.CaptchaExpireException;
+import cn.rlzc.common.core.exception.user.UserException;
+import cn.rlzc.common.core.utils.MessageUtils;
+import cn.rlzc.common.core.utils.ServletUtils;
+import cn.rlzc.common.core.utils.SpringUtils;
+import cn.rlzc.common.core.utils.StringUtils;
+import cn.rlzc.common.log.event.LogininforEvent;
+import cn.rlzc.common.redis.utils.RedisUtils;
+import cn.rlzc.common.tenant.helper.TenantHelper;
+import cn.rlzc.common.web.config.properties.CaptchaProperties;
+import cn.rlzc.system.domain.SysUser;
+import cn.rlzc.system.domain.bo.SysUserBo;
+import cn.rlzc.system.mapper.SysUserMapper;
+import cn.rlzc.system.service.ISysUserService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 注册校验方法
+ *
+ * @author Lion Li
+ */
+@RequiredArgsConstructor
+@Service
+public class SysRegisterService {
+
+    private final ISysUserService userService;
+    private final SysUserMapper userMapper;
+    private final CaptchaProperties captchaProperties;
+
+    /**
+     * 注册
+     */
+    public void register(RegisterBody registerBody) {
+        String tenantId = registerBody.getTenantId();
+        String username = registerBody.getUsername();
+        String password = registerBody.getPassword();
+        // 校验用户类型是否存在
+        String userType = UserType.getUserType(registerBody.getUserType()).getUserType();
+
+        boolean captchaEnabled = captchaProperties.getEnable();
+        // 验证码开关
+        if (captchaEnabled) {
+            validateCaptcha(tenantId, username, registerBody.getCode(), registerBody.getUuid());
+        }
+        SysUserBo sysUser = new SysUserBo();
+        sysUser.setUserName(username);
+        sysUser.setNickName(username);
+        sysUser.setPassword(BCrypt.hashpw(password));
+        sysUser.setUserType(userType);
+
+        boolean exist = TenantHelper.dynamic(tenantId, () -> {
+            return userMapper.exists(new LambdaQueryWrapper<SysUser>()
+                .eq(SysUser::getUserName, sysUser.getUserName()));
+        });
+        if (exist) {
+            throw new UserException("user.register.save.error", username);
+        }
+        boolean regFlag = userService.registerUser(sysUser, tenantId);
+        if (!regFlag) {
+            throw new UserException("user.register.error");
+        }
+        recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success"));
+    }
+
+    /**
+     * 校验验证码
+     *
+     * @param username 用户名
+     * @param code     验证码
+     * @param uuid     唯一标识
+     */
+    public void validateCaptcha(String tenantId, String username, String code, String uuid) {
+        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
+        String captcha = RedisUtils.getCacheObject(verifyKey);
+        RedisUtils.deleteObject(verifyKey);
+        if (captcha == null) {
+            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
+            throw new CaptchaExpireException();
+        }
+        if (!code.equalsIgnoreCase(captcha)) {
+            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
+            throw new CaptchaException();
+        }
+    }
+
+    /**
+     * 记录登录信息
+     *
+     * @param tenantId 租户ID
+     * @param username 用户名
+     * @param status   状态
+     * @param message  消息内容
+     * @return
+     */
+    private void recordLogininfor(String tenantId, String username, String status, String message) {
+        LogininforEvent logininforEvent = new LogininforEvent();
+        logininforEvent.setTenantId(tenantId);
+        logininforEvent.setUsername(username);
+        logininforEvent.setStatus(status);
+        logininforEvent.setMessage(message);
+        logininforEvent.setRequest(ServletUtils.getRequest());
+        SpringUtils.context().publishEvent(logininforEvent);
+    }
+
+}

+ 102 - 0
rlzc-admin/src/main/java/cn/rlzc/web/service/impl/EmailAuthStrategy.java

@@ -0,0 +1,102 @@
+package cn.rlzc.web.service.impl;
+
+import cn.dev33.satoken.stp.SaLoginModel;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import cn.rlzc.common.core.constant.Constants;
+import cn.rlzc.common.core.constant.GlobalConstants;
+import cn.rlzc.common.core.constant.SystemConstants;
+import cn.rlzc.common.core.domain.model.EmailLoginBody;
+import cn.rlzc.common.core.domain.model.LoginUser;
+import cn.rlzc.common.core.enums.LoginType;
+import cn.rlzc.common.core.exception.user.CaptchaExpireException;
+import cn.rlzc.common.core.exception.user.UserException;
+import cn.rlzc.common.core.utils.MessageUtils;
+import cn.rlzc.common.core.utils.StringUtils;
+import cn.rlzc.common.core.utils.ValidatorUtils;
+import cn.rlzc.common.json.utils.JsonUtils;
+import cn.rlzc.common.redis.utils.RedisUtils;
+import cn.rlzc.common.satoken.utils.LoginHelper;
+import cn.rlzc.common.tenant.helper.TenantHelper;
+import cn.rlzc.system.domain.SysUser;
+import cn.rlzc.system.domain.vo.SysClientVo;
+import cn.rlzc.system.domain.vo.SysUserVo;
+import cn.rlzc.system.mapper.SysUserMapper;
+import cn.rlzc.web.domain.vo.LoginVo;
+import cn.rlzc.web.service.IAuthStrategy;
+import cn.rlzc.web.service.SysLoginService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 邮件认证策略
+ *
+ * @author Michelle.Chung
+ */
+@Slf4j
+@Service("email" + IAuthStrategy.BASE_NAME)
+@RequiredArgsConstructor
+public class EmailAuthStrategy implements IAuthStrategy {
+
+    private final SysLoginService loginService;
+    private final SysUserMapper userMapper;
+
+    @Override
+    public LoginVo login(String body, SysClientVo client) {
+        EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        String tenantId = loginBody.getTenantId();
+        String email = loginBody.getEmail();
+        String emailCode = loginBody.getEmailCode();
+        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
+            SysUserVo user = loadUserByEmail(email);
+            loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode));
+            // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
+            return loginService.buildLoginUser(user);
+        });
+        loginUser.setClientKey(client.getClientKey());
+        loginUser.setDeviceType(client.getDeviceType());
+        SaLoginModel model = new SaLoginModel();
+        model.setDevice(client.getDeviceType());
+        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
+        // 例如: 后台用户30分钟过期 app用户1天过期
+        model.setTimeout(client.getTimeout());
+        model.setActiveTimeout(client.getActiveTimeout());
+        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
+        // 生成token
+        LoginHelper.login(loginUser, model);
+
+        LoginVo loginVo = new LoginVo();
+        loginVo.setAccessToken(StpUtil.getTokenValue());
+        loginVo.setExpireIn(StpUtil.getTokenTimeout());
+        loginVo.setClientId(client.getClientId());
+        return loginVo;
+    }
+
+    /**
+     * 校验邮箱验证码
+     */
+    private boolean validateEmailCode(String tenantId, String email, String emailCode) {
+        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email);
+        if (StringUtils.isBlank(code)) {
+            loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
+            throw new CaptchaExpireException();
+        }
+        return code.equals(emailCode);
+    }
+
+    private SysUserVo loadUserByEmail(String email) {
+        SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email));
+        if (ObjectUtil.isNull(user)) {
+            log.info("登录用户:{} 不存在.", email);
+            throw new UserException("user.not.exists", email);
+        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
+            log.info("登录用户:{} 已被停用.", email);
+            throw new UserException("user.blocked", email);
+        }
+        return user;
+    }
+
+}

+ 123 - 0
rlzc-admin/src/main/java/cn/rlzc/web/service/impl/PasswordAuthStrategy.java

@@ -0,0 +1,123 @@
+package cn.rlzc.web.service.impl;
+
+import cn.dev33.satoken.secure.BCrypt;
+import cn.dev33.satoken.stp.SaLoginModel;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import cn.rlzc.common.core.constant.Constants;
+import cn.rlzc.common.core.constant.GlobalConstants;
+import cn.rlzc.common.core.constant.SystemConstants;
+import cn.rlzc.common.core.domain.model.LoginUser;
+import cn.rlzc.common.core.domain.model.PasswordLoginBody;
+import cn.rlzc.common.core.enums.LoginType;
+import cn.rlzc.common.core.exception.user.CaptchaException;
+import cn.rlzc.common.core.exception.user.CaptchaExpireException;
+import cn.rlzc.common.core.exception.user.UserException;
+import cn.rlzc.common.core.utils.MessageUtils;
+import cn.rlzc.common.core.utils.StringUtils;
+import cn.rlzc.common.core.utils.ValidatorUtils;
+import cn.rlzc.common.json.utils.JsonUtils;
+import cn.rlzc.common.redis.utils.RedisUtils;
+import cn.rlzc.common.satoken.utils.LoginHelper;
+import cn.rlzc.common.tenant.helper.TenantHelper;
+import cn.rlzc.common.web.config.properties.CaptchaProperties;
+import cn.rlzc.system.domain.SysUser;
+import cn.rlzc.system.domain.vo.SysClientVo;
+import cn.rlzc.system.domain.vo.SysUserVo;
+import cn.rlzc.system.mapper.SysUserMapper;
+import cn.rlzc.web.domain.vo.LoginVo;
+import cn.rlzc.web.service.IAuthStrategy;
+import cn.rlzc.web.service.SysLoginService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 密码认证策略
+ *
+ * @author Michelle.Chung
+ */
+@Slf4j
+@Service("password" + IAuthStrategy.BASE_NAME)
+@RequiredArgsConstructor
+public class PasswordAuthStrategy implements IAuthStrategy {
+
+    private final CaptchaProperties captchaProperties;
+    private final SysLoginService loginService;
+    private final SysUserMapper userMapper;
+
+    @Override
+    public LoginVo login(String body, SysClientVo client) {
+        PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        String tenantId = loginBody.getTenantId();
+        String username = loginBody.getUsername();
+        String password = loginBody.getPassword();
+        String code = loginBody.getCode();
+        String uuid = loginBody.getUuid();
+
+        boolean captchaEnabled = captchaProperties.getEnable();
+        // 验证码开关
+        if (captchaEnabled) {
+            validateCaptcha(tenantId, username, code, uuid);
+        }
+        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
+            SysUserVo user = loadUserByUsername(username);
+            loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
+            // 此处可根据登录用户的数据不同 自行创建 loginUser
+            return loginService.buildLoginUser(user);
+        });
+        loginUser.setClientKey(client.getClientKey());
+        loginUser.setDeviceType(client.getDeviceType());
+        SaLoginModel model = new SaLoginModel();
+        model.setDevice(client.getDeviceType());
+        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
+        // 例如: 后台用户30分钟过期 app用户1天过期
+        model.setTimeout(client.getTimeout());
+        model.setActiveTimeout(client.getActiveTimeout());
+        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
+        // 生成token
+        LoginHelper.login(loginUser, model);
+
+        LoginVo loginVo = new LoginVo();
+        loginVo.setAccessToken(StpUtil.getTokenValue());
+        loginVo.setExpireIn(StpUtil.getTokenTimeout());
+        loginVo.setClientId(client.getClientId());
+        return loginVo;
+    }
+
+    /**
+     * 校验验证码
+     *
+     * @param username 用户名
+     * @param code     验证码
+     * @param uuid     唯一标识
+     */
+    private void validateCaptcha(String tenantId, String username, String code, String uuid) {
+        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
+        String captcha = RedisUtils.getCacheObject(verifyKey);
+        RedisUtils.deleteObject(verifyKey);
+        if (captcha == null) {
+            loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
+            throw new CaptchaExpireException();
+        }
+        if (!code.equalsIgnoreCase(captcha)) {
+            loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
+            throw new CaptchaException();
+        }
+    }
+
+    private SysUserVo loadUserByUsername(String username) {
+        SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username));
+        if (ObjectUtil.isNull(user)) {
+            log.info("登录用户:{} 不存在.", username);
+            throw new UserException("user.not.exists", username);
+        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
+            log.info("登录用户:{} 已被停用.", username);
+            throw new UserException("user.blocked", username);
+        }
+        return user;
+    }
+
+}

+ 102 - 0
rlzc-admin/src/main/java/cn/rlzc/web/service/impl/SmsAuthStrategy.java

@@ -0,0 +1,102 @@
+package cn.rlzc.web.service.impl;
+
+import cn.dev33.satoken.stp.SaLoginModel;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import cn.rlzc.common.core.constant.Constants;
+import cn.rlzc.common.core.constant.GlobalConstants;
+import cn.rlzc.common.core.constant.SystemConstants;
+import cn.rlzc.common.core.domain.model.LoginUser;
+import cn.rlzc.common.core.domain.model.SmsLoginBody;
+import cn.rlzc.common.core.enums.LoginType;
+import cn.rlzc.common.core.exception.user.CaptchaExpireException;
+import cn.rlzc.common.core.exception.user.UserException;
+import cn.rlzc.common.core.utils.MessageUtils;
+import cn.rlzc.common.core.utils.StringUtils;
+import cn.rlzc.common.core.utils.ValidatorUtils;
+import cn.rlzc.common.json.utils.JsonUtils;
+import cn.rlzc.common.redis.utils.RedisUtils;
+import cn.rlzc.common.satoken.utils.LoginHelper;
+import cn.rlzc.common.tenant.helper.TenantHelper;
+import cn.rlzc.system.domain.SysUser;
+import cn.rlzc.system.domain.vo.SysClientVo;
+import cn.rlzc.system.domain.vo.SysUserVo;
+import cn.rlzc.system.mapper.SysUserMapper;
+import cn.rlzc.web.domain.vo.LoginVo;
+import cn.rlzc.web.service.IAuthStrategy;
+import cn.rlzc.web.service.SysLoginService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 短信认证策略
+ *
+ * @author Michelle.Chung
+ */
+@Slf4j
+@Service("sms" + IAuthStrategy.BASE_NAME)
+@RequiredArgsConstructor
+public class SmsAuthStrategy implements IAuthStrategy {
+
+    private final SysLoginService loginService;
+    private final SysUserMapper userMapper;
+
+    @Override
+    public LoginVo login(String body, SysClientVo client) {
+        SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        String tenantId = loginBody.getTenantId();
+        String phonenumber = loginBody.getPhonenumber();
+        String smsCode = loginBody.getSmsCode();
+        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
+            SysUserVo user = loadUserByPhonenumber(phonenumber);
+            loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode));
+            // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
+            return loginService.buildLoginUser(user);
+        });
+        loginUser.setClientKey(client.getClientKey());
+        loginUser.setDeviceType(client.getDeviceType());
+        SaLoginModel model = new SaLoginModel();
+        model.setDevice(client.getDeviceType());
+        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
+        // 例如: 后台用户30分钟过期 app用户1天过期
+        model.setTimeout(client.getTimeout());
+        model.setActiveTimeout(client.getActiveTimeout());
+        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
+        // 生成token
+        LoginHelper.login(loginUser, model);
+
+        LoginVo loginVo = new LoginVo();
+        loginVo.setAccessToken(StpUtil.getTokenValue());
+        loginVo.setExpireIn(StpUtil.getTokenTimeout());
+        loginVo.setClientId(client.getClientId());
+        return loginVo;
+    }
+
+    /**
+     * 校验短信验证码
+     */
+    private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) {
+        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber);
+        if (StringUtils.isBlank(code)) {
+            loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
+            throw new CaptchaExpireException();
+        }
+        return code.equals(smsCode);
+    }
+
+    private SysUserVo loadUserByPhonenumber(String phonenumber) {
+        SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber));
+        if (ObjectUtil.isNull(user)) {
+            log.info("登录用户:{} 不存在.", phonenumber);
+            throw new UserException("user.not.exists", phonenumber);
+        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
+            log.info("登录用户:{} 已被停用.", phonenumber);
+            throw new UserException("user.blocked", phonenumber);
+        }
+        return user;
+    }
+
+}

+ 131 - 0
rlzc-admin/src/main/java/cn/rlzc/web/service/impl/SocialAuthStrategy.java

@@ -0,0 +1,131 @@
+package cn.rlzc.web.service.impl;
+
+import cn.dev33.satoken.stp.SaLoginModel;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.http.Method;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.model.AuthResponse;
+import me.zhyd.oauth.model.AuthUser;
+import cn.rlzc.common.core.constant.SystemConstants;
+import cn.rlzc.common.core.domain.model.LoginUser;
+import cn.rlzc.common.core.domain.model.SocialLoginBody;
+import cn.rlzc.common.core.exception.ServiceException;
+import cn.rlzc.common.core.exception.user.UserException;
+import cn.rlzc.common.core.utils.StreamUtils;
+import cn.rlzc.common.core.utils.ValidatorUtils;
+import cn.rlzc.common.json.utils.JsonUtils;
+import cn.rlzc.common.satoken.utils.LoginHelper;
+import cn.rlzc.common.social.config.properties.SocialProperties;
+import cn.rlzc.common.social.utils.SocialUtils;
+import cn.rlzc.common.tenant.helper.TenantHelper;
+import cn.rlzc.system.domain.vo.SysClientVo;
+import cn.rlzc.system.domain.vo.SysSocialVo;
+import cn.rlzc.system.domain.vo.SysUserVo;
+import cn.rlzc.system.mapper.SysUserMapper;
+import cn.rlzc.system.service.ISysSocialService;
+import cn.rlzc.web.domain.vo.LoginVo;
+import cn.rlzc.web.service.IAuthStrategy;
+import cn.rlzc.web.service.SysLoginService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 第三方授权策略
+ *
+ * @author thiszhc is 三三
+ */
+@Slf4j
+@Service("social" + IAuthStrategy.BASE_NAME)
+@RequiredArgsConstructor
+public class SocialAuthStrategy implements IAuthStrategy {
+
+    private final SocialProperties socialProperties;
+    private final ISysSocialService sysSocialService;
+    private final SysUserMapper userMapper;
+    private final SysLoginService loginService;
+
+    /**
+     * 登录-第三方授权登录
+     *
+     * @param body     登录信息
+     * @param client   客户端信息
+     */
+    @Override
+    public LoginVo login(String body, SysClientVo client) {
+        SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
+                loginBody.getSource(), loginBody.getSocialCode(),
+                loginBody.getSocialState(), socialProperties);
+        if (!response.ok()) {
+            throw new ServiceException(response.getMsg());
+        }
+        AuthUser authUserData = response.getData();
+        if ("GITEE".equals(authUserData.getSource())) {
+            // 如用户使用 gitee 登录顺手 star 给作者一点支持 拒绝白嫖
+            HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Vue-Plus")
+                    .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
+                    .executeAsync();
+            HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Cloud-Plus")
+                    .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
+                    .executeAsync();
+        }
+
+        List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
+        if (CollUtil.isEmpty(list)) {
+            throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
+        }
+        SysSocialVo social;
+        if (TenantHelper.isEnable()) {
+            Optional<SysSocialVo> opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId()));
+            if (opt.isEmpty()) {
+                throw new ServiceException("对不起,你没有权限登录当前租户!");
+            }
+            social = opt.get();
+        } else {
+            social = list.get(0);
+        }
+        LoginUser loginUser = TenantHelper.dynamic(social.getTenantId(), () -> {
+            SysUserVo user = loadUser(social.getUserId());
+            // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
+            return loginService.buildLoginUser(user);
+        });
+        loginUser.setClientKey(client.getClientKey());
+        loginUser.setDeviceType(client.getDeviceType());
+        SaLoginModel model = new SaLoginModel();
+        model.setDevice(client.getDeviceType());
+        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
+        // 例如: 后台用户30分钟过期 app用户1天过期
+        model.setTimeout(client.getTimeout());
+        model.setActiveTimeout(client.getActiveTimeout());
+        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
+        // 生成token
+        LoginHelper.login(loginUser, model);
+
+        LoginVo loginVo = new LoginVo();
+        loginVo.setAccessToken(StpUtil.getTokenValue());
+        loginVo.setExpireIn(StpUtil.getTokenTimeout());
+        loginVo.setClientId(client.getClientId());
+        return loginVo;
+    }
+
+    private SysUserVo loadUser(Long userId) {
+        SysUserVo user = userMapper.selectVoById(userId);
+        if (ObjectUtil.isNull(user)) {
+            log.info("登录用户:{} 不存在.", "");
+            throw new UserException("user.not.exists", "");
+        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
+            log.info("登录用户:{} 已被停用.", "");
+            throw new UserException("user.blocked", "");
+        }
+        return user;
+    }
+
+}

+ 111 - 0
rlzc-admin/src/main/java/cn/rlzc/web/service/impl/XcxAuthStrategy.java

@@ -0,0 +1,111 @@
+package cn.rlzc.web.service.impl;
+
+import cn.dev33.satoken.stp.SaLoginModel;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.util.ObjectUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.config.AuthConfig;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthResponse;
+import me.zhyd.oauth.model.AuthToken;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthRequest;
+import me.zhyd.oauth.request.AuthWechatMiniProgramRequest;
+import cn.rlzc.common.core.constant.SystemConstants;
+import cn.rlzc.common.core.domain.model.XcxLoginBody;
+import cn.rlzc.common.core.domain.model.XcxLoginUser;
+import cn.rlzc.common.core.exception.ServiceException;
+import cn.rlzc.common.core.utils.ValidatorUtils;
+import cn.rlzc.common.json.utils.JsonUtils;
+import cn.rlzc.common.satoken.utils.LoginHelper;
+import cn.rlzc.system.domain.vo.SysClientVo;
+import cn.rlzc.system.domain.vo.SysUserVo;
+import cn.rlzc.web.domain.vo.LoginVo;
+import cn.rlzc.web.service.IAuthStrategy;
+import cn.rlzc.web.service.SysLoginService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 小程序认证策略
+ *
+ * @author Michelle.Chung
+ */
+@Slf4j
+@Service("xcx" + IAuthStrategy.BASE_NAME)
+@RequiredArgsConstructor
+public class XcxAuthStrategy implements IAuthStrategy {
+
+    private final SysLoginService loginService;
+
+    @Override
+    public LoginVo login(String body, SysClientVo client) {
+        XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        // xcxCode 为 小程序调用 wx.login 授权后获取
+        String xcxCode = loginBody.getXcxCode();
+        // 多个小程序识别使用
+        String appid = loginBody.getAppid();
+
+        // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
+        AuthRequest authRequest = new AuthWechatMiniProgramRequest(AuthConfig.builder()
+            .clientId(appid).clientSecret("自行填写密钥 可根据不同appid填入不同密钥")
+            .ignoreCheckRedirectUri(true).ignoreCheckState(true).build());
+        AuthCallback authCallback = new AuthCallback();
+        authCallback.setCode(xcxCode);
+        AuthResponse<AuthUser> resp = authRequest.login(authCallback);
+        String openid, unionId;
+        if (resp.ok()) {
+            AuthToken token = resp.getData().getToken();
+            openid = token.getOpenId();
+            // 微信小程序只有关联到微信开放平台下之后才能获取到 unionId,因此unionId不一定能返回。
+            unionId = token.getUnionId();
+        } else {
+            throw new ServiceException(resp.getMsg());
+        }
+        // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
+        SysUserVo user = loadUserByOpenid(openid);
+        // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
+        XcxLoginUser loginUser = new XcxLoginUser();
+        loginUser.setTenantId(user.getTenantId());
+        loginUser.setUserId(user.getUserId());
+        loginUser.setUsername(user.getUserName());
+        loginUser.setNickname(user.getNickName());
+        loginUser.setUserType(user.getUserType());
+        loginUser.setClientKey(client.getClientKey());
+        loginUser.setDeviceType(client.getDeviceType());
+        loginUser.setOpenid(openid);
+
+        SaLoginModel model = new SaLoginModel();
+        model.setDevice(client.getDeviceType());
+        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
+        // 例如: 后台用户30分钟过期 app用户1天过期
+        model.setTimeout(client.getTimeout());
+        model.setActiveTimeout(client.getActiveTimeout());
+        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
+        // 生成token
+        LoginHelper.login(loginUser, model);
+
+        LoginVo loginVo = new LoginVo();
+        loginVo.setAccessToken(StpUtil.getTokenValue());
+        loginVo.setExpireIn(StpUtil.getTokenTimeout());
+        loginVo.setClientId(client.getClientId());
+        loginVo.setOpenid(openid);
+        return loginVo;
+    }
+
+    private SysUserVo loadUserByOpenid(String openid) {
+        // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户
+        // todo 自行实现 userService.selectUserByOpenid(openid);
+        SysUserVo user = new SysUserVo();
+        if (ObjectUtil.isNull(user)) {
+            log.info("登录用户:{} 不存在.", openid);
+            // todo 用户不存在 业务逻辑自行实现
+        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
+            log.info("登录用户:{} 已被停用.", openid);
+            // todo 用户已被停用 业务逻辑自行实现
+        }
+        return user;
+    }
+
+}

+ 278 - 0
rlzc-admin/src/main/resources/application-dev.yml

@@ -0,0 +1,278 @@
+--- # 监控中心配置
+spring.boot.admin.client:
+  # 增加客户端开关
+  enabled: false
+  url: http://localhost:9090/admin
+  instance:
+    service-host-type: IP
+    metadata:
+      username: ${spring.boot.admin.client.username}
+      userpassword: ${spring.boot.admin.client.password}
+  username: @monitor.username@
+  password: @monitor.password@
+
+--- # snail-job 配置
+snail-job:
+  enabled: true
+  # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
+  group: "ruoyi_group"
+  # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表
+  token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
+  server:
+    host: 127.0.0.1
+    port: 17888
+  # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
+  namespace: ${spring.profiles.active}
+  # 随主应用端口漂移
+  port: 2${server.port}
+  # 客户端ip指定
+  host:
+  # RPC类型: netty, grpc
+  rpc-type: grpc
+
+
+#文件上传相关配置
+upload: #上传文件到本地的相关配置
+  local:
+    domain: http://localhost:8080
+    rootDir: /upload
+  qiniu: #上传文件到七牛,七牛的相关配置
+    accessKey: dnGxxxxxxxJ5t
+    secretKey: y1xxxxxxxxxAvm
+    region: https://up-z0.qiniup.com
+    domain: https://assets.d2danao.com
+    bucket: dxxxxxxo
+
+
+--- # 数据源配置
+spring:
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+    # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
+    dynamic:
+      # 性能分析插件(有性能损耗 不建议生产环境使用)
+      p6spy: true
+      # 设置默认的数据源或者数据源组,默认值即为 master
+      primary: master
+      # 严格模式 匹配不到数据源则报错
+      strict: true
+      datasource:
+        # 主库数据源
+        master:
+          type: ${spring.datasource.type}
+          driverClassName: com.mysql.cj.jdbc.Driver
+          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
+          # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
+          url: jdbc:mysql://172.16.196.113:3306/jbolt_ruoyi_ai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+          username: root
+          password: 123456a?
+#        # 从库数据源
+#        slave:
+#          lazy: true
+#          type: ${spring.datasource.type}
+#          driverClassName: com.mysql.cj.jdbc.Driver
+#          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+#          username:
+#          password:
+#        oracle:
+#          type: ${spring.datasource.type}
+#          driverClassName: oracle.jdbc.OracleDriver
+#          url: jdbc:oracle:thin:@//localhost:1521/XE
+#          username: ROOT
+#          password: root
+#        postgres:
+#          type: ${spring.datasource.type}
+#          driverClassName: org.postgresql.Driver
+#          url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
+#          username: root
+#          password: root
+#        sqlserver:
+#          type: ${spring.datasource.type}
+#          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
+#          url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
+#          username: SA
+#          password: root
+      hikari:
+        # 最大连接池数量
+        maxPoolSize: 20
+        # 最小空闲线程数量
+        minIdle: 10
+        # 配置获取连接等待超时的时间
+        connectionTimeout: 30000
+        # 校验超时时间
+        validationTimeout: 5000
+        # 空闲连接存活最大时间,默认10分钟
+        idleTimeout: 600000
+        # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
+        maxLifetime: 1800000
+        # 多久检查一次连接的活性
+        keepaliveTime: 30000
+
+--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
+spring.data:
+  redis:
+    # 地址
+    host: 172.16.196.113
+    # 端口,默认为6379
+    port: 6379
+    # 数据库索引
+    database: 0
+    # redis 密码必须配置
+    password: infini_rag_flow
+    # 连接超时时间
+    timeout: 10s
+    # 是否开启ssl
+    ssl.enabled: false
+
+# redisson 配置
+redisson:
+  # redis key前缀
+  keyPrefix:
+  # 线程池数量
+  threads: 4
+  # Netty线程池数量
+  nettyThreads: 8
+  # 单节点配置
+  singleServerConfig:
+    # 客户端名称 不能用中文
+    clientName: RuoYi-Vue-Plus
+    # 最小空闲连接数
+    connectionMinimumIdleSize: 8
+    # 连接池大小
+    connectionPoolSize: 32
+    # 连接空闲超时,单位:毫秒
+    idleConnectionTimeout: 10000
+    # 命令等待超时,单位:毫秒
+    timeout: 3000
+    # 发布和订阅连接池大小
+    subscriptionConnectionPoolSize: 50
+
+--- # mail 邮件发送
+mail:
+  enabled: false
+  host: smtp.163.com
+  port: 465
+  # 是否需要用户名密码验证
+  auth: true
+  # 发送方,遵循RFC-822标准
+  from: xxx@163.com
+  # 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
+  user: xxx@163.com
+  # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助)
+  pass: xxxxxxxxxx
+  # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
+  starttlsEnable: true
+  # 使用SSL安全连接
+  sslEnable: true
+  # SMTP超时时长,单位毫秒,缺省值不超时
+  timeout: 0
+  # Socket连接超时值,单位毫秒,缺省值不超时
+  connectionTimeout: 0
+
+--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
+# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
+sms:
+  # 配置源类型用于标定配置来源(interface,yaml)
+  config-type: yaml
+  # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
+  restricted: true
+  # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
+  minute-max: 1
+  # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
+  account-max: 30
+  # 以下配置来自于 cn.rlzc.sms4j.provider.config.BaseConfig类中
+  blends:
+    # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
+    # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
+    config1:
+      # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
+      supplier: alibaba
+      # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。
+      access-key-id: 您的accessKey
+      # 称为accessSecret有些称之为apiSecret
+      access-key-secret: 您的accessKeySecret
+      signature: 您的短信签名
+      sdk-app-id: 您的sdkAppId
+    config2:
+      # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
+      supplier: tencent
+      access-key-id: 您的accessKey
+      access-key-secret: 您的accessKeySecret
+      signature: 您的短信签名
+      sdk-app-id: 您的sdkAppId
+
+--- # 三方授权
+justauth:
+  # 前端外网访问地址
+  address: http://localhost:80
+  type:
+    maxkey:
+      # maxkey 服务器地址
+      # 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
+      server-url: http://sso.maxkey.top
+      client-id: 876892492581044224
+      client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
+      redirect-uri: ${justauth.address}/social-callback?source=maxkey
+    topiam:
+      # topiam 服务器地址
+      server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
+      client-id: 449c4*********937************759
+      client-secret: ac7***********1e0************28d
+      redirect-uri: ${justauth.address}/social-callback?source=topiam
+      scopes: [openid, email, phone, profile]
+    qq:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=qq
+      union-id: false
+    weibo:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=weibo
+    gitee:
+      client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
+      client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
+      redirect-uri: ${justauth.address}/social-callback?source=gitee
+    dingtalk:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=dingtalk
+    baidu:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=baidu
+    csdn:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=csdn
+    coding:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=coding
+      coding-group-name: xx
+    oschina:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=oschina
+    alipay_wallet:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
+      alipay-public-key: MIIB**************DAQAB
+    wechat_open:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=wechat_open
+    wechat_mp:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
+    wechat_enterprise:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
+      agent-id: 1000002
+    gitlab:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=gitlab

+ 267 - 0
rlzc-admin/src/main/resources/application-prod.yml

@@ -0,0 +1,267 @@
+--- # 临时文件存储位置 避免临时文件被系统清理报错
+spring.servlet.multipart.location: /rlzc/server/temp
+
+--- # 监控中心配置
+spring.boot.admin.client:
+  # 增加客户端开关
+  enabled: true
+  url: http://localhost:9090/admin
+  instance:
+    service-host-type: IP
+    metadata:
+      username: ${spring.boot.admin.client.username}
+      userpassword: ${spring.boot.admin.client.password}
+  username: @monitor.username@
+  password: @monitor.password@
+
+--- # snail-job 配置
+snail-job:
+  enabled: true
+  # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
+  group: "rlzc_group"
+  # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表
+  token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
+  server:
+    host: 127.0.0.1
+    port: 17888
+  # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
+  namespace: ${spring.profiles.active}
+  # 随主应用端口漂移
+  port: 2${server.port}
+  # 客户端ip指定
+  host:
+  # RPC类型: netty, grpc
+  rpc-type: grpc
+
+--- # 数据源配置
+spring:
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+    # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
+    dynamic:
+      # 性能分析插件(有性能损耗 不建议生产环境使用)
+      p6spy: false
+      # 设置默认的数据源或者数据源组,默认值即为 master
+      primary: master
+      # 严格模式 匹配不到数据源则报错
+      strict: true
+      datasource:
+        # 主库数据源
+        master:
+          type: ${spring.datasource.type}
+          driverClassName: com.mysql.cj.jdbc.Driver
+          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
+          # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
+          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+          username: root
+          password: root
+#        # 从库数据源
+#        slave:
+#          lazy: true
+#          type: ${spring.datasource.type}
+#          driverClassName: com.mysql.cj.jdbc.Driver
+#          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+#          username:
+#          password:
+#        oracle:
+#          type: ${spring.datasource.type}
+#          driverClassName: oracle.jdbc.OracleDriver
+#          url: jdbc:oracle:thin:@//localhost:1521/XE
+#          username: ROOT
+#          password: root
+#        postgres:
+#          type: ${spring.datasource.type}
+#          driverClassName: org.postgresql.Driver
+#          url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
+#          username: root
+#          password: root
+#        sqlserver:
+#          type: ${spring.datasource.type}
+#          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
+#          url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
+#          username: SA
+#          password: root
+      hikari:
+        # 最大连接池数量
+        maxPoolSize: 20
+        # 最小空闲线程数量
+        minIdle: 10
+        # 配置获取连接等待超时的时间
+        connectionTimeout: 30000
+        # 校验超时时间
+        validationTimeout: 5000
+        # 空闲连接存活最大时间,默认10分钟
+        idleTimeout: 600000
+        # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
+        maxLifetime: 1800000
+        # 多久检查一次连接的活性
+        keepaliveTime: 30000
+
+--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
+spring.data:
+  redis:
+    # 地址
+    host: localhost
+    # 端口,默认为6379
+    port: 6379
+    # 数据库索引
+    database: 0
+    # redis 密码必须配置
+    password: rlzc123
+    # 连接超时时间
+    timeout: 10s
+    # 是否开启ssl
+    ssl.enabled: false
+
+# redisson 配置
+redisson:
+  # redis key前缀
+  keyPrefix:
+  # 线程池数量
+  threads: 16
+  # Netty线程池数量
+  nettyThreads: 32
+  # 单节点配置
+  singleServerConfig:
+    # 客户端名称 不能用中文
+    clientName: RuoYi-Vue-Plus
+    # 最小空闲连接数
+    connectionMinimumIdleSize: 32
+    # 连接池大小
+    connectionPoolSize: 64
+    # 连接空闲超时,单位:毫秒
+    idleConnectionTimeout: 10000
+    # 命令等待超时,单位:毫秒
+    timeout: 3000
+    # 发布和订阅连接池大小
+    subscriptionConnectionPoolSize: 50
+
+--- # mail 邮件发送
+mail:
+  enabled: false
+  host: smtp.163.com
+  port: 465
+  # 是否需要用户名密码验证
+  auth: true
+  # 发送方,遵循RFC-822标准
+  from: xxx@163.com
+  # 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
+  user: xxx@163.com
+  # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助)
+  pass: xxxxxxxxxx
+  # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
+  starttlsEnable: true
+  # 使用SSL安全连接
+  sslEnable: true
+  # SMTP超时时长,单位毫秒,缺省值不超时
+  timeout: 0
+  # Socket连接超时值,单位毫秒,缺省值不超时
+  connectionTimeout: 0
+
+--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
+# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
+sms:
+  # 配置源类型用于标定配置来源(interface,yaml)
+  config-type: yaml
+  # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
+  restricted: true
+  # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
+  minute-max: 1
+  # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
+  account-max: 30
+  # 以下配置来自于 cn.rlzc.sms4j.provider.config.BaseConfig类中
+  blends:
+    # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
+    # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
+    config1:
+      # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
+      supplier: alibaba
+      # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。
+      access-key-id: 您的accessKey
+      # 称为accessSecret有些称之为apiSecret
+      access-key-secret: 您的accessKeySecret
+      signature: 您的短信签名
+      sdk-app-id: 您的sdkAppId
+    config2:
+      # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
+      supplier: tencent
+      access-key-id: 您的accessKey
+      access-key-secret: 您的accessKeySecret
+      signature: 您的短信签名
+      sdk-app-id: 您的sdkAppId
+
+--- # 三方授权
+justauth:
+  # 前端外网访问地址
+  address: http://localhost:80
+  type:
+    maxkey:
+      # maxkey 服务器地址
+      # 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
+      server-url: http://sso.maxkey.top
+      client-id: 876892492581044224
+      client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
+      redirect-uri: ${justauth.address}/social-callback?source=maxkey
+    topiam:
+      # topiam 服务器地址
+      server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
+      client-id: 449c4*********937************759
+      client-secret: ac7***********1e0************28d
+      redirect-uri: ${justauth.address}/social-callback?source=topiam
+      scopes: [ openid, email, phone, profile ]
+    qq:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=qq
+      union-id: false
+    weibo:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=weibo
+    gitee:
+      client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
+      client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
+      redirect-uri: ${justauth.address}/social-callback?source=gitee
+    dingtalk:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=dingtalk
+    baidu:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=baidu
+    csdn:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=csdn
+    coding:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=coding
+      coding-group-name: xx
+    oschina:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=oschina
+    alipay_wallet:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
+      alipay-public-key: MIIB**************DAQAB
+    wechat_open:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=wechat_open
+    wechat_mp:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
+    wechat_enterprise:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
+      agent-id: 1000002
+    gitlab:
+      client-id: 10**********6
+      client-secret: 1f7d08**********5b7**********29e
+      redirect-uri: ${justauth.address}/social-callback?source=gitlab

+ 296 - 0
rlzc-admin/src/main/resources/application.yml

@@ -0,0 +1,296 @@
+# 开发环境配置
+server:
+  # 服务器的HTTP端口,默认为8080
+  port: 8080
+  servlet:
+    # 应用的访问路径
+    context-path: /dev-api
+  # undertow 配置
+  undertow:
+    # HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
+    max-http-post-size: -1
+    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
+    # 每块buffer的空间大小,越小的空间被利用越充分
+    buffer-size: 512
+    # 是否分配的直接内存
+    direct-buffers: true
+    threads:
+      # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
+      io: 8
+      # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
+      worker: 256
+
+
+captcha:
+  enable: true
+  # 页面 <参数设置> 可开启关闭 验证码校验
+  # 验证码类型 math 数组计算 char 字符验证
+  type: MATH
+  # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
+  category: CIRCLE
+  # 数字验证码位数
+  numberLength: 1
+  # 字符验证码长度
+  charLength: 4
+
+# 日志配置
+logging:
+  level:
+    cn.rlzc: @logging.level@
+    org.springframework: warn
+    org.mybatis.spring.mapper: error
+    org.apache.fury: warn
+  config: classpath:logback-plus.xml
+
+# 用户配置
+user:
+  password:
+    # 密码最大错误次数
+    maxRetryCount: 5
+    # 密码锁定时间(默认10分钟)
+    lockTime: 10
+
+# Spring配置
+spring:
+  application:
+    name: RuoYi-Vue-Plus
+  threads:
+    # 开启虚拟线程 仅jdk21可用
+    virtual:
+      enabled: false
+  # 资源信息
+  messages:
+    # 国际化资源文件路径
+    basename: i18n/messages
+  profiles:
+    active: @profiles.active@
+  # 文件上传
+  servlet:
+    multipart:
+      # 单个文件大小
+      max-file-size: 10MB
+      # 设置总上传的文件大小
+      max-request-size: 20MB
+  mvc:
+    # 设置静态资源路径 防止所有请求都去查静态资源
+    static-path-pattern: /static/**
+    format:
+      date-time: yyyy-MM-dd HH:mm:ss
+  jackson:
+    # 日期格式化
+    date-format: yyyy-MM-dd HH:mm:ss
+    serialization:
+      # 格式化输出
+      indent_output: false
+      # 忽略无法转换的对象
+      fail_on_empty_beans: false
+    deserialization:
+      # 允许对象忽略json中不存在的属性
+      fail_on_unknown_properties: false
+  main:
+    allow-circular-references: true  #可以循环引用
+
+# Sa-Token配置
+sa-token:
+  # token名称 (同时也是cookie名称)
+  token-name: Authorization
+  # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
+  is-concurrent: true
+  # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
+  is-share: false
+  # jwt秘钥
+  jwt-secret-key: abcdefghijklmnopqrstuvwxyz
+
+# security配置
+security:
+  # 排除路径
+  excludes:
+    - /*.html
+    - /**/*.html
+    - /**/*.css
+    - /**/*.js
+    - /favicon.ico
+    - /error
+    - /*/api-docs
+    - /*/api-docs/**
+    - /warm-flow-ui/token-name
+    - /Session/**
+
+# 多租户配置
+tenant:
+  # 是否开启
+  enable: true
+  # 排除表
+  excludes:
+    - sys_menu
+    - sys_tenant
+    - sys_tenant_package
+    - sys_role_dept
+    - sys_role_menu
+    - sys_user_post
+    - sys_user_role
+    - sys_client
+    - sys_oss_config
+    - ja_ai_app
+    - ja_ai_app_session
+    - ja_ai_app_message
+    - ja_ai_app_config
+
+# MyBatisPlus配置
+# https://baomidou.com/config/
+mybatis-plus:
+  # 自定义配置 是否全局开启逻辑删除 关闭后 所有逻辑删除功能将失效
+  enableLogicDelete: true
+  # 多包名使用 例如 cn.rlzc.**.mapper,org.xxx.**.mapper
+  mapperPackage: cn.rlzc.**.mapper
+  # 对应的 XML 文件位置
+  mapperLocations: classpath*:mapper/**/*Mapper.xml
+  # 实体扫描,多个package用逗号或者分号分隔
+  typeAliasesPackage: cn.rlzc.**.domain
+  global-config:
+    dbConfig:
+      # 主键类型
+      # AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID
+      # 如需改为自增 需要将数据库表全部设置为自增
+      idType: ASSIGN_ID
+
+# 数据加密
+mybatis-encryptor:
+  # 是否开启加密
+  enable: false
+  # 默认加密算法
+  algorithm: BASE64
+  # 编码方式 BASE64/HEX。默认BASE64
+  encode: BASE64
+  # 安全秘钥 对称算法的秘钥 如:AES,SM4
+  password:
+  # 公私钥 非对称算法的公私钥 如:SM2,RSA
+  publicKey:
+  privateKey:
+
+# api接口加密
+api-decrypt:
+  # 是否开启全局接口加密
+  enabled: true
+  # AES 加密头标识
+  headerFlag: encrypt-key
+  # 响应加密公钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
+  # 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=
+  publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ==
+  # 请求解密私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
+  # 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
+  privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
+
+springdoc:
+  api-docs:
+    # 是否开启接口文档
+    enabled: true
+#  swagger-ui:
+#    # 持久化认证数据
+#    persistAuthorization: true
+  info:
+    # 标题
+    title: '标题:RuoYi-Vue-Plus多租户管理系统_接口文档'
+    # 描述
+    description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
+    # 版本
+    version: '版本号: ${rlzc.version}'
+    # 作者信息
+    contact:
+      name: Lion Li
+      email: crazylionli@163.com
+      url: https://gitee.com/dromara/RuoYi-Vue-Plus
+  components:
+    # 鉴权方式配置
+    security-schemes:
+      apiKey:
+        type: APIKEY
+        in: HEADER
+        name: ${sa-token.token-name}
+  #这里定义了两个分组,可定义多个,也可以不定义
+  group-configs:
+    - group: 1.演示模块
+      packages-to-scan: cn.rlzc.demo
+    - group: 2.通用模块
+      packages-to-scan: cn.rlzc.web
+    - group: 3.系统模块
+      packages-to-scan: cn.rlzc.system
+    - group: 4.代码生成模块
+      packages-to-scan: cn.rlzc.generator
+    - group: 5.工作流模块
+      packages-to-scan: cn.rlzc.workflow
+    - group: 6.AI模块
+      packages-to-scan: cn.rlzc.ai
+
+# 防止XSS攻击
+xss:
+  # 过滤开关
+  enabled: true
+  # 排除链接(多个用逗号分隔)
+  excludeUrls:
+    - /system/notice
+
+# 全局线程池相关配置
+# 如使用JDK21请直接使用虚拟线程 不要开启此配置
+thread-pool:
+  # 是否开启线程池
+  enabled: false
+  # 队列最大长度
+  queueCapacity: 128
+  # 线程池维护线程所允许的空闲时间
+  keepAliveSeconds: 300
+
+--- # 分布式锁 lock4j 全局配置
+lock4j:
+  # 获取分布式锁超时时间,默认为 3000 毫秒
+  acquire-timeout: 3000
+  # 分布式锁的超时时间,默认为 30 秒
+  expire: 30000
+
+--- # Actuator 监控端点的配置项
+management:
+  endpoints:
+    web:
+      exposure:
+        include: '*'
+  endpoint:
+    health:
+      show-details: ALWAYS
+    logfile:
+      external-file: ./logs/sys-console.log
+
+--- # 默认/推荐使用sse推送
+sse:
+  enabled: true
+  path: /resource/sse
+
+--- # websocket
+websocket:
+  # 如果关闭 需要和前端开关一起关闭
+  enabled: true
+  # 路径
+  path: /resource/websocket
+  # 设置访问源地址
+  allowedOrigins: '*'
+
+--- # warm-flow工作流配置
+warm-flow:
+  # 是否开启工作流,默认true
+  enabled: true
+  # 是否开启设计器ui
+  ui: true
+  # 默认Authorization,如果有多个token,用逗号分隔
+  token-name: ${sa-token.token-name},clientid
+  # 流程状态对应的三元色
+  chart-status-color:
+    ## 未办理
+    - 62,62,62
+    ## 待办理
+    - 255,205,23
+    ## 已办理
+    - 157,255,0
+# ---健康检查,禁用后不报错,开启后报错,没有对接的elasticsearch
+management:
+  health:
+    elasticsearch:
+      enabled: false # true:开启,false:关闭,默认为 true

+ 0 - 0
ruoyi-admin/src/main/resources/banner.txt → rlzc-admin/src/main/resources/banner.txt


+ 0 - 0
ruoyi-admin/src/main/resources/i18n/messages.properties → rlzc-admin/src/main/resources/i18n/messages.properties


+ 0 - 0
ruoyi-admin/src/main/resources/i18n/messages_en_US.properties → rlzc-admin/src/main/resources/i18n/messages_en_US.properties


+ 0 - 0
ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties → rlzc-admin/src/main/resources/i18n/messages_zh_CN.properties


+ 0 - 0
ruoyi-admin/src/main/resources/ip2region.xdb → rlzc-admin/src/main/resources/ip2region.xdb


+ 0 - 0
ruoyi-admin/src/main/resources/logback-plus.xml → rlzc-admin/src/main/resources/logback-plus.xml


BIN
rlzc-admin/src/main/resources/upload/doc/250410101502/wKrZg6.doc


BIN
rlzc-admin/src/main/resources/upload/logo/250410/R2caFk/图片1.png


BIN
rlzc-admin/src/main/resources/upload/logo/250410/nb3iyT/2.jpg


BIN
rlzc-admin/src/main/resources/upload/logo/250410/viszeK/微信截图_20250409164730.png


BIN
rlzc-admin/src/main/resources/upload/logo/250413/LquPCb/log.png


BIN
rlzc-admin/src/main/resources/upload/sysnotice/250410/ISbbbn/安装和简单使用Milvus.doc


+ 1500 - 0
rlzc-admin/src/main/resources/upload/sysnotice/250410/j6azR3/题库.txt

@@ -0,0 +1,1500 @@
+1、关于行政处罚的地域管辖,下列哪一说法是错误的()。
+单选题
+A、地域管辖主要解决行政处罚权在行政机关内部的横向分配问题
+B、《行政处罚法》自1996年施行以来一直坚持的是“违法行为地”原则
+C、法律、行政法规、部门规章可以对地域管辖作出例外规定
+D、海关行政处罚由违法行为发生地海关管辖
+D
+【答案】D
+简单
+
+2、关于互联网领域的违法行为发生地,下列哪一说法是错误的()。
+单选题
+A、用于实施违法行为的网站服务器所在地
+B、被侵害人户籍所在地
+C、网站建立者或者管理者所在地
+D、被侵害的网络及其运营者所在地
+B
+【答案】B
+简单
+
+3、关于行政处罚的级别管辖,下列哪一说法是错误的()。
+单选题
+A、级别管辖主要解决行政处罚决定权在行政机关内部的纵向分配问题
+B、行政处罚由县级以上地方人民政府具有行政处罚权的行政机关管辖
+C、法律、行政法规、规章另有规定的,从其规定
+D、行政处罚必须由有行政处罚权的机关作出
+C
+【答案】C
+简单
+
+4、关于行政处罚权的下放,下列哪一观点是错误的()。
+单选题
+A、只有省、自治区、直辖市政府可以决定将行政处罚权下移,其他任何机关不具有这个权力
+B、省级政府并不是可以随意的将行政处罚权下移,而是必须考虑当地的实际情况,包括基层管理迫切需要,能够有效承接
+C、任何行政处罚权都可以下移
+D、对接下移行政处罚权的可以是符合条件的乡镇人民政府、街道办事处,并且要求要求加强执法能力建设,按照规定范围、依照法定程序实施行政处罚
+C
+【答案】C
+简单
+
+5、关于行政处罚管辖争议的解决,下列哪一说法是错误的()。
+单选题
+A、两个以上行政机关都有管辖权的,由最先立案的行政机关管辖
+B、对管辖发生争议的,应当协商解决
+C、协商不成的,报请共同的上一级行政机关指定管辖
+D、不可以直接由共同的上一级行政机关指定管辖
+D
+【答案】D
+简单
+
+6、关于行政处罚协助制度,下列哪一说法是正确的()。
+单选题
+A、行政机关因实施行政处罚的需要,可以命令其他行政机关协助
+B、协助事项可以超越被请求机关职权范围
+C、协助制度是一种特殊的公法制度
+D、与行政委托制度具有同质性
+C
+【答案】C
+简单
+
+7、关于案件移送制度,下列哪一说法是错误的()。
+单选题
+A、案件移送指的是行政机关向司法机关移送案件
+B、行政机关需要移送的案件是违法行为涉嫌犯罪的
+C、对依法不需要追究刑事责任或者免予刑事处罚,但应当给予行政处罚的,司法机关应当及时将案件移送有关行政机关
+D、《行政执法机关移送涉嫌犯罪案件的规定》(2020修订)中对于行政机关向司法机关移送案件的实体和程序要求进行了具体规定
+A
+【答案】A
+简单
+
+8、关于责令改正制度,下列哪一说法是错误的()。
+单选题
+A、责令改正属于一种行政命令,是行政处罚的配套措施
+B、行政机关实施行政处罚时,可以责令当事人改正或者限期改正违法行为
+C、通过要求违法行为人改正违法行为或者消除违法状态快速的回复被破坏的社会秩序
+D、责令改正包括立即改正和限期改正
+B
+【答案】B
+简单
+
+9、关于没收制度,下列哪一说法是错误的()。
+单选题
+A、当事人有违法所得,都应当没收
+B、依法应当退赔的,可以不予没收
+C、违法所得是指实施违法行为所取得的款项
+D、法律、行政法规、部门规章对违法所得的计算另有规定的,从其规定
+A
+【答案】A
+简单
+
+10、关于一事不再罚制度的适用,下列哪一说法是正确的()。
+单选题
+A、对当事人的同一个违法行为,不得给予两次以上的行政处罚
+B、同一个违法行为违反多个法律规范应当给予罚款处罚的,按照罚款数额高的规定处罚
+C、对同一个违法行为不得既给与罚款处罚,又作出没收决定
+D、对同一个违法行为的判断主要看违法主体的数量
+B
+【答案】B
+简单
+
+11、关于行政处罚的地域管辖,下列哪一说法是正确的()。
+单选题
+A、地域管辖主要解决行政处罚权在行政机关内部的横向分配问题
+B、《行政处罚法》在2020年修订中确立了“违法行为地”原则
+C、只有法律、行政法规可以对地域管辖作出例外规定
+D、海关行政处罚由违法行为发生地海关管辖
+A
+【答案】A
+简单
+
+12、关于互联网领域的违法行为发生地,下列哪一说法是正确的()。
+单选题
+A、用于实施违法行为的网站服务器所在地
+B、被侵害人户籍所在地
+C、网站建立者或者管理者户籍所在地
+D、被侵害的网络及其运营者户籍所在地
+A
+【答案】A
+简单
+
+13、关于行政处罚的级别管辖,下列哪一说法是正确的()。
+单选题
+A、级别管辖主要解决行政处罚决定权在行政机关内部的横向分配问题
+B、行政处罚由市级以上地方人民政府具有行政处罚权的行政机关管辖
+C、法律、行政法规、规章另有规定的,从其规定
+D、行政处罚必须由有行政处罚权的机关作出
+D
+【答案】D
+简单
+
+14、关于行政处罚权的下放,下列哪一观点是正确的()。
+单选题
+A、只有国务院可以决定将行政处罚权下移,其他任何机关不具有这个权力
+B、省级政府可以随意将行政处罚权下移
+C、任何行政处罚权都可以下移
+D、对接下移行政处罚权的可以是符合条件的乡镇人民政府、街道办事处,并且要求要求加强执法能力建设,按照规定范围、依照法定程序实施行政处罚
+D
+【答案】D
+简单
+
+15、关于行政处罚管辖争议的解决,下列哪一说法是正确的()。
+单选题
+A、两个以上行政机关都有管辖权的,由最先知道的行政机关管辖
+B、对管辖发生争议的,应当开会解决
+C、协商不成的,报请共同的上一级行政机关指定管辖
+D、不可以直接由共同的上一级行政机关指定管辖
+C
+【答案】C
+简单
+
+16、关于行政处罚的立法目的,下列哪一说法是正确的()。
+单选题
+A、规范行政处罚的设定
+B、保障行政处罚的规定
+C、监督行政处罚的设定
+D、只维护公共利益
+A
+【答案】A
+简单
+
+17、关于行政处罚的立法依据,下列哪一说法是正确的()。
+单选题
+A、行政法规是立法依据
+B、宪法是立法依据
+C、法律是立法依据
+D、地方性法规是立法依据
+B
+【答案】B
+简单
+
+18、关于行政处罚,下列哪一说法是正确的()。
+单选题
+A、根据法律规定,行政机关享有行政处罚授权
+B、任何国家机关都享有行政处罚权
+C、对于犯罪的相对人可以先给与处罚,再追究刑事责任
+D、行政处罚的手段表现为减损合法权益
+A
+【答案】A
+简单
+
+19、关于《行政处罚法》的适用范围,下列哪一说法是错误的()。
+单选题
+A、行政处罚的设定适用本法
+B、行政处罚的实施适用本法
+C、行政处罚的基本原则适用本法
+D、具体领域的处罚行为认定也必须适用本法
+D
+【答案】D
+简单
+
+20、<p>关于处罚法定原则,下列哪一说法是错误的()。</p>
+单选题
+A、依据法定,包括法律、法规、行政规范性文件
+B、主体法定,指的是有权的行政机关
+C、对象法定,违反行政管理秩序的公民、法人或者其他组织
+D、处罚法定是行政处罚的基本原则
+A
+【答案】A
+简单
+
+21、关于公正原则,下列哪一说法是错误的()。
+单选题
+A、同等情形同等对待
+B、同等对象同等对待
+C、同等情形和同等对象适用法律相同
+D、只要对象相同,就不能作出不同的处理结果
+D
+【答案】D
+简单
+
+22、关于公开原则,下列哪一说法是错误的()。
+单选题
+A、依据公开
+B、程序公开
+C、结果公开
+D、内部会议也必须公开
+D
+【答案】D
+简单
+
+23、关于过罚相当原则,下列哪一说法是错误的()。
+单选题
+A、设定行政处罚必须以事实为依据
+B、不能以罚代刑
+C、根据违法行为具体状态来考量处罚行为的设定和决定
+D、对于虽然没有产生严重危害后果,但是具有不良示范作用的行为,执法人员可以自主决定加重处罚
+D
+【答案】D
+简单
+
+24、关于处罚与教育相结合原则,下列哪一说法是错误的()。
+单选题
+A、以教育代替处罚
+B、没有主观过错不罚
+C、从旧兼从轻原则
+D、首违不罚
+A
+【答案】A
+简单
+
+25、关于救济原则,下列哪一说法是正确的()。
+单选题
+A、陈述申辩权是一种类型的权利
+B、任何人都可以对违法的行政处罚决定起诉
+C、听证属于救济原则的体现
+D、行政处罚机关不需要承担民事责任
+C
+【答案】C
+简单
+
+26、关于行政处罚的立法目的,下列哪一说法是正确的()。
+单选题
+A、规范行政处罚的规定
+B、保障行政处罚的实施
+C、规范行政处罚的设定
+D、只维护公共利益
+C
+【答案】C
+简单
+
+27、关于行政处罚,下列哪一说法是错误的()。
+单选题
+A、根据法律规定,行政机关享有行政处罚授权
+B、任何国家机关都享有行政处罚权
+C、对于犯罪的相对人必须追究刑事责任,不得以罚代刑
+D、行政处罚的手段表现为减损合法权益或者增加义务
+B
+【答案】B
+简单
+
+28、关于《行政处罚法》的适用范围,下列哪一说法是正确的()。
+单选题
+A、行政处罚的规定适用本法
+B、行政处罚的诉讼适用本法
+C、行政处罚的基本原则适用本法
+D、治安管理处罚适用本法
+C
+【答案】C
+简单
+
+29、<p>关于处罚法定原则,下列哪一说法是错误的()。</p>
+单选题
+A、依据法定,包括法律、法规、行政规范性文件
+B、主体法定,指的是有权的国家机关
+C、对象法定,违反行政管理秩序的公民、法人或者其他组织
+D、处罚法定是行政处罚的基本原则
+A
+【答案】A
+简单
+
+30、关于公开原则,下列哪一说法是错误的()。
+单选题
+A、依据公开
+B、程序公开
+C、结果公开
+D、执法人员资格必须公开
+D
+【答案】D
+简单
+
+31、关于行政处罚的执行,下列哪一说法是错误的()。
+单选题
+A、行政处罚的执行包括当事人自我履行和强制执行
+B、强制执行包括行政机关自己执行和申请人民法院执行
+C、行政处罚的执行旨在合法合理的落实行政处罚决定
+D、行政处罚的执行必须属于行政强制措施
+D
+【答案】D
+简单
+
+32、关于当事人自我履行,下列哪一说法是错误的()。
+单选题
+A、原则上,当事人必须自觉履行行政处罚的决定
+B、当事人自我履行必须在行政处罚决定书载明的期限内完成
+C、对于当事人确实有经济困难的,也可以变通执行
+D、当事人如不过不自觉履行执行义务则可能被起诉
+D
+【答案】D
+简单
+
+33、关于行政处罚的强制执行,下列哪一说法是错误的()。
+单选题
+A、行政处罚的强制执行的主体指的是行政机关
+B、到期不缴纳罚款的,每日按罚款数额的百分之三加处罚款,加处罚款的数额不得超出罚款的数额
+C、根据法律规定,将查封、扣押的财物拍卖、依法处理或者将冻结的存款、汇款划拨抵缴罚款
+D、行政机关批准延期、分期缴纳罚款的,申请人民法院强制执行的期限,自暂缓或者分期缴纳罚款期限结束之日起计算
+A
+【答案】A
+简单
+
+34、关于罚缴分离制度,下列哪一说法是错误的()。
+单选题
+A、罚缴分离是为了更好的监督行政机关依法行使行政处罚权
+B、这一制度在《行政处罚法》修订之时新增的内容
+C、作出罚款决定的行政机关应当与收缴罚款的机构分离
+D、除依法当场收缴的罚款外,作出行政处罚决定的行政机关及其执法人员不得自行收缴罚款
+B
+【答案】B
+简单
+
+35、关于不停止执行原则,下列哪一说法是错误的()。
+单选题
+A、当事人对行政处罚决定不服申请行政复议,行政处罚不停止执行,法律另有规定的除外。
+B、当事人对行政处罚决定不服提起行政诉讼,行政处罚不停止执行,行政法规另有规定的除外
+C、暂缓执行是对不停止执行的一个例外
+D、加处罚款的停止执行是另一个例外
+B
+【答案】B
+简单
+
+36、关于行政处罚的处理,下列哪一说法是错误的()。
+单选题
+A、行政处罚的处理主要针对罚款和没收的违法所得或者非法财物
+B、依法没收的非法财物必须按照国家规定公开拍卖
+C、罚款、没收的违法所得或者没收非法财物拍卖的款项,必须全部上缴国库,任何行政机关或者个人不得以任何形式截留、私分或者变相私分。
+D、罚款、没收的违法所得或者没收非法财物拍卖的款项,不得同作出行政处罚决定的行政机关及其工作人员的考核、考评直接或者变相挂钩。除依法应当退还、退赔的外,财政部门不得以任何形式向作出行政处罚决定的行政机关返还罚款、没收的违法所得或者没收非法财物拍卖的款项。
+B
+【答案】B
+简单
+
+37、关于行政处罚的内部监督,下列哪一说法是错误的()。
+单选题
+A、行政机关应当建立健全对行政处罚的监督制度
+B、县级以上人民政府应当定期组织开展行政执法评议、考核
+C、加强对行政处罚的监督检查,规范和保障行政处罚的实施
+D、行政处罚不需要外部监督
+D
+【答案】D
+简单
+
+38、关于行政处罚的外部监督,下列哪一说法是错误的()。
+单选题
+A、行政机关实施行政处罚应当接受社会监督
+B、公民、法人或者其他组织对行政机关的行政处罚行为不服起诉,不属于监督
+C、社会监督与行政机关内部的层级监督的监督主体不同
+D、社会监督包括舆论监督
+B
+【答案】B
+简单
+
+39、关于申请人民法院强制执行,下列哪一说法是错误的。()
+单选题
+A、当事人在法定期限内不申请行政复议或者提起行政诉讼,又不履行行政决定的,没有行政强制执行权的行政机关可以自期限届满之日起三个月内,依照本章规定申请人民法院强制执行
+B、行政机关申请人民法院强制执行前,应当催告当事人履行义务
+C、人民法院接到行政机关强制执行的申请,应当在五日内受理。行政机关对人民法院不予受理的裁定有异议的,可以在十五日内向上一级人民法院申请复议,上一级人民法院应当自收到复议申请之日起十五日内作出是否受理的裁定
+D、人民法院对行政机关强制执行的申请进行实质审查
+D
+【答案】D
+简单
+
+40、关于暂缓拘留的条件,下列哪一说法是错误的()。
+单选题
+A、被处罚人不服行政拘留处罚决定,申请行政复议、提起行政诉讼
+B、向公安机关提出暂缓执行行政拘留的申请
+C、公安机关认为暂缓执行行政拘留不致发生社会危险的
+D、被处罚人或者其近亲属必须提供担保人
+D
+【答案】D
+简单
+
+41、关于行政处罚的执行,下列哪一说法是正确的()。
+单选题
+A、行政处罚的执行包括当事人自我履行和强制措施
+B、强制执行指的是行政机关自己执行
+C、行政处罚的执行旨在合法合理的落实行政处罚决定
+D、行政处罚的执行必须属于行政强制措施
+C
+【答案】C
+简单
+
+42、<p>关于当事人自我履行,下列哪一说法是正确的()。</p>
+单选题
+A、当事人必须自觉履行行政处罚的决定
+B、当事人自我履行行政处罚决定没有期限限制
+C、对于当事人确实有经济困难的,也可以变通执行
+D、当事人如不自觉履行执行义务则可能被起诉
+C
+【答案】C
+简单
+
+43、关于行政处罚的强制执行,下列哪一说法是正确的()。
+单选题
+A、行政处罚的强制执行的主体指的是行政机关
+B、到期不缴纳罚款的,每日按罚款数额的百分之四加处罚款,加处罚款的数额不得超出罚款的数额
+C、根据法律规定,将查封、扣押的财物拍卖、依法处理或者将冻结的存款、汇款划拨抵缴罚款
+D、行政机关批准延期、分期缴纳罚款的,申请人民法院强制执行的期限,自行政机关批准延期、分期缴纳罚款之日起计算
+C
+【答案】C
+简单
+
+44、关于不停止执行原则,下列哪一说法是正确的()。
+单选题
+A、当事人对行政处罚决定不服申请行政复议,行政处罚不停止执行,法律、法规另有规定的除外。
+B、当事人对行政处罚决定不服提起行政诉讼,行政处罚不停止执行,行政法规另有规定的除外
+C、暂缓执行不属于不停止执行的例外
+D、加处罚款的停止执行属于停止执行的例外
+D
+【答案】D
+简单
+
+45、关于行政处罚的处理,下列哪一说法是正确的()。
+单选题
+A、行政处罚的处理主要针对非法财物
+B、依法没收的非法财物必须按照国家规定公开拍卖
+C、罚款、没收的违法所得或者没收非法财物拍卖的款项,必须全部上缴国库,任何行政机关或者个人不得以任何形式截留、私分或者变相私分。
+D、财政部门不得以任何形式向作出行政处罚决定的行政机关返还罚款、没收的违法所得或者没收非法财物拍卖的款项。
+C
+【答案】C
+简单
+
+46、最近一次修订《行政处罚法》是()。
+单选题
+A、1996年3月17日
+B、2009年8月27日
+C、2017年9月1日
+D、2021年1月22日
+D
+【答案】D
+简单
+
+47、()是指行政机关依法对违反行政管理秩序的公民法人或者其他组织,以减损权益或者增加义务的方式予以惩戒的行为。
+单选题
+A、行政强制
+B、行政许可
+C、行政处罚
+D、行政检查
+C
+【答案】C
+简单
+
+48、行政处罚由具有行政处罚权的()在法定职权范围内实施。
+单选题
+A、法院
+B、行政机关
+C、检察院
+D、地方政府
+B
+【答案】B
+简单
+
+49、实施行政处罚,纠正违法行为,应当坚持()相结合,教育公民法人或者其他组织自觉守法。
+单选题
+A、教育和指导
+B、治病与救人
+C、处罚与教育
+D、教育和帮助
+C
+【答案】C
+简单
+
+50、公民法人或者其他组织因违法行为受到行政处罚,其违法行为对他人造成损害的,应当依法承担()责任。违法行为构成犯罪,应当依法追究()责任的,不得以()处罚代替()处罚。
+单选题
+A、民事、刑事、行政、刑事
+B、行政、刑事、刑事、行政
+C、刑事、行政、民事、行政
+D、民事、行政、刑事、行政
+A
+【答案】A
+简单
+
+51、关于行政处罚的地域管辖,下列哪些说法是正确的()。
+多选题
+A、地域管辖主要解决行政处罚权在行政机关内部的纵向分配问题
+B、《行政处罚法》自1996年施行以来一直坚持的是“违法行为地”原则
+C、法律、行政法规、部门规章可以对地域管辖作出例外规定
+D、海关行政处罚由违法行为发生地海关管辖
+BC
+【答案】BC
+简单
+
+52、关于互联网领域的违法行为发生地,下列哪些说法是正确的()。
+多选题
+A、用于实施违法行为的网站服务器所在地
+B、被侵害人户籍所在地
+C、侵害人户籍所在地
+D、被侵害的网络及其运营者所在地
+AD
+【答案】AD
+简单
+
+53、关于行政处罚的级别管辖,下列哪些说法是错误的()。
+多选题
+A、级别管辖主要解决行政处罚决定权在行政机关内部的横向分配问题
+B、行政处罚由县级以上地方人民政府具有行政处罚权的行政机关管辖
+C、法律、行政法规、规章另有规定的,从其规定
+D、行政处罚必须由有行政处罚权的机关作出
+AC
+【答案】AC
+简单
+
+54、关于行政处罚权的下移,下列哪些观点是错误的()。
+多选题
+A、只有自治区、直辖市政府可以决定将行政处罚权下移,其他任何机关不具有这个权力
+B、省级政府并不是可以随意的将行政处罚权下移,而是必须考虑当地的实际情况,包括基层管理迫切需要,能够有效承接
+C、任何行政处罚权都可以下移
+D、对接下移行政处罚权的可以是符合条件的乡镇人民政府、街道办事处,并且要求要求加强执法能力建设,按照规定范围、依照法定程序实施行政处罚
+AC
+【答案】AC
+简单
+
+55、关于行政处罚管辖争议的解决,下列哪些说法是错误的()。
+多选题
+A、两个以上行政机关都有管辖权的,由最先受理的行政机关管辖
+B、对管辖发生争议的,应当协商解决
+C、协商不成的,报请共同的上一级行政机关指定管辖
+D、不可以直接由共同的上一级行政机关指定管辖
+AD
+【答案】AD
+简单
+
+56、关于行政处罚协助制度,下列哪些说法是正确的()。
+多选题
+A、行政机关因实施行政处罚的需要,可以命令其他行政机关协助
+B、协助事项可以超越被请求机关职权范围
+C、协助制度是一种特殊的公法制度
+D、行政处罚协助制度与行政委托制度具有本质区别
+CD
+【答案】CD
+简单
+
+57、关于案件移送制度,下列哪些说法是错误的()。
+多选题
+A、案件移送指的是行政机关向司法机关移送案件
+B、行政机关需要移送的案件是违法行为涉嫌犯罪的
+C、对依法不需要追究刑事责任或者免予刑事处罚,但应当给予行政处罚的,司法机关应当及时将案件移送有关行政机关
+D、对于行政机关移送的案件司法机关不得退回
+AD
+【答案】AD
+简单
+
+58、<p>关于从轻或者减轻处罚制度的适用,下列哪些说法是错误的()。</p>
+多选题
+A、“减轻”是指降低一个处罚幅度进行处罚
+B、“从轻”是在一个处罚幅度内选择较轻的标准进行处罚
+C、已满十四周岁不满十六周岁的未成年人有违法行为的,应当从轻或者减轻行政处罚
+D、尚未完全丧失辨认或者控制自己行为能力的精神病人、智力残疾人有违法行为的,应当从轻行政处罚
+CD
+【答案】CD
+简单
+
+59、<p>对于不予处罚制度的适用,下列哪些说法是错误的()。</p>
+多选题
+A、“不予处罚”指的是针对已经发生的违法行为,不认为是违法
+B、不满十八周岁的未成年人有违法行为的,不予行政处罚,责令监护人加以管教。
+C、神病人、智力残疾人在不能辨认或者不能控制自己行为时有违法行为的,不予行政处罚,但应当责令其监护人严加看管和治疗。间歇性精神病人在精神正常时有违法行为的,应当给予行政处罚
+D、违法行为轻微并及时改正,没有造成危害后果的,不予行政处罚。初次违法且危害后果轻微并及时改正的,可以不予行政处罚。当事人有证据足以证明没有主观过错的,不予行政处罚。法律、行政法规另有规定的,从其规定。对当事人的违法行为依法不予行政处罚的,行政机关应当对当事人进行教育。
+AB
+【答案】AB
+简单
+
+60、对于折抵制度的适用,下列哪些说法是错误的()。
+多选题
+A、刑期的折抵,一天折抵两天
+B、罚金的折抵,等量折抵
+C、一个可能既需要判处罚金,也需要进行罚款的违法行为,如果已经判处罚金了,行政机关尚未给予当事人罚款的,那么就不需要再作出罚款行政处罚决定
+D、无期徒刑也可以折抵
+AD
+【答案】AD
+简单
+
+61、关于行政处罚权的下移,下列哪些观点是正确的()。
+多选题
+A、省、自治区、直辖市根据当地实际情况可以决定将行政处罚权下移,其他任何机关不具有这个权力
+B、省级政府并不是可以随意的将行政处罚权下移,而是必须考虑当地的实际情况
+C、县级人民政府部门的行政处罚权可以下移
+D、对接下移行政处罚权的可以是符合条件的乡镇人民政府、街道办事处,并且要求要求加强执法能力建设,按照规定范围、依照法定程序实施行政处罚
+ABCD
+【答案】ABCD
+简单
+
+62、关于行政处罚管辖争议的解决,下列哪些说法是正确的()。
+多选题
+A、两个以上行政机关都有管辖权的,由最先受理的行政机关管辖
+B、对管辖发生争议的,应当协商解决
+C、协商不成的,报请共同的上一级行政机关指定管辖
+D、也可以不经协商直接由共同的上一级行政机关指定管辖
+BCD
+【答案】BCD
+简单
+
+63、关于行政处罚协助制度,下列哪些说法是正确的()。
+多选题
+A、行政机关因实施行政处罚的需要,可以请求其他行政机关协助
+B、协助事项不可以超越被请求机关职权范围
+C、协助制度是一种特殊的公法制度
+D、行政处罚协助制度与行政委托制度在主体、名义、职权归属以及责任承担等方面都存在区别
+ABCD
+【答案】ABCD
+简单
+
+64、关于案件移送制度,下列哪些说法是正确的()。
+多选题
+A、案件移送指的是行政机关和司法机关互相之间进行案件移送
+B、行政机关需要移送的案件是违法行为涉嫌犯罪的
+C、对依法不需要追究刑事责任或者免予刑事处罚,但应当给予行政处罚的,司法机关应当及时将案件移送有关行政机关
+D、对于行政机关移送的案件司法机关可以退回
+ABCD
+【答案】ABCD
+简单
+
+65、对于不予处罚制度的适用,下列哪些说法是正确的()。
+多选题
+A、当事人有证据足以证明没有主观过错的,不予行政处罚
+B、不满十四周岁的未成年人有违法行为的,不予行政处罚,责令监护人加以管教。
+C、神病人、智力残疾人在不能辨认或者不能控制自己行为时有违法行为的,不予行政处罚,但应当责令其监护人严加看管和治疗
+D、违法行为轻微并及时改正,没有造成危害后果的,不予行政处罚。初次违法且危害后果轻微并及时改正的,可以不予行政处罚
+ABCD
+【答案】ABCD
+简单
+
+66、关于行政处罚的立法目的,下列哪些说法是错误的()。
+多选题
+A、规范行政处罚的设定
+B、支持行政处罚的规定
+C、监督行政处罚的设定
+D、维护公共利益和特殊的个人利益
+BCD
+【答案】BCD
+简单
+
+67、关于行政处罚的立法依据,下列哪些说法是错误的()。
+多选题
+A、行政法规是直接立法依据
+B、宪法是立法依据
+C、法律是根本立法依据
+D、地方性法规是具体立法依据
+ACD
+【答案】ACD
+简单
+
+68、关于行政处罚的概念,下列哪些说法是错误的()。
+多选题
+A、根据法律的规定,行政机关可以享有行政处罚授权
+B、司法机关也享有一定的行政处罚权
+C、对于犯罪的相对人可以先给与处罚,再追究刑事责任
+D、行政处罚的手段表现为增加义务
+BCD
+【答案】BCD
+简单
+
+69、关于《行政处罚法》的适用范围,下列哪些说法是正确的()。
+多选题
+A、行政处罚的设定适用本法
+B、行政处罚的实施适用本法
+C、行政处罚的基本原则适用本法
+D、治安管理处罚也必须适用本法
+ABC
+【答案】ABC
+简单
+
+70、关于公正原则,下列哪些说法是正确的()。
+多选题
+A、同等情形同等对待
+B、同等对象同等对待
+C、同等情形和同等对象适用法律相同
+D、只要对象相同,就不能作出不同的处理结果
+ABC
+【答案】ABC
+简单
+
+71、关于公开原则,下列哪些说法是正确的()。
+多选题
+A、依据公开
+B、程序公开
+C、结果公开
+D、内部会议也必须公开
+ABC
+【答案】ABC
+简单
+
+72、关于过罚相当原则,下列哪些说法是正确的()。
+多选题
+A、设定行政处罚必须以事实为依据
+B、不能以罚代刑,但是可以以刑代罚
+C、根据违法行为具体状态来考量处罚行为的设定和决定
+D、对于虽然没有产生严重危害后果,但是具有不良示范作用的行为,执法人员可以自主决定加重处罚
+AC
+【答案】AC
+简单
+
+73、下列哪些说法体现了处罚与教育相结合原则()。
+多选题
+A、以教育代替处罚
+B、没有主观过错不罚
+C、从旧兼从轻
+D、首违不罚
+BCD
+【答案】BCD
+简单
+
+74、关于救济原则,下列哪些说法是错误的()。
+多选题
+A、陈述权和申辩权性质一样
+B、任何人都可以对违法的行政处罚决定起诉
+C、听证属于救济原则的体现
+D、行政处罚机关不需要承担民事责任
+ABD
+【答案】ABD
+简单
+
+75、关于行政处罚的立法目的,下列哪些说法是错误的()。
+多选题
+A、规范行政处罚的设定
+B、支持行政处罚的规定
+C、监督行政管理的实施
+D、维护公共利益和行业秩序
+BD
+【答案】BD
+简单
+
+76、关于行政处罚的立法依据,下列哪些说法是正确的()。
+多选题
+A、行政法规是直接立法依据
+B、宪法是立法依据
+C、法律不是根本立法依据
+D、个别地方性法规是具体立法依据
+BC
+【答案】BC
+简单
+
+77、关于过罚相当原则,下列哪些说法是正确的()。
+多选题
+A、行政处罚决定必须以事实为依据
+B、不能以罚代刑
+C、根据违法行为具体状态来考量处罚行为的设定和决定
+D、对于虽然没有产生严重危害后果,但是具有不良示范作用的行为,执法人员可以自主决定加重处罚
+ABC
+【答案】ABC
+简单
+
+78、下列哪些说法体现了处罚与教育相结合原则()。
+多选题
+A、以教育手段弥补处罚措施的不足
+B、没有主观过错不罚
+C、从旧兼从轻
+D、首违不罚
+ABCD
+【答案】ABCD
+简单
+
+79、关于救济原则,下列哪些说法是正确的()。
+多选题
+A、陈述权和申辩权性质不一样
+B、任何人都可以对违法的行政处罚决定起诉
+C、听证属于救济原则的体现
+D、行政处罚机关不需要承担民事责任
+AC
+【答案】AC
+简单
+
+80、关于行政处罚的执行,下列哪些说法是错误的()。
+多选题
+A、行政处罚的执行指的是行政机关强制执行
+B、强制执行包括行政机关自己执行和申请人民法院执行
+C、行政处罚的执行旨在合法合理的落实行政处罚决定
+D、行政处罚的执行必须属于行政强制措施
+AD
+【答案】AD
+简单
+
+81、关于当事人自我履行,下列哪些说法是错误的()。
+多选题
+A、原则上,当事人必须自觉履行行政处罚的决定
+B、当事人自我履行必须在行政处罚决定书载明的期限内完成
+C、当事人必须不折不扣的执行处罚决定,不得变通执行
+D、当事人如不过不自觉履行执行义务则可能被起诉
+CD
+【答案】CD
+简单
+
+82、关于行政处罚的强制执行,下列哪些说法是错误的()。
+多选题
+A、行政处罚的强制执行的主体指的是行政机关
+B、到期不缴纳罚款的,每日按罚款数额的百分之五加处罚款,加处罚款的数额不得超出罚款的数额
+C、根据法律规定,将查封、扣押的财物拍卖、依法处理或者将冻结的存款、汇款划拨抵缴罚款
+D、行政机关批准延期、分期缴纳罚款的,申请人民法院强制执行的期限,自暂缓或者分期缴纳罚款期限结束之日起计算
+AB
+【答案】AB
+简单
+
+83、关于罚缴分离制度,下列哪些说法是错误的()。
+多选题
+A、罚缴分离是为了更好的收缴罚款
+B、这一制度在《行政处罚法》修订之时新增的内容
+C、作出罚款决定的行政机关应当与收缴罚款的机构分离
+D、除依法当场收缴的罚款外,作出行政处罚决定的行政机关及其执法人员不得自行收缴罚款
+AB
+【答案】AB
+简单
+
+84、关于不停止执行原则,下列哪些说法是错误的()。
+多选题
+A、当事人对行政处罚决定不服申请行政复议,行政处罚不停止执行,法律、行政法规另有规定的除外。
+B、当事人对行政处罚决定不服提起行政诉讼,行政处罚不停止执行,行政法规另有规定的除外
+C、暂缓执行是对不停止执行的一个例外
+D、加处罚款的停止执行是另一个例外
+AB
+【答案】AB
+简单
+
+85、关于行政处罚的处理,下列哪些说法是错误的()。
+多选题
+A、行政处罚的处理主要针对罚款
+B、依法没收的非法财物必须按照国家规定公开拍卖
+C、罚款、没收的违法所得或者没收非法财物拍卖的款项,必须全部上缴国库,任何行政机关或者个人不得以任何形式截留、私分或者变相私分。
+D、罚款、没收的违法所得或者没收非法财物拍卖的款项,不得同作出行政处罚决定的行政机关及其工作人员的考核、考评直接或者变相挂钩。除依法应当退还、退赔的外,财政部门不得以任何形式向作出行政处罚决定的行政机关返还罚款、没收的违法所得或者没收非法财物拍卖的款项。
+AB
+【答案】AB
+简单
+
+86、关于行政处罚的内部监督,下列哪些说法是正确的()。
+多选题
+A、行政机关应当建立健全对行政处罚的监督制度
+B、县级以上人民政府应当定期组织开展行政执法评议、考核
+C、加强对行政处罚的监督检查,规范和保障行政处罚的实施
+D、行政处罚不需要外部监督
+ABC
+【答案】ABC
+简单
+
+87、关于行政处罚的外部监督,下列哪些说法是错误的()。
+多选题
+A、行政机关实施行政处罚应当接受社会监督
+B、公民、法人或者其他组织对行政机关的行政处罚行为不服起诉,不属于监督
+C、社会监督与行政机关内部的层级监督的监督主体不同
+D、社会监督不包括舆论监督
+BD
+【答案】BD
+简单
+
+88、关于申请人民法院强制执行,下列哪些说法是错误的()。
+多选题
+A、当事人在法定期限内不申请行政复议或者提起行政诉讼,又不履行行政决定的,没有行政强制执行权的行政机关可以自期限届满之日起六个月内,依照本章规定申请人民法院强制执行
+B、行政机关申请人民法院强制执行前,应当催告当事人履行义务
+C、人民法院接到行政机关强制执行的申请,应当在五日内受理。行政机关对人民法院不予受理的裁定有异议的,可以在十五日内向上一级人民法院申请复议,上一级人民法院应当自收到复议申请之日起十五日内作出是否受理的裁定
+D、人民法院对行政机关强制执行的申请进行实质审查
+AD
+【答案】AD
+简单
+
+89、关于暂缓拘留的条件,下列哪些说法是错误的()。
+多选题
+A、被处罚人不服行政拘留处罚决定可以直接申请暂缓执行
+B、向公安机关提出暂缓执行行政拘留的申请
+C、公安机关认为暂缓执行行政拘留不致发生社会危险的
+D、被处罚人或者其近亲属必须提供担保人
+AD
+【答案】AD
+简单
+
+90、关于申请人民法院强制执行,下列哪些说法是错误的()。
+多选题
+A、当事人在法定期限内不申请行政复议或者提起行政诉讼,又不履行行政决定的,没有行政强制执行权的行政机关可以自期限届满之日起六个月内,依照本章规定申请人民法院强制执行
+B、行政机关申请人民法院强制执行前,应当催告当事人履行义务
+C、人民法院接到行政机关强制执行的申请,应当在五日内受理。行政机关对人民法院不予受理的裁定有异议的,可以在十五日内向上一级人民法院申请复议,上一级人民法院应当自收到复议申请之日起十五日内作出是否受理的裁定
+D、人民法院对行政机关强制执行的申请进行实质审查
+AD
+【答案】AD
+简单
+
+91、关于行政处罚的执行,下列哪些说法是正确的()。
+多选题
+A、行政处罚的执行指的是行政机关强制执行和申请人民法院强制执行
+B、强制执行包括行政机关自己执行和申请人民法院执行
+C、行政处罚的执行旨在合法合理的落实行政处罚决定
+D、行政处罚的执行属于行政强制措施
+BC
+【答案】BC
+简单
+
+92、关于行政处罚的强制执行,下列哪些说法是正确的()。
+多选题
+A、行政处罚的强制执行的主体指的是行政机关和人民法院
+B、到期不缴纳罚款的,每日按罚款数额的百分之五加处罚款,加处罚款的数额不得超出罚款的数额
+C、根据法律规定,将查封、扣押的财物拍卖、依法处理或者将冻结的存款、汇款划拨抵缴罚款
+D、行政机关批准延期、分期缴纳罚款的,申请人民法院强制执行的期限,自暂缓或者分期缴纳罚款期限结束之日起计算
+ACD
+【答案】ACD
+简单
+
+93、关于不停止执行原则,下列哪些说法是正确的()。
+多选题
+A、当事人对行政处罚决定不服申请行政复议,行政处罚不停止执行,法律另有规定的除外
+B、当事人对行政处罚决定不服提起行政诉讼,行政处罚不停止执行,行政法规另有规定的除外
+C、暂缓执行是对不停止执行的一个例外
+D、加处罚款的停止执行是另一个例外
+ACD
+【答案】ACD
+简单
+
+94、关于行政处罚的内部监督,下列哪些说法是错误的()。
+多选题
+A、行政机关应当建立健全对行政处罚的监督制度
+B、市级以上人民政府应当定期组织开展行政执法评议、考核
+C、加强对行政处罚的监督检查,规范和保障行政处罚的实施
+D、行政处罚不需要外部监督
+BD
+【答案】BD
+简单
+
+95、公民法人或者其他组织违反行政管理秩序的行为,应当给予行政处罚的,依照本法由()、()、()规定,并由行政机关依照本法规定的程序实施。
+多选题
+A、法律
+B、法规
+C、规章
+D、其他规范性文件
+ABC
+【答案】ABC
+简单
+
+96、下列属于行政处罚的是()。
+多选题
+A、通报批评
+B、限制开展生产经营活动
+C、行政拘留
+D、降低资质等级
+ABCD
+【答案】ABCD
+简单
+
+97、设定和实施行政处罚必须以事实为依据,与违法行为的()、()、()以及()相当。
+多选题
+A、事实
+B、性质
+C、情节
+D、社会危害程度
+ABCD
+【答案】ABCD
+简单
+
+98、地方性法规可以设定除()、()以外的行政处罚。
+多选题
+A、限制人身自由
+B、吊销营业执照
+C、责令停产停业
+D、通报批评
+AB
+【答案】AB
+简单
+
+99、()和()及其有关部门应当定期组织评估行政处罚的实施情况和必要性,对不适当的行政处罚事项及种类罚款数额等,应当提出修改或者废止的建议。
+多选题
+A、国务院部门
+B、省自治区直辖市人民代表大会常务委员会
+C、省自治区直辖市人民代表大会
+D、省自治区直辖市人民政府
+AD
+【答案】AD
+简单
+
+100、国家在城市管理市场监管()、()、()、()农业等领域推行建立综合行政执法制度,相对集中行政处罚权。
+多选题
+A、生态环境
+B、文化市场
+C、交通运输
+D、应急管理
+ABCD
+【答案】ABCD
+简单
+
+101、违法行为在二年内未被发现的,一律不再给予行政处罚。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+102、所谓从旧兼从轻指的是在适用法律时,原则上适用旧法,当旧法不存在时适用新法。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+103、行政处罚过程中没有听取当事人的陈述、申辩的无效。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+104、违法行为仅仅指的是实施(发生)地。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+105、行政处罚机关因管辖权产生争议的必须经过协商解决。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+106、受他人胁迫或者诱骗实施违法行为的应当从轻或者减轻行政处罚。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+107、对当事人的违法行为依法不予行政处罚的,行政机关应当对当事人进行教育。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+108、涉及公民生命健康安全的,违法行为在四年内未被发现的,不再给予行政处罚。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+109、主动供述行政机关尚未掌握的违法行为的不予处罚。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+110、配合行政机关查处违法行为有立功表现的不予处罚。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+111、行政处罚的终极目的是为了保障和监督监督行政机关有效实施行政管理。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+112、行政处罚的依据只有宪法。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+113、行政处罚是指行政机关在行政管理过程中,对违反行政管理秩序的公民、法人或者其他组织,以依法减损权利或者增加义务的方式予以惩戒的行为。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+114、处罚法定意味着行政机关在处罚过程中没有裁量余地。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+115、公开原则要求行政处罚的依据必须公开。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+116、过罚相当原则要求行政机关只能根据行为情节决定处罚。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+117、处罚与教育相结合原则任何处罚决定作出前必须先对相对人进行教育。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+118、公民、法人或者其他组织因行政机关违法给予行政处罚受到损害的,有权依法提出赔偿要求。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+119、公民、法人或者其他组织违反行政管理秩序的行为只需要承担行政责任。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+120、行政处罚只能通过减损权益的方式实现惩戒目的。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+121、规范行政处罚的设定和实施是《行政处罚法》的立法目的之一。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+122、《行政处罚法》的制定依据是《宪法》。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+123、行政处罚的制裁性表现为减损合法权益。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+124、处罚法定原则依然赋予了行政处罚机关裁量权。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+125、公开原则要求行政处罚的一切事项都必须公开。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+126、过罚相当原则要求根据违法行为的事实、性质、情节以及社会危害程度来考量处罚行为的设定和决定。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+127、处罚和教育都只是手段不是终极目的。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+128、行政机关在行政处罚中可能需要承担民事赔偿责任。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+129、公民、法人或者其他组织违反行政管理秩序的行为需要承担行政责任、民事责任或者刑事责任。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+130、从旧兼从轻体现了教育与处罚相结合的原则。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+131、行政处罚的执行指的是行政机关强制执行。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+132、当事人必须按照行政处罚决定执行,不得有任何变通。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+133、行政机关可以按照《行政处罚法》的规定申请人民法院强制执行。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+134、作出罚款决定的行政机关应当与收缴罚款的机构分离,不存在例外情形。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+135、依法给予一百元以下罚款的可以当场收缴。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+136、在边远、水上、交通不便地区,行政机关及其执法人员依法作出罚款决定后,当事人到指定的银行或者通过电子支付系统缴纳罚款确有困难,行政机关及其执法人员可以当场收缴罚款。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+137、当事人对行政处罚决定不服,申请行政复议或者提起行政诉讼的,行政处罚不停止执行。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+138、依法没收的非法财物必须按照国家规定公开拍卖。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+139、罚款、没收的违法所得或者没收非法财物拍卖的款项,不得同作出行政处罚决定的行政机关及其工作人员的考核、考评直接或者变相挂钩。财政部门不得以任何形式向作出行政处罚决定的行政机关返还罚款、没收的违法所得或者没收非法财物拍卖的款项。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+140、行政机关应当建立健全对行政处罚的监督制度。省级以上人民政府应当定期组织开展行政执法评议、考核,加强对行政处罚的监督检查,规范和保障行政处罚的实施。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+141、行政处罚的执行指的是行政机关强制执行和申请人民法院强制执行。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+142、当事人确有经济困难,需要延期或者分期缴纳罚款的,行政机关可以主动提出暂缓或者分期缴纳。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+143、行政机关应该按照《行政诉讼法》的规定申请人民法院强制执行。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+144、当事人应当自收到行政处罚决定书之日起十五日内,到指定的银行或者通过电子支付系统缴纳罚款。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+145、不当场收缴事后难以执行的可以当场收缴。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+146、违法事实确凿并有法定依据,对公民处以二百元以下、对法人或者其他组织处以三千元以下罚款或者警告的行政处罚的,可以当场作出行政处罚决定。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+147、罚款、没收的违法所得或者没收非法财物拍卖的款项,不得同作出行政处罚决定的行政机关及其工作人员的考核、考评直接或者变相挂钩。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+148、违法事实不能成立的,不予行政处罚。()
+判断题
+A、错误
+B、正确
+C、
+D、
+B
+【答案】正确
+简单
+
+149、行政机关及其执法人员当场收缴罚款的,必须向当事人出具国务院财政部门或者省、自治区、直辖市人民政府财政部门统一制发的专用票据;不出具财政部门统一制发的专用票据的,当事人缴纳罚款后可以起诉。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+
+150、执法人员当场收缴的罚款,一律应当自收缴罚款之日起二日内,交至行政机关。()
+判断题
+A、错误
+B、正确
+C、
+D、
+A
+【答案】错误
+简单
+

BIN
rlzc-admin/src/main/resources/upload/sysnotice/250410/sgjMRY/处罚法.docx


+ 45 - 0
rlzc-admin/src/test/java/cn/rlzc/test/AssertUnitTest.java

@@ -0,0 +1,45 @@
+package cn.rlzc.test;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/**
+ * 断言单元测试案例
+ *
+ * @author Lion Li
+ */
+@DisplayName("断言单元测试案例")
+public class AssertUnitTest {
+
+    @DisplayName("测试 assertEquals 方法")
+    @Test
+    public void testAssertEquals() {
+        Assertions.assertEquals("666", new String("666"));
+        Assertions.assertNotEquals("666", new String("666"));
+    }
+
+    @DisplayName("测试 assertSame 方法")
+    @Test
+    public void testAssertSame() {
+        Object obj = new Object();
+        Object obj1 = obj;
+        Assertions.assertSame(obj, obj1);
+        Assertions.assertNotSame(obj, obj1);
+    }
+
+    @DisplayName("测试 assertTrue 方法")
+    @Test
+    public void testAssertTrue() {
+        Assertions.assertTrue(true);
+        Assertions.assertFalse(true);
+    }
+
+    @DisplayName("测试 assertNull 方法")
+    @Test
+    public void testAssertNull() {
+        Assertions.assertNull(null);
+        Assertions.assertNotNull(null);
+    }
+
+}

+ 70 - 0
rlzc-admin/src/test/java/cn/rlzc/test/DemoUnitTest.java

@@ -0,0 +1,70 @@
+package cn.rlzc.test;
+
+import cn.rlzc.common.web.config.properties.CaptchaProperties;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 单元测试案例
+ *
+ * @author Lion Li
+ */
+@SpringBootTest // 此注解只能在 springboot 主包下使用 需包含 main 方法与 yml 配置文件
+@DisplayName("单元测试案例")
+public class DemoUnitTest {
+
+    @Autowired
+    private CaptchaProperties captchaProperties;
+
+    @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解")
+    @Test
+    public void testTest() {
+        System.out.println(captchaProperties);
+    }
+
+    @Disabled
+    @DisplayName("测试 @Disabled 注解")
+    @Test
+    public void testDisabled() {
+        System.out.println(captchaProperties);
+    }
+
+    @Timeout(value = 2L, unit = TimeUnit.SECONDS)
+    @DisplayName("测试 @Timeout 注解")
+    @Test
+    public void testTimeout() throws InterruptedException {
+        Thread.sleep(3000);
+        System.out.println(captchaProperties);
+    }
+
+
+    @DisplayName("测试 @RepeatedTest 注解")
+    @RepeatedTest(3)
+    public void testRepeatedTest() {
+        System.out.println(666);
+    }
+
+    @BeforeAll
+    public static void testBeforeAll() {
+        System.out.println("@BeforeAll ==================");
+    }
+
+    @BeforeEach
+    public void testBeforeEach() {
+        System.out.println("@BeforeEach ==================");
+    }
+
+    @AfterEach
+    public void testAfterEach() {
+        System.out.println("@AfterEach ==================");
+    }
+
+    @AfterAll
+    public static void testAfterAll() {
+        System.out.println("@AfterAll ==================");
+    }
+
+}

+ 72 - 0
rlzc-admin/src/test/java/cn/rlzc/test/ParamUnitTest.java

@@ -0,0 +1,72 @@
+package cn.rlzc.test;
+
+import cn.rlzc.common.core.enums.UserType;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.NullSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+/**
+ * 带参数单元测试案例
+ *
+ * @author Lion Li
+ */
+@DisplayName("带参数单元测试案例")
+public class ParamUnitTest {
+
+    @DisplayName("测试 @ValueSource 注解")
+    @ParameterizedTest
+    @ValueSource(strings = {"t1", "t2", "t3"})
+    public void testValueSource(String str) {
+        System.out.println(str);
+    }
+
+    @DisplayName("测试 @NullSource 注解")
+    @ParameterizedTest
+    @NullSource
+    public void testNullSource(String str) {
+        System.out.println(str);
+    }
+
+    @DisplayName("测试 @EnumSource 注解")
+    @ParameterizedTest
+    @EnumSource(UserType.class)
+    public void testEnumSource(UserType type) {
+        System.out.println(type.getUserType());
+    }
+
+    @DisplayName("测试 @MethodSource 注解")
+    @ParameterizedTest
+    @MethodSource("getParam")
+    public void testMethodSource(String str) {
+        System.out.println(str);
+    }
+
+    public static Stream<String> getParam() {
+        List<String> list = new ArrayList<>();
+        list.add("t1");
+        list.add("t2");
+        list.add("t3");
+        return list.stream();
+    }
+
+    @BeforeEach
+    public void testBeforeEach() {
+        System.out.println("@BeforeEach ==================");
+    }
+
+    @AfterEach
+    public void testAfterEach() {
+        System.out.println("@AfterEach ==================");
+    }
+
+
+}

+ 54 - 0
rlzc-admin/src/test/java/cn/rlzc/test/TagUnitTest.java

@@ -0,0 +1,54 @@
+package cn.rlzc.test;
+
+import org.junit.jupiter.api.*;
+import org.springframework.boot.test.context.SpringBootTest;
+
+/**
+ * 标签单元测试案例
+ *
+ * @author Lion Li
+ */
+@SpringBootTest
+@DisplayName("标签单元测试案例")
+public class TagUnitTest {
+
+    @Tag("dev")
+    @DisplayName("测试 @Tag dev")
+    @Test
+    public void testTagDev() {
+        System.out.println("dev");
+    }
+
+    @Tag("prod")
+    @DisplayName("测试 @Tag prod")
+    @Test
+    public void testTagProd() {
+        System.out.println("prod");
+    }
+
+    @Tag("local")
+    @DisplayName("测试 @Tag local")
+    @Test
+    public void testTagLocal() {
+        System.out.println("local");
+    }
+
+    @Tag("exclude")
+    @DisplayName("测试 @Tag exclude")
+    @Test
+    public void testTagExclude() {
+        System.out.println("exclude");
+    }
+
+    @BeforeEach
+    public void testBeforeEach() {
+        System.out.println("@BeforeEach ==================");
+    }
+
+    @AfterEach
+    public void testAfterEach() {
+        System.out.println("@AfterEach ==================");
+    }
+
+
+}

+ 0 - 0
ruoyi-admin/zhFonts/.uuid → rlzc-admin/zhFonts/.uuid


+ 0 - 0
ruoyi-admin/zhFonts/SIMSUN.TTC → rlzc-admin/zhFonts/SIMSUN.TTC


+ 0 - 0
ruoyi-admin/zhFonts/fonts.dir → rlzc-admin/zhFonts/fonts.dir


+ 0 - 0
ruoyi-admin/zhFonts/fonts.scale → rlzc-admin/zhFonts/fonts.scale


+ 46 - 0
rlzc-common/pom.xml

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>rlzc-vue-plus</artifactId>
+        <groupId>cn.rlzc</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <modules>
+        <module>rlzc-common-bom</module>
+        <module>rlzc-common-social</module>
+        <module>rlzc-common-core</module>
+        <module>rlzc-common-doc</module>
+        <module>rlzc-common-excel</module>
+        <module>rlzc-common-idempotent</module>
+        <module>rlzc-common-job</module>
+        <module>rlzc-common-log</module>
+        <module>rlzc-common-mail</module>
+        <module>rlzc-common-mybatis</module>
+        <module>rlzc-common-oss</module>
+        <module>rlzc-common-ratelimiter</module>
+        <module>rlzc-common-redis</module>
+        <module>rlzc-common-satoken</module>
+        <module>rlzc-common-security</module>
+        <module>rlzc-common-sms</module>
+        <module>rlzc-common-web</module>
+        <module>rlzc-common-translation</module>
+        <module>rlzc-common-sensitive</module>
+        <module>rlzc-common-json</module>
+        <module>rlzc-common-encrypt</module>
+        <module>rlzc-common-tenant</module>
+        <module>rlzc-common-websocket</module>
+        <module>rlzc-common-sse</module>
+    </modules>
+
+    <artifactId>rlzc-common</artifactId>
+    <packaging>pom</packaging>
+
+    <description>
+        common 通用模块
+    </description>
+
+</project>

+ 185 - 0
rlzc-common/rlzc-common-bom/pom.xml

@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>cn.rlzc</groupId>
+    <artifactId>rlzc-common-bom</artifactId>
+    <version>${revision}</version>
+    <packaging>pom</packaging>
+
+    <description>
+        rlzc-common-bom common依赖项
+    </description>
+
+    <properties>
+        <revision>5.3.1</revision>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- 核心模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-core</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 接口模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-doc</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- excel -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-excel</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 幂等 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-idempotent</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 调度模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-job</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 日志记录 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-log</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 邮件服务 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-mail</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 数据库服务 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-mybatis</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- OSS -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-oss</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 限流 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-ratelimiter</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 缓存服务 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-redis</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- satoken -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-satoken</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 安全模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-security</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 短信模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-sms</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-social</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- web服务 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-web</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 翻译模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-translation</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 脱敏模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-sensitive</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 序列化模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-json</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 数据库加解密模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-encrypt</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 租户模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-tenant</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- WebSocket模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-websocket</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- SSE模块 -->
+            <dependency>
+                <groupId>cn.rlzc</groupId>
+                <artifactId>rlzc-common-sse</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+
+</project>

+ 99 - 0
rlzc-common/rlzc-common-core/pom.xml

@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>cn.rlzc</groupId>
+        <artifactId>rlzc-common</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>rlzc-common-core</artifactId>
+
+    <description>
+        rlzc-common-core 核心模块
+    </description>
+
+    <dependencies>
+        <!-- Spring框架基本的核心工具 -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context-support</artifactId>
+        </dependency>
+
+        <!-- SpringWeb模块 -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+        </dependency>
+
+        <!-- 自定义验证注解 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+        <!--常用工具类 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
+        <!-- servlet包 -->
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-http</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-extra</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <!--  自动生成YML配置关联JSON文件  -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-properties-migrator</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>io.github.linpeilie</groupId>
+            <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
+        </dependency>
+
+        <!-- 离线IP地址定位库 -->
+        <dependency>
+            <groupId>org.lionsoul</groupId>
+            <artifactId>ip2region</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 17 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/config/ApplicationConfig.java

@@ -0,0 +1,17 @@
+package cn.rlzc.common.core.config;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+/**
+ * 程序注解配置
+ *
+ * @author Lion Li
+ */
+@AutoConfiguration
+@EnableAspectJAutoProxy
+@EnableAsync(proxyTargetClass = true)
+public class ApplicationConfig {
+
+}

+ 52 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/config/AsyncConfig.java

@@ -0,0 +1,52 @@
+package cn.rlzc.common.core.config;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.rlzc.common.core.exception.ServiceException;
+import cn.rlzc.common.core.utils.SpringUtils;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.core.task.VirtualThreadTaskExecutor;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+
+/**
+ * 异步配置
+ * <p>
+ * 如果未使用虚拟线程则生效
+ *
+ * @author Lion Li
+ */
+@AutoConfiguration
+public class AsyncConfig implements AsyncConfigurer {
+
+    /**
+     * 自定义 @Async 注解使用系统线程池
+     */
+    @Override
+    public Executor getAsyncExecutor() {
+        if(SpringUtils.isVirtual()) {
+            return new VirtualThreadTaskExecutor("async-");
+        }
+        return SpringUtils.getBean("scheduledExecutorService");
+    }
+
+    /**
+     * 异步执行异常处理
+     */
+    @Override
+    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+        return (throwable, method, objects) -> {
+            throwable.printStackTrace();
+            StringBuilder sb = new StringBuilder();
+            sb.append("Exception message - ").append(throwable.getMessage())
+                .append(", Method name - ").append(method.getName());
+            if (ArrayUtil.isNotEmpty(objects)) {
+                sb.append(", Parameter value - ").append(Arrays.toString(objects));
+            }
+            throw new ServiceException(sb.toString());
+        };
+    }
+
+}

+ 87 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/config/ThreadPoolConfig.java

@@ -0,0 +1,87 @@
+package cn.rlzc.common.core.config;
+
+import jakarta.annotation.PreDestroy;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import cn.rlzc.common.core.config.properties.ThreadPoolProperties;
+import cn.rlzc.common.core.utils.SpringUtils;
+import cn.rlzc.common.core.utils.Threads;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.task.VirtualThreadTaskExecutor;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 线程池配置
+ *
+ * @author Lion Li
+ **/
+@Slf4j
+@AutoConfiguration
+@EnableConfigurationProperties(ThreadPoolProperties.class)
+public class ThreadPoolConfig {
+
+    /**
+     * 核心线程数 = cpu 核心数 + 1
+     */
+    private final int core = Runtime.getRuntime().availableProcessors() + 1;
+
+    private ScheduledExecutorService scheduledExecutorService;
+
+    @Bean(name = "threadPoolTaskExecutor")
+    @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true")
+    public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(core);
+        executor.setMaxPoolSize(core * 2);
+        executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
+        executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        return executor;
+    }
+
+    /**
+     * 执行周期性或定时任务
+     */
+    @Bean(name = "scheduledExecutorService")
+    protected ScheduledExecutorService scheduledExecutorService() {
+        // daemon 必须为 true
+        BasicThreadFactory.Builder builder = new BasicThreadFactory.Builder().daemon(true);
+        if (SpringUtils.isVirtual()) {
+            builder.namingPattern("virtual-schedule-pool-%d").wrappedFactory(new VirtualThreadTaskExecutor().getVirtualThreadFactory());
+        } else {
+            builder.namingPattern("schedule-pool-%d");
+        }
+        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core,
+            builder.build(),
+            new ThreadPoolExecutor.CallerRunsPolicy()) {
+            @Override
+            protected void afterExecute(Runnable r, Throwable t) {
+                super.afterExecute(r, t);
+                Threads.printException(r, t);
+            }
+        };
+        this.scheduledExecutorService = scheduledThreadPoolExecutor;
+        return scheduledThreadPoolExecutor;
+    }
+
+    /**
+     * 销毁事件
+     */
+    @PreDestroy
+    public void destroy() {
+        try {
+            log.info("====关闭后台任务任务线程池====");
+            Threads.shutdownAndAwaitTermination(scheduledExecutorService);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+    }
+
+}

+ 41 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/config/ValidatorConfig.java

@@ -0,0 +1,41 @@
+package cn.rlzc.common.core.config;
+
+import jakarta.validation.Validator;
+import org.hibernate.validator.HibernateValidator;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
+import org.springframework.context.MessageSource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
+
+import java.util.Properties;
+
+/**
+ * 校验框架配置类
+ *
+ * @author Lion Li
+ */
+@AutoConfiguration(before = ValidationAutoConfiguration.class)
+public class ValidatorConfig {
+
+    /**
+     * 配置校验框架 快速失败模式
+     */
+    @Bean
+    public Validator validator(MessageSource messageSource) {
+        try (LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean()) {
+            // 国际化
+            factoryBean.setValidationMessageSource(messageSource);
+            // 设置使用 HibernateValidator 校验器
+            factoryBean.setProviderClass(HibernateValidator.class);
+            Properties properties = new Properties();
+            // 设置快速失败模式(fail-fast),即校验过程中一旦遇到失败,立即停止并返回错误
+            properties.setProperty("hibernate.validator.fail_fast", "true");
+            factoryBean.setValidationProperties(properties);
+            // 加载配置
+            factoryBean.afterPropertiesSet();
+            return factoryBean.getValidator();
+        }
+    }
+
+}

+ 30 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/config/properties/ThreadPoolProperties.java

@@ -0,0 +1,30 @@
+package cn.rlzc.common.core.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * 线程池 配置属性
+ *
+ * @author Lion Li
+ */
+@Data
+@ConfigurationProperties(prefix = "thread-pool")
+public class ThreadPoolProperties {
+
+    /**
+     * 是否开启线程池
+     */
+    private boolean enabled;
+
+    /**
+     * 队列最大长度
+     */
+    private int queueCapacity;
+
+    /**
+     * 线程池维护线程所允许的空闲时间
+     */
+    private int keepAliveSeconds;
+
+}

+ 30 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/CacheConstants.java

@@ -0,0 +1,30 @@
+package cn.rlzc.common.core.constant;
+
+/**
+ * 缓存的key 常量
+ *
+ * @author Lion Li
+ */
+public interface CacheConstants {
+
+    /**
+     * 在线用户 redis key
+     */
+    String ONLINE_TOKEN_KEY = "online_tokens:";
+
+    /**
+     * 参数管理 cache key
+     */
+    String SYS_CONFIG_KEY = "sys_config:";
+
+    /**
+     * 字典管理 cache key
+     */
+    String SYS_DICT_KEY = "sys_dict:";
+
+    /**
+     * 登录账户密码错误次数 redis key
+     */
+    String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
+
+}

+ 88 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/CacheNames.java

@@ -0,0 +1,88 @@
+package cn.rlzc.common.core.constant;
+
+/**
+ * 缓存组名称常量
+ * <p>
+ * key 格式为 cacheNames#ttl#maxIdleTime#maxSize
+ * <p>
+ * ttl 过期时间 如果设置为0则不过期 默认为0
+ * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0
+ * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0
+ * <p>
+ * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
+ *
+ * @author Lion Li
+ */
+public interface CacheNames {
+
+    /**
+     * 演示案例
+     */
+    String DEMO_CACHE = "demo:cache#60s#10m#20";
+
+    /**
+     * 系统配置
+     */
+    String SYS_CONFIG = "sys_config";
+
+    /**
+     * 数据字典
+     */
+    String SYS_DICT = "sys_dict";
+
+    /**
+     * 数据字典类型
+     */
+    String SYS_DICT_TYPE = "sys_dict_type";
+
+    /**
+     * 租户
+     */
+    String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
+
+    /**
+     * 客户端
+     */
+    String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d";
+
+    /**
+     * 用户账户
+     */
+    String SYS_USER_NAME = "sys_user_name#30d";
+
+    /**
+     * 用户名称
+     */
+    String SYS_NICKNAME = "sys_nickname#30d";
+
+    /**
+     * 部门
+     */
+    String SYS_DEPT = "sys_dept#30d";
+
+    /**
+     * OSS内容
+     */
+    String SYS_OSS = "sys_oss#30d";
+
+    /**
+     * 角色自定义权限
+     */
+    String SYS_ROLE_CUSTOM = "sys_role_custom#30d";
+
+    /**
+     * 部门及以下权限
+     */
+    String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d";
+
+    /**
+     * OSS配置
+     */
+    String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config";
+
+    /**
+     * 在线用户
+     */
+    String ONLINE_TOKEN = "online_tokens";
+
+}

+ 76 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/Constants.java

@@ -0,0 +1,76 @@
+package cn.rlzc.common.core.constant;
+
+/**
+ * 通用常量信息
+ *
+ * @author rlzc
+ */
+public interface Constants {
+
+    /**
+     * UTF-8 字符集
+     */
+    String UTF8 = "UTF-8";
+
+    /**
+     * GBK 字符集
+     */
+    String GBK = "GBK";
+
+    /**
+     * www主域
+     */
+    String WWW = "www.";
+
+    /**
+     * http请求
+     */
+    String HTTP = "http://";
+
+    /**
+     * https请求
+     */
+    String HTTPS = "https://";
+
+    /**
+     * 通用成功标识
+     */
+    String SUCCESS = "0";
+
+    /**
+     * 通用失败标识
+     */
+    String FAIL = "1";
+
+    /**
+     * 登录成功
+     */
+    String LOGIN_SUCCESS = "Success";
+
+    /**
+     * 注销
+     */
+    String LOGOUT = "Logout";
+
+    /**
+     * 注册
+     */
+    String REGISTER = "Register";
+
+    /**
+     * 登录失败
+     */
+    String LOGIN_FAIL = "Error";
+
+    /**
+     * 验证码有效期(分钟)
+     */
+    Integer CAPTCHA_EXPIRATION = 2;
+
+    /**
+     * 顶级父级id
+     */
+    Long TOP_PARENT_ID = 0L;
+
+}
+

+ 34 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/GlobalConstants.java

@@ -0,0 +1,34 @@
+package cn.rlzc.common.core.constant;
+
+/**
+ * 全局的key常量 (业务无关的key)
+ *
+ * @author Lion Li
+ */
+public interface GlobalConstants {
+
+    /**
+     * 全局 redis key (业务无关的key)
+     */
+    String GLOBAL_REDIS_KEY = "global:";
+
+    /**
+     * 验证码 redis key
+     */
+    String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:";
+
+    /**
+     * 防重提交 redis key
+     */
+    String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:";
+
+    /**
+     * 限流 redis key
+     */
+    String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:";
+
+    /**
+     * 三方认证 redis key
+     */
+    String SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:";
+}

+ 93 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/HttpStatus.java

@@ -0,0 +1,93 @@
+package cn.rlzc.common.core.constant;
+
+/**
+ * 返回状态码
+ *
+ * @author Lion Li
+ */
+public interface HttpStatus {
+    /**
+     * 操作成功
+     */
+    int SUCCESS = 200;
+
+    /**
+     * 对象创建成功
+     */
+    int CREATED = 201;
+
+    /**
+     * 请求已经被接受
+     */
+    int ACCEPTED = 202;
+
+    /**
+     * 操作已经执行成功,但是没有返回数据
+     */
+    int NO_CONTENT = 204;
+
+    /**
+     * 资源已被移除
+     */
+    int MOVED_PERM = 301;
+
+    /**
+     * 重定向
+     */
+    int SEE_OTHER = 303;
+
+    /**
+     * 资源没有被修改
+     */
+    int NOT_MODIFIED = 304;
+
+    /**
+     * 参数列表错误(缺少,格式不匹配)
+     */
+    int BAD_REQUEST = 400;
+
+    /**
+     * 未授权
+     */
+    int UNAUTHORIZED = 401;
+
+    /**
+     * 访问受限,授权过期
+     */
+    int FORBIDDEN = 403;
+
+    /**
+     * 资源,服务未找到
+     */
+    int NOT_FOUND = 404;
+
+    /**
+     * 不允许的http方法
+     */
+    int BAD_METHOD = 405;
+
+    /**
+     * 资源冲突,或者资源被锁
+     */
+    int CONFLICT = 409;
+
+    /**
+     * 不支持的数据,媒体类型
+     */
+    int UNSUPPORTED_TYPE = 415;
+
+    /**
+     * 系统内部错误
+     */
+    int ERROR = 500;
+
+    /**
+     * 接口未实现
+     */
+    int NOT_IMPLEMENTED = 501;
+
+    /**
+     * 系统警告消息
+     */
+    int WARN = 601;
+}

+ 59 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/RegexConstants.java

@@ -0,0 +1,59 @@
+package cn.rlzc.common.core.constant;
+
+import cn.hutool.core.lang.RegexPool;
+
+/**
+ * 常用正则表达式字符串
+ * <p>
+ * 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/
+ *
+ * @author Feng
+ */
+public interface RegexConstants extends RegexPool {
+
+    /**
+     * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
+     */
+    String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$";
+
+    /**
+     * 权限标识必须符合以下格式:
+     * 1. 标准格式:xxx:yyy:zzz
+     * - 第一部分(xxx):只能包含字母、数字和下划线(_),不能使用 `*`
+     * - 第二部分(yyy):可以包含字母、数字、下划线(_)和 `*`
+     * - 第三部分(zzz):可以包含字母、数字、下划线(_)和 `*`
+     * 2. 允许空字符串(""),表示没有权限标识
+     */
+    String PERMISSION_STRING = "^$|^[a-zA-Z0-9_]+:[a-zA-Z0-9_*]+:[a-zA-Z0-9_*]+$";
+
+    /**
+     * 身份证号码(后6位)
+     */
+    String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
+
+    /**
+     * QQ号码
+     */
+    String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$";
+
+    /**
+     * 邮政编码
+     */
+    String POSTAL_CODE = "^[1-9]\\d{5}$";
+
+    /**
+     * 注册账号
+     */
+    String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$";
+
+    /**
+     * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符
+     */
+    String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$";
+
+    /**
+     * 通用状态(0表示正常,1表示停用)
+     */
+    String STATUS = "^[01]$";
+
+}

+ 80 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/SystemConstants.java

@@ -0,0 +1,80 @@
+package cn.rlzc.common.core.constant;
+
+/**
+ * 系统常量信息
+ *
+ * @author Lion Li
+ */
+public interface SystemConstants {
+
+    /**
+     * 正常状态
+     */
+    String NORMAL = "0";
+
+    /**
+     * 异常状态
+     */
+    String DISABLE = "1";
+
+    /**
+     * 是否为系统默认(是)
+     */
+    String YES = "Y";
+
+    /**
+     * 是否为系统默认(否)
+     */
+    String NO = "N";
+
+    /**
+     * 是否菜单外链(是)
+     */
+    String YES_FRAME = "0";
+
+    /**
+     * 是否菜单外链(否)
+     */
+    String NO_FRAME = "1";
+
+    /**
+     * 菜单类型(目录)
+     */
+    String TYPE_DIR = "M";
+
+    /**
+     * 菜单类型(菜单)
+     */
+    String TYPE_MENU = "C";
+
+    /**
+     * 菜单类型(按钮)
+     */
+    String TYPE_BUTTON = "F";
+
+    /**
+     * Layout组件标识
+     */
+    String LAYOUT = "Layout";
+
+    /**
+     * ParentView组件标识
+     */
+    String PARENT_VIEW = "ParentView";
+
+    /**
+     * InnerLink组件标识
+     */
+    String INNER_LINK = "InnerLink";
+
+    /**
+     * 超级管理员ID
+     */
+    Long SUPER_ADMIN_ID = 1L;
+
+    /**
+     * 根部门祖级列表
+     */
+    String ROOT_DEPT_ANCESTORS = "0";
+
+}

+ 35 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/constant/TenantConstants.java

@@ -0,0 +1,35 @@
+package cn.rlzc.common.core.constant;
+
+/**
+ * 租户常量信息
+ *
+ * @author Lion Li
+ */
+public interface TenantConstants {
+
+    /**
+     * 超级管理员ID
+     */
+    Long SUPER_ADMIN_ID = 1L;
+
+    /**
+     * 超级管理员角色 roleKey
+     */
+    String SUPER_ADMIN_ROLE_KEY = "superadmin";
+
+    /**
+     * 租户管理员角色 roleKey
+     */
+    String TENANT_ADMIN_ROLE_KEY = "admin";
+
+    /**
+     * 租户管理员角色名称
+     */
+    String TENANT_ADMIN_ROLE_NAME = "管理员";
+
+    /**
+     * 默认租户ID
+     */
+    String DEFAULT_TENANT_ID = "000000";
+
+}

+ 110 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/R.java

@@ -0,0 +1,110 @@
+package cn.rlzc.common.core.domain;
+
+import cn.rlzc.common.core.constant.HttpStatus;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 响应信息主体
+ *
+ * @author Lion Li
+ */
+@Data
+@NoArgsConstructor
+public class R<T> implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 成功
+     */
+    public static final int SUCCESS = 200;
+
+    /**
+     * 失败
+     */
+    public static final int FAIL = 500;
+
+    private int code;
+
+    private String msg;
+
+    private T data;
+
+    public static <T> R<T> ok() {
+        return restResult(null, SUCCESS, "操作成功");
+    }
+
+    public static <T> R<T> ok(T data) {
+        return restResult(data, SUCCESS, "操作成功");
+    }
+
+    public static <T> R<T> ok(String msg) {
+        return restResult(null, SUCCESS, msg);
+    }
+
+    public static <T> R<T> ok(String msg, T data) {
+        return restResult(data, SUCCESS, msg);
+    }
+
+    public static <T> R<T> fail() {
+        return restResult(null, FAIL, "操作失败");
+    }
+
+    public static <T> R<T> fail(String msg) {
+        return restResult(null, FAIL, msg);
+    }
+
+    public static <T> R<T> fail(T data) {
+        return restResult(data, FAIL, "操作失败");
+    }
+
+    public static <T> R<T> fail(String msg, T data) {
+        return restResult(data, FAIL, msg);
+    }
+
+    public static <T> R<T> fail(int code, String msg) {
+        return restResult(null, code, msg);
+    }
+
+    /**
+     * 返回警告消息
+     *
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static <T> R<T> warn(String msg) {
+        return restResult(null, HttpStatus.WARN, msg);
+    }
+
+    /**
+     * 返回警告消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static <T> R<T> warn(String msg, T data) {
+        return restResult(data, HttpStatus.WARN, msg);
+    }
+
+    private static <T> R<T> restResult(T data, int code, String msg) {
+        R<T> r = new R<>();
+        r.setCode(code);
+        r.setData(data);
+        r.setMsg(msg);
+        return r;
+    }
+
+    public static <T> Boolean isError(R<T> ret) {
+        return !isSuccess(ret);
+    }
+
+    public static <T> Boolean isSuccess(R<T> ret) {
+        return R.SUCCESS == ret.getCode();
+    }
+}

+ 71 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/CompleteTaskDTO.java

@@ -0,0 +1,71 @@
+package cn.rlzc.common.core.domain.dto;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 办理任务请求对象
+ *
+ * @author may
+ */
+@Data
+public class CompleteTaskDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 任务id
+     */
+    private Long taskId;
+
+    /**
+     * 附件id
+     */
+    private String fileId;
+
+    /**
+     * 抄送人员
+     */
+    private List<FlowCopyDTO> flowCopyList;
+
+    /**
+     * 消息类型
+     */
+    private List<String> messageType;
+
+    /**
+     * 办理意见
+     */
+    private String message;
+
+    /**
+     * 消息通知
+     */
+    private String notice;
+
+    /**
+     * 流程变量
+     */
+    private Map<String, Object> variables;
+
+    /**
+     * 扩展变量(此处为逗号分隔的ossId)
+     */
+    private String ext;
+
+    public Map<String, Object> getVariables() {
+        if (variables == null) {
+            return new HashMap<>(16);
+        }
+        variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
+        return variables;
+    }
+
+}

+ 36 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/DeptDTO.java

@@ -0,0 +1,36 @@
+package cn.rlzc.common.core.domain.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 部门
+ *
+ * @author AprilWind
+ */
+@Data
+@NoArgsConstructor
+public class DeptDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 父部门ID
+     */
+    private Long parentId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+}

+ 41 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/DictDataDTO.java

@@ -0,0 +1,41 @@
+package cn.rlzc.common.core.domain.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 字典数据DTO
+ *
+ * @author AprilWind
+ */
+@Data
+@NoArgsConstructor
+public class DictDataDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 字典标签
+     */
+    private String dictLabel;
+
+    /**
+     * 字典键值
+     */
+    private String dictValue;
+
+    /**
+     * 是否默认(Y是 N否)
+     */
+    private String isDefault;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 41 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/DictTypeDTO.java

@@ -0,0 +1,41 @@
+package cn.rlzc.common.core.domain.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 字典类型DTO
+ *
+ * @author AprilWind
+ */
+@Data
+@NoArgsConstructor
+public class DictTypeDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 字典主键
+     */
+    private Long dictId;
+
+    /**
+     * 字典名称
+     */
+    private String dictName;
+
+    /**
+     * 字典类型
+     */
+    private String dictType;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 30 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/FlowCopyDTO.java

@@ -0,0 +1,30 @@
+package cn.rlzc.common.core.domain.dto;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 抄送
+ *
+ * @author may
+ */
+@Data
+public class FlowCopyDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户id
+     */
+    private Long userId;
+
+    /**
+     * 用户名称
+     */
+    private String userName;
+
+}

+ 46 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/OssDTO.java

@@ -0,0 +1,46 @@
+package cn.rlzc.common.core.domain.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * OSS对象
+ *
+ * @author Lion Li
+ */
+@Data
+@NoArgsConstructor
+public class OssDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 对象存储主键
+     */
+    private Long ossId;
+
+    /**
+     * 文件名
+     */
+    private String fileName;
+
+    /**
+     * 原名
+     */
+    private String originalName;
+
+    /**
+     * 文件后缀名
+     */
+    private String fileSuffix;
+
+    /**
+     * URL地址
+     */
+    private String url;
+
+}

+ 46 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/PostDTO.java

@@ -0,0 +1,46 @@
+package cn.rlzc.common.core.domain.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 岗位
+ *
+ * @author AprilWind
+ */
+@Data
+@NoArgsConstructor
+public class PostDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 岗位ID
+     */
+    private Long postId;
+
+    /**
+     * 部门id
+     */
+    private Long deptId;
+
+    /**
+     * 岗位编码
+     */
+    private String postCode;
+
+    /**
+     * 岗位名称
+     */
+    private String postName;
+
+    /**
+     * 岗位类别编码
+     */
+    private String postCategory;
+
+}

+ 42 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/RoleDTO.java

@@ -0,0 +1,42 @@
+package cn.rlzc.common.core.domain.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 角色
+ *
+ * @author Lion Li
+ */
+
+@Data
+@NoArgsConstructor
+public class RoleDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+
+    /**
+     * 角色名称
+     */
+    private String roleName;
+
+    /**
+     * 角色权限
+     */
+    private String roleKey;
+
+    /**
+     * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限)
+     */
+    private String dataScope;
+
+}

+ 45 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/StartProcessDTO.java

@@ -0,0 +1,45 @@
+package cn.rlzc.common.core.domain.dto;
+
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 启动流程对象
+ *
+ * @author may
+ */
+@Data
+public class StartProcessDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 业务唯一值id
+     */
+    private String businessId;
+
+    /**
+     * 流程定义编码
+     */
+    private String flowCode;
+
+    /**
+     * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}}
+     */
+    private Map<String, Object> variables;
+
+    public Map<String, Object> getVariables() {
+        if (variables == null) {
+            return new HashMap<>(16);
+        }
+        variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
+        return variables;
+    }
+}

+ 30 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/StartProcessReturnDTO.java

@@ -0,0 +1,30 @@
+package cn.rlzc.common.core.domain.dto;
+
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 启动流程返回对象
+ *
+ * @author Lion Li
+ */
+@Data
+public class StartProcessReturnDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 流程实例id
+     */
+    private Long processInstanceId;
+
+    /**
+     * 任务id
+     */
+    private Long taskId;
+
+}

+ 101 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/TaskAssigneeDTO.java

@@ -0,0 +1,101 @@
+package cn.rlzc.common.core.domain.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 任务受让人
+ *
+ * @author AprilWind
+ */
+@Data
+@NoArgsConstructor
+public class TaskAssigneeDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 总大小
+     */
+    private Long total = 0L;
+
+    /**
+     *
+     */
+    private List<TaskHandler> list;
+
+    public TaskAssigneeDTO(Long total, List<TaskHandler> list) {
+        this.total = total;
+        this.list = list;
+    }
+
+    /**
+     * 将源列表转换为 TaskHandler 列表
+     *
+     * @param <T>              通用类型
+     * @param sourceList       待转换的源列表
+     * @param storageId        提取 storageId 的函数
+     * @param handlerCode      提取 handlerCode 的函数
+     * @param handlerName      提取 handlerName 的函数
+     * @param groupName        提取 groupName 的函数
+     * @param createTimeMapper 提取 createTime 的函数
+     * @return 转换后的 TaskHandler 列表
+     */
+    public static <T> List<TaskHandler> convertToHandlerList(
+        List<T> sourceList,
+        Function<T, Long> storageId,
+        Function<T, String> handlerCode,
+        Function<T, String> handlerName,
+        Function<T, Long> groupName,
+        Function<T, Date> createTimeMapper) {
+        return sourceList.stream()
+            .map(item -> new TaskHandler(
+                String.valueOf(storageId.apply(item)),
+                handlerCode.apply(item),
+                handlerName.apply(item),
+                groupName != null ? String.valueOf(groupName.apply(item)) : null,
+                createTimeMapper.apply(item)
+            )).collect(Collectors.toList());
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class TaskHandler {
+
+        /**
+         * 主键
+         */
+        private String storageId;
+
+        /**
+         * 权限编码
+         */
+        private String handlerCode;
+
+        /**
+         * 权限名称
+         */
+        private String handlerName;
+
+        /**
+         * 权限分组
+         */
+        private String groupName;
+
+        /**
+         * 创建时间
+         */
+        private Date createTime;
+    }
+
+}

+ 73 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/UserDTO.java

@@ -0,0 +1,73 @@
+package cn.rlzc.common.core.domain.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 用户
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@NoArgsConstructor
+public class UserDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 用户账号
+     */
+    private String userName;
+
+    /**
+     * 用户昵称
+     */
+    private String nickName;
+
+    /**
+     * 用户类型(sys_user系统用户)
+     */
+    private String userType;
+
+    /**
+     * 用户邮箱
+     */
+    private String email;
+
+    /**
+     * 手机号码
+     */
+    private String phonenumber;
+
+    /**
+     * 用户性别(0男 1女 2未知)
+     */
+    private String sex;
+
+    /**
+     * 帐号状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+}

+ 72 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/dto/UserOnlineDTO.java

@@ -0,0 +1,72 @@
+package cn.rlzc.common.core.domain.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 当前在线会话
+ *
+ * @author rlzc
+ */
+
+@Data
+@NoArgsConstructor
+public class UserOnlineDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 会话编号
+     */
+    private String tokenId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 用户名称
+     */
+    private String userName;
+
+    /**
+     * 客户端
+     */
+    private String clientKey;
+
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+
+    /**
+     * 登录IP地址
+     */
+    private String ipaddr;
+
+    /**
+     * 登录地址
+     */
+    private String loginLocation;
+
+    /**
+     * 浏览器类型
+     */
+    private String browser;
+
+    /**
+     * 操作系统
+     */
+    private String os;
+
+    /**
+     * 登录时间
+     */
+    private Long loginTime;
+
+}

+ 44 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/event/ProcessCreateTaskEvent.java

@@ -0,0 +1,44 @@
+package cn.rlzc.common.core.domain.event;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 流程创建任务监听
+ *
+ * @author may
+ */
+@Data
+public class ProcessCreateTaskEvent implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 流程定义编码
+     */
+    private String flowCode;
+
+    /**
+     * 审批节点编码
+     */
+    private String nodeCode;
+
+    /**
+     * 任务id
+     */
+    private Long taskId;
+
+    /**
+     * 业务id
+     */
+    private String businessId;
+
+}

+ 34 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/event/ProcessDeleteEvent.java

@@ -0,0 +1,34 @@
+package cn.rlzc.common.core.domain.event;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 删除流程监听
+ *
+ * @author AprilWind
+ */
+@Data
+public class ProcessDeleteEvent implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 流程定义编码
+     */
+    private String flowCode;
+
+    /**
+     * 业务id
+     */
+    private String businessId;
+
+}

+ 50 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/event/ProcessEvent.java

@@ -0,0 +1,50 @@
+package cn.rlzc.common.core.domain.event;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * 总体流程监听
+ *
+ * @author may
+ */
+@Data
+public class ProcessEvent implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 流程定义编码
+     */
+    private String flowCode;
+
+    /**
+     * 业务id
+     */
+    private String businessId;
+
+    /**
+     * 状态
+     */
+    private String status;
+
+    /**
+     * 办理参数
+     */
+    private Map<String, Object> params;
+
+    /**
+     * 当为true时为申请人节点办理
+     */
+    private boolean submit;
+
+}

+ 31 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/EmailLoginBody.java

@@ -0,0 +1,31 @@
+package cn.rlzc.common.core.domain.model;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 邮件登录对象
+ *
+ * @author Lion Li
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class EmailLoginBody extends LoginBody {
+
+    /**
+     * 邮箱
+     */
+    @NotBlank(message = "{user.email.not.blank}")
+    @Email(message = "{user.email.not.valid}")
+    private String email;
+
+    /**
+     * 邮箱code
+     */
+    @NotBlank(message = "{email.code.not.blank}")
+    private String emailCode;
+
+}

+ 48 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/LoginBody.java

@@ -0,0 +1,48 @@
+package cn.rlzc.common.core.domain.model;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 用户登录对象
+ *
+ * @author Lion Li
+ */
+
+@Data
+public class LoginBody implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 客户端id
+     */
+    @NotBlank(message = "{auth.clientid.not.blank}")
+    private String clientId;
+
+    /**
+     * 授权类型
+     */
+    @NotBlank(message = "{auth.grant.type.not.blank}")
+    private String grantType;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 验证码
+     */
+    private String code;
+
+    /**
+     * 唯一标识
+     */
+    private String uuid;
+
+}

+ 148 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/LoginUser.java

@@ -0,0 +1,148 @@
+package cn.rlzc.common.core.domain.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import cn.rlzc.common.core.domain.dto.PostDTO;
+import cn.rlzc.common.core.domain.dto.RoleDTO;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 登录用户身份权限
+ *
+ * @author Lion Li
+ */
+@Data
+@NoArgsConstructor
+public class LoginUser implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 部门类别编码
+     */
+    private String deptCategory;
+
+    /**
+     * 部门名
+     */
+    private String deptName;
+
+    /**
+     * 用户唯一标识
+     */
+    private String token;
+
+    /**
+     * 用户类型
+     */
+    private String userType;
+
+    /**
+     * 登录时间
+     */
+    private Long loginTime;
+
+    /**
+     * 过期时间
+     */
+    private Long expireTime;
+
+    /**
+     * 登录IP地址
+     */
+    private String ipaddr;
+
+    /**
+     * 登录地点
+     */
+    private String loginLocation;
+
+    /**
+     * 浏览器类型
+     */
+    private String browser;
+
+    /**
+     * 操作系统
+     */
+    private String os;
+
+    /**
+     * 菜单权限
+     */
+    private Set<String> menuPermission;
+
+    /**
+     * 角色权限
+     */
+    private Set<String> rolePermission;
+
+    /**
+     * 用户名
+     */
+    private String username;
+
+    /**
+     * 用户昵称
+     */
+    private String nickname;
+
+    /**
+     * 角色对象
+     */
+    private List<RoleDTO> roles;
+
+    /**
+     * 岗位对象
+     */
+    private List<PostDTO> posts;
+
+    /**
+     * 数据权限 当前角色ID
+     */
+    private Long roleId;
+
+    /**
+     * 客户端
+     */
+    private String clientKey;
+
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+
+    /**
+     * 获取登录id
+     */
+    public String getLoginId() {
+        if (userType == null) {
+            throw new IllegalArgumentException("用户类型不能为空");
+        }
+        if (userId == null) {
+            throw new IllegalArgumentException("用户ID不能为空");
+        }
+        return userType + ":" + userId;
+    }
+
+}

+ 31 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/PasswordLoginBody.java

@@ -0,0 +1,31 @@
+package cn.rlzc.common.core.domain.model;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.hibernate.validator.constraints.Length;
+
+/**
+ * 密码登录对象
+ *
+ * @author Lion Li
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class PasswordLoginBody extends LoginBody {
+
+    /**
+     * 用户名
+     */
+    @NotBlank(message = "{user.username.not.blank}")
+    @Length(min = 2, max = 30, message = "{user.username.length.valid}")
+    private String username;
+
+    /**
+     * 用户密码
+     */
+    @NotBlank(message = "{user.password.not.blank}")
+    @Length(min = 5, max = 30, message = "{user.password.length.valid}")
+    private String password;
+
+}

+ 33 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/RegisterBody.java

@@ -0,0 +1,33 @@
+package cn.rlzc.common.core.domain.model;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.hibernate.validator.constraints.Length;
+
+/**
+ * 用户注册对象
+ *
+ * @author Lion Li
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class RegisterBody extends LoginBody {
+
+    /**
+     * 用户名
+     */
+    @NotBlank(message = "{user.username.not.blank}")
+    @Length(min = 2, max = 20, message = "{user.username.length.valid}")
+    private String username;
+
+    /**
+     * 用户密码
+     */
+    @NotBlank(message = "{user.password.not.blank}")
+    @Length(min = 5, max = 20, message = "{user.password.length.valid}")
+    private String password;
+
+    private String userType;
+
+}

+ 29 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/SmsLoginBody.java

@@ -0,0 +1,29 @@
+package cn.rlzc.common.core.domain.model;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 短信登录对象
+ *
+ * @author Lion Li
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SmsLoginBody extends LoginBody {
+
+    /**
+     * 手机号
+     */
+    @NotBlank(message = "{user.phonenumber.not.blank}")
+    private String phonenumber;
+
+    /**
+     * 短信code
+     */
+    @NotBlank(message = "{sms.code.not.blank}")
+    private String smsCode;
+
+}

+ 35 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/SocialLoginBody.java

@@ -0,0 +1,35 @@
+package cn.rlzc.common.core.domain.model;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 三方登录对象
+ *
+ * @author Lion Li
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SocialLoginBody extends LoginBody {
+
+    /**
+     * 第三方登录平台
+     */
+    @NotBlank(message = "{social.source.not.blank}")
+    private String source;
+
+    /**
+     * 第三方登录code
+     */
+    @NotBlank(message = "{social.code.not.blank}")
+    private String socialCode;
+
+    /**
+     * 第三方登录socialState
+     */
+    @NotBlank(message = "{social.state.not.blank}")
+    private String socialState;
+
+}

+ 56 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/TaskAssigneeBody.java

@@ -0,0 +1,56 @@
+package cn.rlzc.common.core.domain.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 任务受让人
+ *
+ * @author AprilWind
+ */
+@Data
+@NoArgsConstructor
+public class TaskAssigneeBody implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 权限编码
+     */
+    private String handlerCode;
+
+    /**
+     * 权限名称
+     */
+    private String handlerName;
+
+    /**
+     * 权限分组
+     */
+    private String groupId;
+
+    /**
+     * 开始时间
+     */
+    private String beginTime;
+
+    /**
+     * 结束时间
+     */
+    private String endTime;
+
+    /**
+     * 当前页
+     */
+    private Integer pageNum = 1;
+
+    /**
+     * 每页显示条数
+     */
+    private Integer pageSize = 10;
+
+}

+ 28 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/XcxLoginBody.java

@@ -0,0 +1,28 @@
+package cn.rlzc.common.core.domain.model;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 三方登录对象
+ *
+ * @author Lion Li
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class XcxLoginBody extends LoginBody {
+
+    /**
+     * 小程序id(多个小程序时使用)
+     */
+    private String appid;
+
+    /**
+     * 小程序code
+     */
+    @NotBlank(message = "{xcx.code.not.blank}")
+    private String xcxCode;
+
+}

+ 27 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/domain/model/XcxLoginUser.java

@@ -0,0 +1,27 @@
+package cn.rlzc.common.core.domain.model;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+
+/**
+ * 小程序登录用户身份权限
+ *
+ * @author Lion Li
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+public class XcxLoginUser extends LoginUser {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * openid
+     */
+    private String openid;
+
+}

+ 215 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/enums/BusinessStatusEnum.java

@@ -0,0 +1,215 @@
+package cn.rlzc.common.core.enums;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import cn.rlzc.common.core.exception.ServiceException;
+import cn.rlzc.common.core.utils.StringUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 业务状态枚举
+ *
+ * @author may
+ */
+@Getter
+@AllArgsConstructor
+public enum BusinessStatusEnum {
+
+    /**
+     * 已撤销
+     */
+    CANCEL("cancel", "已撤销"),
+
+    /**
+     * 草稿
+     */
+    DRAFT("draft", "草稿"),
+
+    /**
+     * 待审核
+     */
+    WAITING("waiting", "待审核"),
+
+    /**
+     * 已完成
+     */
+    FINISH("finish", "已完成"),
+
+    /**
+     * 已作废
+     */
+    INVALID("invalid", "已作废"),
+
+    /**
+     * 已退回
+     */
+    BACK("back", "已退回"),
+
+    /**
+     * 已终止
+     */
+    TERMINATION("termination", "已终止");
+
+    /**
+     * 状态
+     */
+    private final String status;
+
+    /**
+     * 描述
+     */
+    private final String desc;
+
+    private static final Map<String, BusinessStatusEnum> STATUS_MAP = Arrays.stream(BusinessStatusEnum.values())
+        .collect(Collectors.toConcurrentMap(BusinessStatusEnum::getStatus, Function.identity()));
+
+    /**
+     * 根据状态获取对应的 BusinessStatusEnum 枚举
+     *
+     * @param status 业务状态码
+     * @return 对应的 BusinessStatusEnum 枚举,如果找不到则返回 null
+     */
+    public static BusinessStatusEnum getByStatus(String status) {
+        // 使用 STATUS_MAP 获取对应的枚举,若找不到则返回 null
+        return STATUS_MAP.get(status);
+    }
+
+    /**
+     * 根据状态获取对应的业务状态描述信息
+     *
+     * @param status 业务状态码
+     * @return 返回业务状态描述,若状态码为空或未找到对应的枚举,返回空字符串
+     */
+    public static String findByStatus(String status) {
+        if (StringUtils.isBlank(status)) {
+            return StrUtil.EMPTY;
+        }
+        BusinessStatusEnum statusEnum = STATUS_MAP.get(status);
+        return (statusEnum != null) ? statusEnum.getDesc() : StrUtil.EMPTY;
+    }
+
+    /**
+     * 判断是否为指定的状态之一:草稿、已撤销或已退回
+     *
+     * @param status 要检查的状态
+     * @return 如果状态为草稿、已撤销或已退回之一,则返回 true;否则返回 false
+     */
+    public static boolean isDraftOrCancelOrBack(String status) {
+        return DRAFT.status.equals(status) || CANCEL.status.equals(status) || BACK.status.equals(status);
+    }
+
+    /**
+     * 判断是否为撤销,退回,作废,终止
+     *
+     * @param status status
+     * @return 结果
+     */
+    public static boolean initialState(String status) {
+        return CANCEL.status.equals(status) || BACK.status.equals(status) || INVALID.status.equals(status) || TERMINATION.status.equals(status);
+    }
+
+    /**
+     * 获取运行中的实例状态列表
+     *
+     * @return 包含运行中实例状态的不可变列表
+     * (包含 DRAFT、WAITING、BACK 和 CANCEL 状态)
+     */
+    public static List<String> runningStatus() {
+        return Arrays.asList(DRAFT.status, WAITING.status, BACK.status, CANCEL.status);
+    }
+
+    /**
+     * 获取结束实例的状态列表
+     *
+     * @return 包含结束实例状态的不可变列表
+     * (包含 FINISH、INVALID 和 TERMINATION 状态)
+     */
+    public static List<String> finishStatus() {
+        return Arrays.asList(FINISH.status, INVALID.status, TERMINATION.status);
+    }
+
+    /**
+     * 启动流程校验
+     *
+     * @param status 状态
+     */
+    public static void checkStartStatus(String status) {
+        if (WAITING.getStatus().equals(status)) {
+            throw new ServiceException("该单据已提交过申请,正在审批中!");
+        } else if (FINISH.getStatus().equals(status)) {
+            throw new ServiceException("该单据已完成申请!");
+        } else if (INVALID.getStatus().equals(status)) {
+            throw new ServiceException("该单据已作废!");
+        } else if (TERMINATION.getStatus().equals(status)) {
+            throw new ServiceException("该单据已终止!");
+        } else if (StringUtils.isBlank(status)) {
+            throw new ServiceException("流程状态为空!");
+        }
+    }
+
+    /**
+     * 撤销流程校验
+     *
+     * @param status 状态
+     */
+    public static void checkCancelStatus(String status) {
+        if (CANCEL.getStatus().equals(status)) {
+            throw new ServiceException("该单据已撤销!");
+        } else if (FINISH.getStatus().equals(status)) {
+            throw new ServiceException("该单据已完成申请!");
+        } else if (INVALID.getStatus().equals(status)) {
+            throw new ServiceException("该单据已作废!");
+        } else if (TERMINATION.getStatus().equals(status)) {
+            throw new ServiceException("该单据已终止!");
+        } else if (BACK.getStatus().equals(status)) {
+            throw new ServiceException("该单据已退回!");
+        } else if (StringUtils.isBlank(status)) {
+            throw new ServiceException("流程状态为空!");
+        }
+    }
+
+    /**
+     * 驳回流程校验
+     *
+     * @param status 状态
+     */
+    public static void checkBackStatus(String status) {
+        if (BACK.getStatus().equals(status)) {
+            throw new ServiceException("该单据已退回!");
+        } else if (FINISH.getStatus().equals(status)) {
+            throw new ServiceException("该单据已完成申请!");
+        } else if (INVALID.getStatus().equals(status)) {
+            throw new ServiceException("该单据已作废!");
+        } else if (TERMINATION.getStatus().equals(status)) {
+            throw new ServiceException("该单据已终止!");
+        } else if (CANCEL.getStatus().equals(status)) {
+            throw new ServiceException("该单据已撤销!");
+        } else if (StringUtils.isBlank(status)) {
+            throw new ServiceException("流程状态为空!");
+        }
+    }
+
+    /**
+     * 作废,终止流程校验
+     *
+     * @param status 状态
+     */
+    public static void checkInvalidStatus(String status) {
+        if (FINISH.getStatus().equals(status)) {
+            throw new ServiceException("该单据已完成申请!");
+        } else if (INVALID.getStatus().equals(status)) {
+            throw new ServiceException("该单据已作废!");
+        } else if (TERMINATION.getStatus().equals(status)) {
+            throw new ServiceException("该单据已终止!");
+        } else if (StringUtils.isBlank(status)) {
+            throw new ServiceException("流程状态为空!");
+        }
+    }
+
+}

+ 37 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/enums/DeviceType.java

@@ -0,0 +1,37 @@
+package cn.rlzc.common.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 设备类型
+ * 针对一套 用户体系
+ *
+ * @author Lion Li
+ */
+@Getter
+@AllArgsConstructor
+public enum DeviceType {
+
+    /**
+     * pc端
+     */
+    PC("pc"),
+
+    /**
+     * app端
+     */
+    APP("app"),
+
+    /**
+     * 小程序端
+     */
+    XCX("xcx"),
+
+    /**
+     * social第三方端
+     */
+    SOCIAL("social");
+
+    private final String device;
+}

+ 146 - 0
rlzc-common/rlzc-common-core/src/main/java/cn/rlzc/common/core/enums/FormatsType.java

@@ -0,0 +1,146 @@
+package cn.rlzc.common.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import cn.rlzc.common.core.utils.StringUtils;
+
+/*
+ * 日期格式
+ * "yyyy":4位数的年份,例如:2023年表示为"2023"。
+ * "yy":2位数的年份,例如:2023年表示为"23"。
+ * "MM":2位数的月份,取值范围为01到12,例如:7月表示为"07"。
+ * "M":不带前导零的月份,取值范围为1到12,例如:7月表示为"7"。
+ * "dd":2位数的日期,取值范围为01到31,例如:22日表示为"22"。
+ * "d":不带前导零的日期,取值范围为1到31,例如:22日表示为"22"。
+ * "EEEE":星期的全名,例如:星期三表示为"Wednesday"。
+ * "E":星期的缩写,例如:星期三表示为"Wed"。
+ * "DDD" 或 "D":一年中的第几天,取值范围为001到366,例如:第200天表示为"200"。
+ * 时间格式
+ * "HH":24小时制的小时数,取值范围为00到23,例如:下午5点表示为"17"。
+ * "hh":12小时制的小时数,取值范围为01到12,例如:下午5点表示为"05"。
+ * "mm":分钟数,取值范围为00到59,例如:30分钟表示为"30"。
+ * "ss":秒数,取值范围为00到59,例如:45秒表示为"45"。
+ * "SSS":毫秒数,取值范围为000到999,例如:123毫秒表示为"123"。
+ */
+
+/**
+ * 日期格式与时间格式枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum FormatsType {
+
+    /**
+     * 例如:2023年表示为"23"
+     */
+    YY("yy"),
+
+    /**
+     * 例如:2023年表示为"2023"
+     */
+    YYYY("yyyy"),
+
+    /**
+     * 例例如,2023年7月可以表示为 "2023-07"
+     */
+    YYYY_MM("yyyy-MM"),
+
+    /**
+     * 例如,日期 "2023年7月22日" 可以表示为 "2023-07-22"
+     */
+    YYYY_MM_DD("yyyy-MM-dd"),
+
+    /**
+     * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023-07-22 15:30"
+     */
+    YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"),
+
+    /**
+     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023-07-22 15:30:45"
+     */
+    YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"),
+
+    /**
+     * 例如:下午3点30分45秒,表示为 "15:30:45"
+     */
+    HH_MM_SS("HH:mm:ss"),
+
+    /**
+     * 例例如,2023年7月可以表示为 "2023/07"
+     */
+    YYYY_MM_SLASH("yyyy/MM"),
+
+    /**
+     * 例如,日期 "2023年7月22日" 可以表示为 "2023/07/22"
+     */
+    YYYY_MM_DD_SLASH("yyyy/MM/dd"),
+
+    /**
+     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45"
+     */
+    YYYY_MM_DD_HH_MM_SLASH("yyyy/MM/dd HH:mm"),
+
+    /**
+     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45"
+     */
+    YYYY_MM_DD_HH_MM_SS_SLASH("yyyy/MM/dd HH:mm:ss"),
+
+    /**
+     * 例例如,2023年7月可以表示为 "2023.07"
+     */
+    YYYY_MM_DOT("yyyy.MM"),
+
+    /**
+     * 例如,日期 "2023年7月22日" 可以表示为 "2023.07.22"
+     */
+    YYYY_MM_DD_DOT("yyyy.MM.dd"),
+
+    /**
+     * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023.07.22 15:30"
+     */
+    YYYY_MM_DD_HH_MM_DOT("yyyy.MM.dd HH:mm"),
+
+    /**
+     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023.07.22 15:30:45"
+     */
+    YYYY_MM_DD_HH_MM_SS_DOT("yyyy.MM.dd HH:mm:ss"),
+
+    /**
+     * 例如,2023年7月可以表示为 "202307"
+     */
+    YYYYMM("yyyyMM"),
+
+    /**
+     * 例如,2023年7月22日可以表示为 "20230722"
+     */
+    YYYYMMDD("yyyyMMdd"),
+
+    /**
+     * 例如,2023年7月22日下午3点可以表示为 "2023072215"
+     */
+    YYYYMMDDHH("yyyyMMddHH"),
+
+    /**
+     * 例如,2023年7月22日下午3点30分可以表示为 "202307221530"
+     */
+    YYYYMMDDHHMM("yyyyMMddHHmm"),
+
+    /**
+     * 例如,2023年7月22日下午3点30分45秒可以表示为 "20230722153045"
+     */
+    YYYYMMDDHHMMSS("yyyyMMddHHmmss");
+
+    /**
+     * 时间格式
+     */
+    private final String timeFormat;
+
+    public static FormatsType getFormatsType(String str) {
+        for (FormatsType value : values()) {
+            if (StringUtils.contains(str, value.getTimeFormat())) {
+                return value;
+            }
+        }
+        throw new RuntimeException("'FormatsType' not found By " + str);
+    }
+}

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff