xusl 1 سال پیش
کامیت
2be7077787
100فایلهای تغییر یافته به همراه8491 افزوده شده و 0 حذف شده
  1. 1 0
      .idea/.name
  2. 11 0
      .idea/aws.xml
  3. 21 0
      .idea/compiler.xml
  4. 10 0
      .idea/encodings.xml
  5. 36 0
      .idea/inspectionProfiles/Project_Default.xml
  6. 25 0
      .idea/jarRepositories.xml
  7. 12 0
      .idea/misc.xml
  8. 8 0
      .idea/modules.xml
  9. 10 0
      .idea/runConfigurations.xml
  10. 124 0
      .idea/uiDesigner.xml
  11. 6 0
      .idea/vcs.xml
  12. 207 0
      .idea/workspace.xml
  13. 379 0
      backend/pom.xml
  14. 22 0
      backend/src/main/java/com/jiayue/ssi/SsiApplication.java
  15. 12 0
      backend/src/main/java/com/jiayue/ssi/annotation/AgainVerify.java
  16. 93 0
      backend/src/main/java/com/jiayue/ssi/aspectj/AgainVerifyAspect.java
  17. 38 0
      backend/src/main/java/com/jiayue/ssi/backenum/ApproveOperaterEnum.java
  18. 26 0
      backend/src/main/java/com/jiayue/ssi/backenum/ApproveResultEnum.java
  19. 26 0
      backend/src/main/java/com/jiayue/ssi/backenum/ApproveStatusEnum.java
  20. 48 0
      backend/src/main/java/com/jiayue/ssi/backenum/AuditAblesEventEnum.java
  21. 19 0
      backend/src/main/java/com/jiayue/ssi/backenum/AuditType.java
  22. 20 0
      backend/src/main/java/com/jiayue/ssi/backenum/BusinessStatus.java
  23. 91 0
      backend/src/main/java/com/jiayue/ssi/backenum/BusinessType.java
  24. 37 0
      backend/src/main/java/com/jiayue/ssi/backenum/HttpMethod.java
  25. 24 0
      backend/src/main/java/com/jiayue/ssi/backenum/OperatorType.java
  26. 43 0
      backend/src/main/java/com/jiayue/ssi/backenum/PasswordRuleEnum.java
  27. 25 0
      backend/src/main/java/com/jiayue/ssi/backenum/ResponseEnum.java
  28. 85 0
      backend/src/main/java/com/jiayue/ssi/config/CaptchaConfig.java
  29. 57 0
      backend/src/main/java/com/jiayue/ssi/config/EnvironmentPreparedListener.java
  30. 29 0
      backend/src/main/java/com/jiayue/ssi/config/GlobalExceptionAdvice.java
  31. 35 0
      backend/src/main/java/com/jiayue/ssi/config/MybatisPlusConfig.java
  32. 119 0
      backend/src/main/java/com/jiayue/ssi/config/SaTokenConfiguration.java
  33. 13 0
      backend/src/main/java/com/jiayue/ssi/constant/ApproveConstants.java
  34. 96 0
      backend/src/main/java/com/jiayue/ssi/constant/CacheConstants.java
  35. 146 0
      backend/src/main/java/com/jiayue/ssi/constant/Constants.java
  36. 22 0
      backend/src/main/java/com/jiayue/ssi/constant/CustomException.java
  37. 27 0
      backend/src/main/java/com/jiayue/ssi/constant/LoginConstants.java
  38. 37 0
      backend/src/main/java/com/jiayue/ssi/constant/PermissionCharacter.java
  39. 27 0
      backend/src/main/java/com/jiayue/ssi/constant/PermissionContextHolder.java
  40. 24 0
      backend/src/main/java/com/jiayue/ssi/constant/SecretKeyConstants.java
  41. 78 0
      backend/src/main/java/com/jiayue/ssi/constant/UserConstants.java
  42. 115 0
      backend/src/main/java/com/jiayue/ssi/controller/ElectricFieldController.java
  43. 275 0
      backend/src/main/java/com/jiayue/ssi/controller/SysApproveController.java
  44. 205 0
      backend/src/main/java/com/jiayue/ssi/controller/SysLogininforController.java
  45. 333 0
      backend/src/main/java/com/jiayue/ssi/controller/SysMenuController.java
  46. 260 0
      backend/src/main/java/com/jiayue/ssi/controller/SysOperlogController.java
  47. 233 0
      backend/src/main/java/com/jiayue/ssi/controller/SysParameterController.java
  48. 75 0
      backend/src/main/java/com/jiayue/ssi/controller/SysPolicyController.java
  49. 293 0
      backend/src/main/java/com/jiayue/ssi/controller/SysRoleController.java
  50. 512 0
      backend/src/main/java/com/jiayue/ssi/controller/SysUserController.java
  51. 259 0
      backend/src/main/java/com/jiayue/ssi/controller/UserLoginController.java
  52. 19 0
      backend/src/main/java/com/jiayue/ssi/dto/ActiveUserDto.java
  53. 25 0
      backend/src/main/java/com/jiayue/ssi/dto/UserVisitInfoDto.java
  54. 47 0
      backend/src/main/java/com/jiayue/ssi/entity/BaseEntity.java
  55. 62 0
      backend/src/main/java/com/jiayue/ssi/entity/ElectricField.java
  56. 243 0
      backend/src/main/java/com/jiayue/ssi/entity/Server.java
  57. 94 0
      backend/src/main/java/com/jiayue/ssi/entity/SysApprove.java
  58. 136 0
      backend/src/main/java/com/jiayue/ssi/entity/SysLogininfor.java
  59. 295 0
      backend/src/main/java/com/jiayue/ssi/entity/SysMenu.java
  60. 276 0
      backend/src/main/java/com/jiayue/ssi/entity/SysOperLog.java
  61. 41 0
      backend/src/main/java/com/jiayue/ssi/entity/SysParameter.java
  62. 93 0
      backend/src/main/java/com/jiayue/ssi/entity/SysPolicy.java
  63. 52 0
      backend/src/main/java/com/jiayue/ssi/entity/SysRole.java
  64. 48 0
      backend/src/main/java/com/jiayue/ssi/entity/SysRoleMenu.java
  65. 101 0
      backend/src/main/java/com/jiayue/ssi/entity/SysUser.java
  66. 45 0
      backend/src/main/java/com/jiayue/ssi/entity/SysUserRole.java
  67. 76 0
      backend/src/main/java/com/jiayue/ssi/entity/TreeSelect.java
  68. 102 0
      backend/src/main/java/com/jiayue/ssi/entity/server/Cpu.java
  69. 132 0
      backend/src/main/java/com/jiayue/ssi/entity/server/Jvm.java
  70. 62 0
      backend/src/main/java/com/jiayue/ssi/entity/server/Mem.java
  71. 84 0
      backend/src/main/java/com/jiayue/ssi/entity/server/Sys.java
  72. 114 0
      backend/src/main/java/com/jiayue/ssi/entity/server/SysFile.java
  73. 106 0
      backend/src/main/java/com/jiayue/ssi/entity/vo/MetaVo.java
  74. 149 0
      backend/src/main/java/com/jiayue/ssi/entity/vo/RouterVo.java
  75. 105 0
      backend/src/main/java/com/jiayue/ssi/factory/LoginFactory.java
  76. 26 0
      backend/src/main/java/com/jiayue/ssi/factory/OperateLogFactory.java
  77. 51 0
      backend/src/main/java/com/jiayue/ssi/filter/InterfaceLimitFilter.java
  78. 43 0
      backend/src/main/java/com/jiayue/ssi/handler/MyMetaObjectHandler.java
  79. 16 0
      backend/src/main/java/com/jiayue/ssi/mapper/ElectricFieldMapper.java
  80. 16 0
      backend/src/main/java/com/jiayue/ssi/mapper/SysApproveMapper.java
  81. 47 0
      backend/src/main/java/com/jiayue/ssi/mapper/SysLogininforMapper.java
  82. 130 0
      backend/src/main/java/com/jiayue/ssi/mapper/SysMenuMapper.java
  83. 51 0
      backend/src/main/java/com/jiayue/ssi/mapper/SysOperLogMapper.java
  84. 17 0
      backend/src/main/java/com/jiayue/ssi/mapper/SysParameterMapper.java
  85. 16 0
      backend/src/main/java/com/jiayue/ssi/mapper/SysPolicyMapper.java
  86. 123 0
      backend/src/main/java/com/jiayue/ssi/mapper/SysRoleMapper.java
  87. 48 0
      backend/src/main/java/com/jiayue/ssi/mapper/SysRoleMenuMapper.java
  88. 48 0
      backend/src/main/java/com/jiayue/ssi/mapper/SysUserMapper.java
  89. 65 0
      backend/src/main/java/com/jiayue/ssi/mapper/SysUserRoleMapper.java
  90. 14 0
      backend/src/main/java/com/jiayue/ssi/service/ElectricFieldService.java
  91. 20 0
      backend/src/main/java/com/jiayue/ssi/service/SysApproveService.java
  92. 35 0
      backend/src/main/java/com/jiayue/ssi/service/SysLogininforService.java
  93. 122 0
      backend/src/main/java/com/jiayue/ssi/service/SysMenuService.java
  94. 35 0
      backend/src/main/java/com/jiayue/ssi/service/SysOperLogService.java
  95. 21 0
      backend/src/main/java/com/jiayue/ssi/service/SysParameterService.java
  96. 14 0
      backend/src/main/java/com/jiayue/ssi/service/SysPolicyService.java
  97. 179 0
      backend/src/main/java/com/jiayue/ssi/service/SysRoleService.java
  98. 13 0
      backend/src/main/java/com/jiayue/ssi/service/SysUserRoleService.java
  99. 96 0
      backend/src/main/java/com/jiayue/ssi/service/SysUserService.java
  100. 9 0
      backend/src/main/java/com/jiayue/ssi/service/UmsAdminService.java

+ 1 - 0
.idea/.name

@@ -0,0 +1 @@
+pfr

+ 11 - 0
.idea/aws.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="accountSettings">
+    <option name="activeRegion" value="us-east-1" />
+    <option name="recentlyUsedRegions">
+      <list>
+        <option value="us-east-1" />
+      </list>
+    </option>
+  </component>
+</project>

+ 21 - 0
.idea/compiler.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <annotationProcessing>
+      <profile default="true" name="Default" enabled="true" />
+      <profile name="Maven default annotation processors profile" enabled="true">
+        <sourceOutputDir name="target/generated-sources/annotations" />
+        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
+        <outputRelativeToContentRoot value="true" />
+        <module name="ui" />
+        <module name="backend" />
+      </profile>
+    </annotationProcessing>
+  </component>
+  <component name="JavacSettings">
+    <option name="ADDITIONAL_OPTIONS_OVERRIDE">
+      <module name="backend" options="-parameters" />
+      <module name="pfr" options="-parameters" />
+    </option>
+  </component>
+</project>

+ 10 - 0
.idea/encodings.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding">
+    <file url="file://$PROJECT_DIR$/backend/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/ui/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/ui/src/main/resources" charset="UTF-8" />
+  </component>
+</project>

+ 36 - 0
.idea/inspectionProfiles/Project_Default.xml

@@ -0,0 +1,36 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="TOP_LEVEL_CLASS_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="" />
+        </value>
+      </option>
+      <option name="INNER_CLASS_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="" />
+        </value>
+      </option>
+      <option name="METHOD_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
+        </value>
+      </option>
+      <option name="FIELD_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="" />
+        </value>
+      </option>
+      <option name="IGNORE_DEPRECATED" value="false" />
+      <option name="IGNORE_JAVADOC_PERIOD" value="true" />
+      <option name="IGNORE_DUPLICATED_THROWS" value="false" />
+      <option name="IGNORE_POINT_TO_ITSELF" value="false" />
+      <option name="myAdditionalJavadocTags" value="date" />
+    </inspection_tool>
+  </profile>
+</component>

+ 25 - 0
.idea/jarRepositories.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RemoteRepositoriesConfiguration">
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Central Repository" />
+      <option name="url" value="https://repo.maven.apache.org/maven2" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Central Repository" />
+      <option name="url" value="http://49.4.68.219:8888/repository/jiayue-group/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Maven Central repository" />
+      <option name="url" value="https://repo1.maven.org/maven2" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="jboss.community" />
+      <option name="name" value="JBoss Community repository" />
+      <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
+    </remote-repository>
+  </component>
+</project>

+ 12 - 0
.idea/misc.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="MavenProjectsManager">
+    <option name="originalFiles">
+      <list>
+        <option value="$PROJECT_DIR$/pom.xml" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/ui/ui.iml" filepath="$PROJECT_DIR$/ui/ui.iml" />
+    </modules>
+  </component>
+</project>

+ 10 - 0
.idea/runConfigurations.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RunConfigurationProducerService">
+    <option name="ignoredProducers">
+      <set>
+        <option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
+      </set>
+    </option>
+  </component>
+</project>

+ 124 - 0
.idea/uiDesigner.xml

@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Palette2">
+    <group name="Swing">
+      <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+      </item>
+      <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
+        <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+        <initial-values>
+          <property name="text" value="Button" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="RadioButton" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="CheckBox" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="Label" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+          <preferred-size width="-1" height="20" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+      </item>
+    </group>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 207 - 0
.idea/workspace.xml

@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="AutoImportSettings">
+    <option name="autoReloadType" value="SELECTIVE" />
+  </component>
+  <component name="ChangeListManager">
+    <list default="true" id="326a92dc-2aca-48b4-b27d-074155546838" name="Changes" comment="初始化" />
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="FileTemplateManagerImpl">
+    <option name="RECENT_TEMPLATES">
+      <list>
+        <option value="Interface" />
+        <option value="Class" />
+      </list>
+    </option>
+  </component>
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
+  </component>
+  <component name="HighlightingSettingsPerFile">
+    <setting file="jar://$MAVEN_REPOSITORY$/cn/dev33/sa-token-core/1.30.0/sa-token-core-1.30.0-sources.jar!/cn/dev33/satoken/stp/StpLogic.java" root0="FORCE_HIGHLIGHTING" />
+  </component>
+  <component name="MarkdownSettingsMigration">
+    <option name="stateVersion" value="1" />
+  </component>
+  <component name="MavenImportPreferences">
+    <option name="generalSettings">
+      <MavenGeneralSettings>
+        <option name="localRepository" value="C:\Users\jy\.m2\repository" />
+        <option name="mavenHome" value="$PROJECT_DIR$/../../tools/apache-maven-3.5.3" />
+        <option name="userSettingsFile" value="C:\Users\jy\.m2\v3-sertting\settings.xml" />
+      </MavenGeneralSettings>
+    </option>
+  </component>
+  <component name="ProjectColorInfo">{
+  &quot;associatedIndex&quot;: 2
+}</component>
+  <component name="ProjectId" id="2WbdHaFF9nWPVEyYgtOwFr1iph9" />
+  <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
+  <component name="ProjectViewState">
+    <option name="hideEmptyMiddlePackages" value="true" />
+    <option name="showLibraryContents" value="true" />
+  </component>
+  <component name="PropertiesComponent"><![CDATA[{
+  "keyToString": {
+    "RequestMappingsPanelOrder0": "0",
+    "RequestMappingsPanelOrder1": "1",
+    "RequestMappingsPanelWidth0": "75",
+    "RequestMappingsPanelWidth1": "75",
+    "RunOnceActivity.OpenProjectViewOnStart": "true",
+    "RunOnceActivity.ShowReadmeOnStart": "true",
+    "WebServerToolWindowFactoryState": "false",
+    "git-widget-placeholder": "master",
+    "last_opened_file_path": "D:/code/pfr",
+    "node.js.detected.package.eslint": "true",
+    "node.js.detected.package.tslint": "true",
+    "node.js.selected.package.eslint": "(autodetect)",
+    "node.js.selected.package.tslint": "(autodetect)",
+    "nodejs_package_manager_path": "npm",
+    "project.structure.last.edited": "Project",
+    "project.structure.proportion": "0.15",
+    "project.structure.side.proportion": "0.2",
+    "settings.editor.selected.configurable": "vcs.Git",
+    "ts.external.directory.path": "D:\\idea2023\\plugins\\javascript-impl\\jsLanguageServicesImpl\\external",
+    "vue.rearranger.settings.migration": "true"
+  }
+}]]></component>
+  <component name="RecentsManager">
+    <key name="CopyFile.RECENT_KEYS">
+      <recent name="D:\test\ssi-satoken\ui\src\assets\images\bg" />
+      <recent name="D:\test\ssi-satoken\ui\src\assets" />
+      <recent name="D:\test\ssi-satoken\ui\src\views\largeScreen" />
+    </key>
+    <key name="CopyClassDialog.RECENTS_KEY">
+      <recent name="com.jiayue.ssi.aspectj" />
+      <recent name="com.jiayue.ssi.annotation" />
+      <recent name="com.jiayue.ssi.filter" />
+    </key>
+  </component>
+  <component name="RunManager" selected="npm.run dev">
+    <configuration name="AesGen" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
+      <option name="MAIN_CLASS_NAME" value="com.jiayue.ssi.AesGen" />
+      <module name="backend" />
+      <extension name="coverage">
+        <pattern>
+          <option name="PATTERN" value="com.jiayue.ssi.*" />
+          <option name="ENABLED" value="true" />
+        </pattern>
+      </extension>
+      <method v="2">
+        <option name="Make" enabled="true" />
+      </method>
+    </configuration>
+    <configuration name="SsiApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
+      <module name="backend" />
+      <option name="SPRING_BOOT_MAIN_CLASS" value="com.jiayue.ssi.SsiApplication" />
+      <method v="2">
+        <option name="Make" enabled="true" />
+      </method>
+    </configuration>
+    <configuration name="run dev" type="js.build_tools.npm">
+      <package-json value="$PROJECT_DIR$/ui/package.json" />
+      <command value="run" />
+      <scripts>
+        <script value="dev" />
+      </scripts>
+      <node-interpreter value="project" />
+      <envs />
+      <method v="2" />
+    </configuration>
+    <list>
+      <item itemvalue="Application.AesGen" />
+      <item itemvalue="npm.run dev" />
+      <item itemvalue="Spring Boot.SsiApplication" />
+    </list>
+    <recent_temporary>
+      <list>
+        <item itemvalue="Application.AesGen" />
+      </list>
+    </recent_temporary>
+  </component>
+  <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="326a92dc-2aca-48b4-b27d-074155546838" name="Changes" comment="" />
+      <created>1697005127649</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1697005127649</updated>
+      <workItem from="1697005128905" duration="8731000" />
+      <workItem from="1697081152019" duration="15601000" />
+      <workItem from="1697174146119" duration="12477000" />
+      <workItem from="1697422534081" duration="17304000" />
+      <workItem from="1697506391049" duration="18567000" />
+      <workItem from="1697591005268" duration="22485000" />
+      <workItem from="1697678407472" duration="16001000" />
+      <workItem from="1697772488159" duration="10635000" />
+      <workItem from="1698026534160" duration="136000" />
+      <workItem from="1698027407958" duration="382000" />
+    </task>
+    <servers />
+  </component>
+  <component name="TypeScriptGeneratedFilesManager">
+    <option name="version" value="3" />
+  </component>
+  <component name="Vcs.Log.Tabs.Properties">
+    <option name="TAB_STATES">
+      <map>
+        <entry key="MAIN">
+          <value>
+            <State />
+          </value>
+        </entry>
+      </map>
+    </option>
+  </component>
+  <component name="VcsManagerConfiguration">
+    <ignored-roots>
+      <path value="$PROJECT_DIR$" />
+    </ignored-roots>
+  </component>
+  <component name="XDebuggerManager">
+    <breakpoint-manager>
+      <breakpoints>
+        <line-breakpoint enabled="true" type="java-line">
+          <url>jar://$MAVEN_REPOSITORY$/cn/dev33/sa-token-core/1.30.0/sa-token-core-1.30.0-sources.jar!/cn/dev33/satoken/stp/StpLogic.java</url>
+          <line>280</line>
+          <option name="timeStamp" value="17" />
+        </line-breakpoint>
+        <line-breakpoint enabled="true" type="java-line">
+          <url>jar://$MAVEN_REPOSITORY$/cn/dev33/sa-token-core/1.30.0/sa-token-core-1.30.0-sources.jar!/cn/dev33/satoken/stp/StpLogic.java</url>
+          <line>306</line>
+          <option name="timeStamp" value="18" />
+        </line-breakpoint>
+        <line-breakpoint enabled="true" type="java-line">
+          <url>file://$PROJECT_DIR$/backend/src/main/java/com/jiayue/ssi/controller/SysMenuController.java</url>
+          <line>282</line>
+          <option name="timeStamp" value="25" />
+        </line-breakpoint>
+      </breakpoints>
+    </breakpoint-manager>
+    <watches-manager>
+      <configuration name="SpringBootApplicationConfigurationType">
+        <watch expression="StpUtil" />
+        <watch expression="saTokenInfo" />
+        <watch expression="tokenMap" />
+        <watch expression="saTokenInfo.getTokenValue()" />
+        <watch expression="sysUser.getId()" />
+        <watch expression="splicingKeyJustCreatedSave()" />
+        <watch expression="storage" />
+        <watch expression="sysMenuService.checkMenuNameUnique(menu)" />
+        <watch expression="menu.getMenuName()" />
+        <watch expression="menu.getParentId()" />
+        <watch expression="info" />
+        <watch expression="role.getRemark()" />
+        <watch expression="pjp" />
+        <watch expression="request" />
+        <watch expression="againVerify" />
+        <watch expression="paramMap" />
+      </configuration>
+    </watches-manager>
+  </component>
+</project>

+ 379 - 0
backend/pom.xml

@@ -0,0 +1,379 @@
+<?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>pfr</artifactId>
+        <groupId>com.jiayue.ssi</groupId>
+        <version>1.0.0</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>backend</artifactId>
+    <version>1.0.0</version>
+
+    <name>backend</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <java.version>1.8</java.version>
+        <spring-boot.version>2.2.1.RELEASE</spring-boot.version>
+        <spring-platform.version>Cairo-SR8</spring-platform.version>
+        <spring-boot-admin.version>2.2.0</spring-boot-admin.version>
+        <jjwt.version>0.9.0</jjwt.version>
+        <javajwt.version>3.4.0</javajwt.version>
+        <slf4j.version>1.7.26</slf4j.version>
+        <commons-lang3.version>3.9</commons-lang3.version>
+        <byte-buddy.version>1.8.17</byte-buddy.version>
+        <jackson-databind.version>2.9.8</jackson-databind.version>
+        <!--<mysql.connector.version>5.1.46</mysql.connector.version>-->
+        <mysql.connector.version>8.0.18</mysql.connector.version>
+        <hibernate.version>6.0.14.Final</hibernate.version>
+        <jasypt-boot.version>3.0.3</jasypt-boot.version>
+        <commons-pool2.version>2.7.0</commons-pool2.version>
+        <jedis.version>3.1.0</jedis.version>
+        <quartz.version>2.3.1</quartz.version>
+        <hutool.version>5.8.12</hutool.version>
+        <knife4j.version>2.0.1</knife4j.version>
+        <ttl.version>2.11.2</ttl.version>
+        <swagger.core.version>1.5.22</swagger.core.version>
+        <mybatis-plus-generator.version>3.1.1</mybatis-plus-generator.version>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+            <version>1.68</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-dependencies</artifactId>
+            <version>${spring-boot.version}</version>
+            <type>pom</type>
+        </dependency>
+        <!--web 模块-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <version>${spring-boot.version}</version>
+            <exclusions>
+                <!--排除tomcat依赖-->
+                <exclusion>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                    <groupId>org.springframework.boot</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-undertow</artifactId>
+        </dependency>
+        <!--mysql 驱动-->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>${mysql.connector.version}</version>
+        </dependency>
+        <!--swagger 最新依赖内置版本-->
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+            <version>${swagger.core.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>${swagger.core.version}</version>
+        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>io.jsonwebtoken</groupId>-->
+<!--            <artifactId>jjwt</artifactId>-->
+<!--            <version>${jjwt.version}</version>-->
+<!--        </dependency>-->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.18</version>
+        </dependency>
+        <!-- Quartz定时任务 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-quartz</artifactId>
+        </dependency>
+        <!-- MybatisPlus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.4.1</version>
+        </dependency>
+        <!-- 验证码 -->
+        <dependency>
+            <groupId>com.github.penggle</groupId>
+            <artifactId>kaptcha</artifactId>
+            <version>2.3.2</version>
+        </dependency>
+        <!-- druid 连接池 -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+            <version>1.1.22</version>
+        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.boot</groupId>-->
+<!--            <artifactId>spring-boot-starter-security</artifactId>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-spring-boot-starter</artifactId>
+            <version>1.36.0</version>
+        </dependency>
+        <!-- Sa-Token 整合 jwt -->
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-jwt</artifactId>
+            <version>1.36.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.10.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+            <version>1.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.12.0</version>
+        </dependency>
+        <!--配置文件处理器-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+<!--        &lt;!&ndash;jasypt配置文件加解密&ndash;&gt;-->
+<!--        <dependency>-->
+<!--            <groupId>com.github.ulisesbocchio</groupId>-->
+<!--            <artifactId>jasypt-spring-boot-starter</artifactId>-->
+<!--            <version>${jasypt-boot.version}</version>-->
+<!--        </dependency>-->
+        <dependency>
+            <groupId>com.github.whvcse</groupId>
+            <artifactId>easy-captcha</artifactId>
+            <version>1.6.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.6</version>
+        </dependency>
+        <!-- AOP依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15to18</artifactId>
+            <version>1.72</version>
+        </dependency>
+        <!-- 获取系统信息 -->
+        <dependency>
+            <groupId>com.github.oshi</groupId>
+            <artifactId>oshi-core</artifactId>
+            <version>6.4.0</version>
+        </dependency>
+        <dependency>
+            <groupId>net.java.dev.jna</groupId>
+            <artifactId>jna</artifactId>
+            <version>5.13.0</version>
+        </dependency>
+        <dependency>
+            <groupId>net.java.dev.jna</groupId>
+            <artifactId>jna-platform</artifactId>
+            <version>5.13.0</version>
+        </dependency>
+        <!-- 解析客户端操作系统、浏览器等 -->
+        <dependency>
+            <groupId>eu.bitwalker</groupId>
+            <artifactId>UserAgentUtils</artifactId>
+            <version>1.21</version>
+        </dependency>
+        <!-- 阿里JSON解析器 -->
+        <dependency>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2</artifactId>
+            <version>2.0.23</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>ssi</finalName>
+        <plugins>
+            <!--跳过test类-->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.21.0</version>
+                <configuration>
+                    <skipTests>true</skipTests>
+                </configuration>
+            </plugin>
+            <!-- 插件maven-clean-plugin,用于在编译前,清除之前编译的文件、文件夹等,避免残留之前的内容 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-clean-plugin</artifactId>
+                <version>3.1.0</version>
+                <configuration>
+                    <filesets>
+                        <fileset>
+                            <!-- 前端资源目录,即:存放前端包目录-->
+                            <directory>src/main/resources/static</directory>
+                        </fileset>
+                        <fileset>
+                            <!-- Vue项目打包自动生成的dist目录 -->
+                            <directory>../ui/dist</directory>
+                        </fileset>
+                    </filesets>
+                </configuration>
+            </plugin>
+
+            <!--frontend-maven-plugin为项目本地下载/安装Node和NPM,运行npm install命令-->
+            <plugin>
+                <groupId>com.github.eirslett</groupId>
+                <artifactId>frontend-maven-plugin</artifactId>
+                <version>1.6</version>
+                <configuration>
+                    <workingDirectory>../ui</workingDirectory>
+                    <nodeVersion>v16.13.1</nodeVersion>
+                    <npmVersion>8.1.2</npmVersion>
+                    <nodeDownloadRoot>https://npm.taobao.org/mirrors/node/</nodeDownloadRoot>
+                    <npmDownloadRoot>https://registry.npm.taobao.org/npm/-/</npmDownloadRoot>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>install node and npm</id>
+                        <goals>
+                            <goal>install-node-and-npm</goal>
+                        </goals>
+                    </execution>
+                    <!-- Install all project dependencies -->
+                    <execution>
+                        <id>npm install</id>
+                        <goals>
+                            <goal>npm</goal>
+                        </goals>
+                        <phase>generate-resources</phase>
+                        <configuration>
+                            <arguments>install</arguments>
+                        </configuration>
+                    </execution>
+                    <!-- Build and minify static files -->
+                    <execution>
+                        <id>npm run build</id>
+                        <goals>
+                            <goal>npm</goal>
+                        </goals>
+                        <configuration>
+                            <arguments>run build:prod</arguments>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!--资源插件,主要为了从前端项目里复制打包好的文件到springboot项目-->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>3.1.0</version>
+                <executions>
+                    <execution>
+                        <id>copy static</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <!-- 复制前端打包文件到这里 -->
+                            <outputDirectory>src/main/resources/static</outputDirectory>
+                            <overwrite>true</overwrite>
+                            <resources>
+                                <resource>
+                                    <!-- 从前端打包的目录dist进行指定文件、文件夹内容的复制-->
+                                    <directory>${project.parent.basedir}/ui/dist</directory>
+                                    <includes>
+                                        <!-- 具体根据实际前端代码、及目录结构进行配置-->
+                                        <include>assets/</include>
+                                        <include>favicon.ico</include>
+                                        <include>index.html</include>
+                                    </includes>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.1.7.RELEASE</version>
+                <configuration>
+                    <!--控制是否启用支持Linux下service方式运行-->
+                    <executable>true</executable>
+                    <!--打包后自动运行程序-->
+<!--                    <includeSystemScope>true</includeSystemScope>-->
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>build-info</goal>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <!--上传配置 必须 -->
+    <distributionManagement>
+        <repository>
+            <id>jiayue-releases</id>
+            <name>Nexus Release Repository</name>
+            <url>http://49.4.68.219:8888/repository/jiayue-releases/</url>
+        </repository>
+        <snapshotRepository>
+            <id>jiayue-snapshots</id>
+            <name>Nexus Snapshot Repository</name>
+            <url>http://49.4.68.219:8888/repository/jiayue-snapshots/</url>
+        </snapshotRepository>
+    </distributionManagement>
+</project>

+ 22 - 0
backend/src/main/java/com/jiayue/ssi/SsiApplication.java

@@ -0,0 +1,22 @@
+package com.jiayue.ssi;
+
+//import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+
+/**
+ * TODO
+ *
+ * @author xsl
+ * @version 3.0
+ */
+@SpringBootApplication
+@MapperScan("com.jiayue.ssi.mapper")
+//@EnableEncryptableProperties
+public class SsiApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(SsiApplication.class, args);
+    }
+}

+ 12 - 0
backend/src/main/java/com/jiayue/ssi/annotation/AgainVerify.java

@@ -0,0 +1,12 @@
+package com.jiayue.ssi.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 重新鉴别
+ */
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AgainVerify {
+}

+ 93 - 0
backend/src/main/java/com/jiayue/ssi/aspectj/AgainVerifyAspect.java

@@ -0,0 +1,93 @@
+package com.jiayue.ssi.aspectj;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.crypto.SmUtil;
+import com.jiayue.ssi.annotation.AgainVerify;
+import com.jiayue.ssi.constant.SecretKeyConstants;
+import com.jiayue.ssi.entity.SysUser;
+import com.jiayue.ssi.service.SysUserService;
+import com.jiayue.ssi.util.ResponseVO;
+import com.jiayue.ssi.util.SM2CryptUtils;
+import com.jiayue.ssi.util.SaResultRefit;
+import com.jiayue.ssi.util.SecurityContextUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+* 重新鉴别
+*
+* @author xsl
+* @since 2023/04/06
+*/
+@Aspect
+@Component
+@Slf4j
+@Order(3)
+public class AgainVerifyAspect {
+    @Autowired
+    SysUserService sysUserService;
+    /**
+     * 层切点
+     */
+    @Pointcut("@annotation(againVerify)")
+    public void controllerAspect(AgainVerify againVerify) {
+    }
+
+    @Around("controllerAspect(againVerify)")
+    public SaResultRefit doAround(ProceedingJoinPoint pjp, AgainVerify againVerify) throws Throwable {
+        // 获得request对象
+        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
+        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
+        HttpServletRequest request = sra.getRequest();
+        String againPwd = request.getParameter("againPwd");
+        String decryptPassword = null;
+        try {
+            decryptPassword= SmUtil.sm3(againPwd).toUpperCase();
+        } catch (Exception e) {
+            return SaResultRefit.errorTips("鉴别失败,不能操作");
+        }
+
+        // 先检测存储密码的完整性
+        String dbpwd = "";
+        SysUser sysUser = sysUserService.getById(Long.parseLong(StpUtil.getLoginId().toString()));
+        // 权限集合
+        sysUser.setMailbox(SM2CryptUtils.decrypt(sysUser.getMailbox(), SecretKeyConstants.SERVER_PRIVATE_KEY));
+        sysUser.setPhonenumber(SM2CryptUtils.decrypt(sysUser.getPhonenumber(), SecretKeyConstants.SERVER_PRIVATE_KEY));
+        sysUser.setNickname(SM2CryptUtils.decrypt(sysUser.getNickname(), SecretKeyConstants.SERVER_PRIVATE_KEY));
+        sysUser.setIdcard(SM2CryptUtils.decrypt(sysUser.getIdcard(), SecretKeyConstants.SERVER_PRIVATE_KEY));
+        try {
+            dbpwd = SM2CryptUtils.decrypt(sysUser.getPassword(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+        }
+        catch (Exception e1){
+            log.error("账号:"+sysUser.getUsername()+",数据库密码被破坏!");
+            return SaResultRefit.errorTips("鉴别失败,不能操作");
+        }
+        if (!dbpwd.equals(sysUser.getCheckPassword())){
+            log.error("账号:"+sysUser.getUsername()+",数据库密码完整性校验失败!");
+            return SaResultRefit.errorTips("鉴别失败,不能操作");
+        }
+
+        if (!decryptPassword.equals(dbpwd)) {
+//            log.error("鉴别失败,不能操作");
+            return SaResultRefit.errorTips("鉴别失败,不能操作");
+        }
+        // result的值就是被拦截方法的返回值
+        SaResultRefit result = (SaResultRefit)pjp.proceed();
+        return result;
+    }
+}

+ 38 - 0
backend/src/main/java/com/jiayue/ssi/backenum/ApproveOperaterEnum.java

@@ -0,0 +1,38 @@
+package com.jiayue.ssi.backenum;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+* 审批操作枚举
+*
+* @author xsl
+* @since 2023/04/13
+*/
+@Getter
+@AllArgsConstructor
+public enum ApproveOperaterEnum {
+    /**
+     * 新增
+     */
+    INSERT(0, "新增"),
+    /**
+     * 修改
+     */
+    UPDATE(1, "修改"),
+    /**
+     * 删除
+     */
+    DELETE(2, "删除"),
+    /**
+     * 授权
+     */
+    APPROVE(3, "授权"),
+    /**
+     * 解锁
+     */
+    RELOCK(4, "解锁");
+
+    private Integer code;
+    private String message;
+}

+ 26 - 0
backend/src/main/java/com/jiayue/ssi/backenum/ApproveResultEnum.java

@@ -0,0 +1,26 @@
+package com.jiayue.ssi.backenum;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+* 审批结果枚举
+*
+* @author xsl
+* @since 2023/04/13
+*/
+@Getter
+@AllArgsConstructor
+public enum ApproveResultEnum {
+    /**
+     * 通过
+     */
+    TG(0, "通过"),
+    /**
+     * 未通过
+     */
+    WTG(1, "未通过");
+
+    private Integer code;
+    private String message;
+}

+ 26 - 0
backend/src/main/java/com/jiayue/ssi/backenum/ApproveStatusEnum.java

@@ -0,0 +1,26 @@
+package com.jiayue.ssi.backenum;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+* 审批状态枚举
+*
+* @author xsl
+* @since 2023/04/13
+*/
+@Getter
+@AllArgsConstructor
+public enum ApproveStatusEnum {
+    /**
+     * 待审批
+     */
+    DSP(0, "待审批"),
+    /**
+     * 已审批
+     */
+    YSP(1, "已审批");
+
+    private Integer code;
+    private String message;
+}

+ 48 - 0
backend/src/main/java/com/jiayue/ssi/backenum/AuditAblesEventEnum.java

@@ -0,0 +1,48 @@
+package com.jiayue.ssi.backenum;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+* 审计策略配置枚举
+*
+* @author xsl
+* @since 2023/08/18
+*/
+@Getter
+public enum AuditAblesEventEnum {
+    /**
+     * nwp
+     */
+    NWP(0, "/nwpController/getAll"),
+    /**
+     * 短期
+     */
+    DQ(1, "/forecastPowerShortTermController/getAll");
+
+    private Integer code;
+    private String message;
+
+    AuditAblesEventEnum(Integer code, String msg) {
+        this.code = code;
+        this.message = msg;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+
+    public String getMsg() {
+        return message;
+    }
+
+    public static AuditAblesEventEnum getByCode(Integer code) {
+        for (AuditAblesEventEnum type : values()) {
+            if (type.getCode().intValue()==code.intValue()) {
+                return type;
+            }
+        }
+        return null;
+    }
+}

+ 19 - 0
backend/src/main/java/com/jiayue/ssi/backenum/AuditType.java

@@ -0,0 +1,19 @@
+package com.jiayue.ssi.backenum;
+
+/**
+ * 审计类型
+ *
+ * @author xsl
+ */
+public enum AuditType
+{
+    /**
+     * 系统
+     */
+    SYS,
+
+    /**
+     * 业务
+     */
+    BIZ
+}

+ 20 - 0
backend/src/main/java/com/jiayue/ssi/backenum/BusinessStatus.java

@@ -0,0 +1,20 @@
+package com.jiayue.ssi.backenum;
+
+/**
+ * 操作状态
+ *
+ * @author ruoyi
+ *
+ */
+public enum BusinessStatus
+{
+    /**
+     * 成功
+     */
+    SUCCESS,
+
+    /**
+     * 失败
+     */
+    FAIL,
+}

+ 91 - 0
backend/src/main/java/com/jiayue/ssi/backenum/BusinessType.java

@@ -0,0 +1,91 @@
+package com.jiayue.ssi.backenum;
+
+/**
+ * 业务操作类型
+ *
+ * @author ruoyi
+ */
+public enum BusinessType
+{
+    /**
+     * 查询
+     */
+    QUERY,
+    /**
+     * 解锁
+     */
+    UNLOCK,
+    /**
+     * 越权访问
+     */
+    BAC,
+    /**
+     * 其它
+     */
+    OTHER,
+    /**
+     * 初始/重置密码
+     */
+    RESETPWD,
+
+    /**
+     * 新增
+     */
+    INSERT,
+
+    /**
+     * 修改
+     */
+    UPDATE,
+    /**
+     * 个人密码修改
+     */
+    PWDUPDATE,
+
+    /**
+     * 删除
+     */
+    DELETE,
+
+    /**
+     * 授权
+     */
+    GRANT,
+    /**
+     * 获取角色信息
+     */
+    GETROLE,
+
+    /**
+     * 导出
+     */
+    EXPORT,
+
+    /**
+     * 导入
+     */
+    IMPORT,
+
+    /**
+     * 强退
+     */
+    FORCE,
+
+    /**
+     * 生成代码
+     */
+    GENCODE,
+
+    /**
+     * 清空数据
+     */
+    CLEAN,
+    /**
+     * 连接超时
+     */
+    CTO,
+    /**
+     * IP异常
+     */
+    IPYC,
+}

+ 37 - 0
backend/src/main/java/com/jiayue/ssi/backenum/HttpMethod.java

@@ -0,0 +1,37 @@
+package com.jiayue.ssi.backenum;
+
+import org.springframework.lang.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 请求方式
+ *
+ * @author ruoyi
+ */
+public enum HttpMethod
+{
+    GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
+
+    private static final Map<String, HttpMethod> mappings = new HashMap<>(16);
+
+    static
+    {
+        for (HttpMethod httpMethod : values())
+        {
+            mappings.put(httpMethod.name(), httpMethod);
+        }
+    }
+
+    @Nullable
+    public static HttpMethod resolve(@Nullable String method)
+    {
+        return (method != null ? mappings.get(method) : null);
+    }
+
+    public boolean matches(String method)
+    {
+        return (this == resolve(method));
+    }
+}

+ 24 - 0
backend/src/main/java/com/jiayue/ssi/backenum/OperatorType.java

@@ -0,0 +1,24 @@
+package com.jiayue.ssi.backenum;
+
+/**
+ * 操作人类别
+ *
+ * @author ruoyi
+ */
+public enum OperatorType
+{
+    /**
+     * 其它
+     */
+    OTHER,
+
+    /**
+     * 后台用户
+     */
+    MANAGE,
+
+    /**
+     * 手机端用户
+     */
+    MOBILE
+}

+ 43 - 0
backend/src/main/java/com/jiayue/ssi/backenum/PasswordRuleEnum.java

@@ -0,0 +1,43 @@
+package com.jiayue.ssi.backenum;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+* 密码规则枚举
+*
+* @author xsl
+* @since 2023/06/06
+*/
+@Getter
+@AllArgsConstructor
+public enum PasswordRuleEnum {
+    /**
+     * 大写字母
+     */
+    A("大写字母", ".*[A-Z]+.*"),
+    /**
+     * 小写字母
+     */
+    B("小写字母", ".*[a-z]+.*"),
+    /**
+     * 数字
+     */
+    C("数字", ".*\\d+.*"),
+    /**
+     * 特殊字符
+     */
+    D("特殊字符", ".*[~!@#$%^&*()_+|<>,.?/:;'\\[\\]{}\"]+.*");
+
+    private String code;
+    private String message;
+
+    public static PasswordRuleEnum getByName(String name) {
+        for (PasswordRuleEnum type : values()) {
+            if (type.name().equals(name)) {
+                return type;
+            }
+        }
+        return null;
+    }
+}

+ 25 - 0
backend/src/main/java/com/jiayue/ssi/backenum/ResponseEnum.java

@@ -0,0 +1,25 @@
+package com.jiayue.ssi.backenum;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 数据信息状态枚举类
+ *
+ * @author zzy
+ * @version 1.0
+ * @since 2019/6/24 9:34
+ */
+@Getter
+@AllArgsConstructor
+public enum ResponseEnum {
+    /**
+     * 0 表示返回成功
+     */
+    SUCCESS(0, "操作成功!"),
+    FAILED(1, "操作失败!"),
+    ERROR(2, "系统错误!");
+
+    private Integer code;
+    private String message;
+}

+ 85 - 0
backend/src/main/java/com/jiayue/ssi/config/CaptchaConfig.java

@@ -0,0 +1,85 @@
+package com.jiayue.ssi.config;
+
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Properties;
+
+import static com.google.code.kaptcha.Constants.*;
+
+/**
+ * 验证码配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+public class CaptchaConfig
+{
+    @Bean(name = "captchaProducer")
+    public DefaultKaptcha getKaptchaBean()
+    {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        // 是否有边框 默认为true 我们可以自己设置yes,no
+        properties.setProperty(KAPTCHA_BORDER, "yes");
+        // 验证码文本字符颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
+        // 验证码图片宽度 默认为200
+        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+        // 验证码图片高度 默认为50
+        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+        // 验证码文本字符大小 默认为40
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
+        // KAPTCHA_SESSION_KEY
+        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
+        // 验证码文本字符长度 默认为5
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
+        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+
+    @Bean(name = "captchaProducerMath")
+    public DefaultKaptcha getKaptchaBeanMath()
+    {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        // 是否有边框 默认为true 我们可以自己设置yes,no
+        properties.setProperty(KAPTCHA_BORDER, "yes");
+        // 边框颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
+        // 验证码文本字符颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
+        // 验证码图片宽度 默认为200
+        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+        // 验证码图片高度 默认为50
+        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+        // 验证码文本字符大小 默认为40
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
+        // KAPTCHA_SESSION_KEY
+        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
+        // 验证码文本生成器
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ssq.util.KaptchaTextCreator");
+        // 验证码文本字符间距 默认为2
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
+        // 验证码文本字符长度 默认为5
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
+        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+        // 验证码噪点颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
+        // 干扰实现类
+        properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
+        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+}

+ 57 - 0
backend/src/main/java/com/jiayue/ssi/config/EnvironmentPreparedListener.java

@@ -0,0 +1,57 @@
+package com.jiayue.ssi.config;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.jiayue.ssi.constant.SecretKeyConstants;
+import com.jiayue.ssi.util.SM2CryptUtils;
+import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
+import org.springframework.boot.env.OriginTrackedMapPropertySource;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.MutablePropertySources;
+import org.springframework.core.env.PropertySource;
+
+/**
+*
+*
+* @author xsl
+* @since 2023/08/11
+*/
+
+public class EnvironmentPreparedListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>{
+    @Override
+    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
+        ConfigurableEnvironment env = event.getEnvironment();
+        MutablePropertySources pss = env.getPropertySources();
+        List<PropertySource> list = new ArrayList<>();
+        for(PropertySource ps : pss){
+            Map<String,Object>  map = new HashMap<>();
+            if(ps instanceof OriginTrackedMapPropertySource){
+                OriginTrackedMapPropertySource propertySource = new OriginTrackedMapPropertySource(ps.getName(),map);
+                Map<String,Object> src = (Map<String,Object>)ps.getSource();
+                src.forEach((k,v)->{
+                    String strValue = String.valueOf(v);
+                    if(strValue.startsWith("JY[") && strValue.endsWith("]")) {
+                        // 此处进行截取出对应的密文 BR23C92223KKDNUIQMPLS0009 ,然后调用对应的解密算法进行解密操作
+                        v=SM2CryptUtils.decrypt(strValue.substring(3, strValue.length()-1), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                    }
+                    map.put(k,v);
+                });
+                list.add(propertySource);
+            }
+        }
+        /**
+         此处是删除原来的 OriginTrackedMapPropertySource 对象,
+         把解密后新生成的放入到 Environment,为什么不直接修改原来的
+         OriginTrackedMapPropertySource 对象,此处不做过多解释
+         不懂的可以去看看它对应的源码,也算是留一个悬念,也是希望大家
+         能够没事多看一看源码。
+         */
+        list.forEach(ps->{
+            pss.remove(ps.getName());
+            pss.addLast(ps);
+        });
+    }
+}

+ 29 - 0
backend/src/main/java/com/jiayue/ssi/config/GlobalExceptionAdvice.java

@@ -0,0 +1,29 @@
+package com.jiayue.ssi.config;
+
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.util.IPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 全局异常处理
+ *
+ * @author xsl
+ * @since 2023/04/28
+ */
+@Slf4j
+@ControllerAdvice
+public class GlobalExceptionAdvice {
+    @ExceptionHandler(value = CustomException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ResponseBody
+    public void handleException(HttpServletRequest request, Exception e) throws Exception {
+        String ipAddr = IPUtils.getIpAddr(request);
+        log.error("IP:"+ipAddr+"访问异常:", e);
+    }
+}

+ 35 - 0
backend/src/main/java/com/jiayue/ssi/config/MybatisPlusConfig.java

@@ -0,0 +1,35 @@
+package com.jiayue.ssi.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+
+
+/**
+ * 注册mp分页
+ *
+ * @author xsl
+ * @version 3.0
+ */
+@Configuration
+public class MybatisPlusConfig {
+    /**
+     * MybatisPlus注册
+     * @return MybatisPlusInterceptor
+     */
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
+//        paginationInnerInterceptor.setMaxLimit(500L);
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        interceptor.addInnerInterceptor(paginationInnerInterceptor);
+        //注册乐观锁插件
+        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+        return interceptor;
+    }
+}

+ 119 - 0
backend/src/main/java/com/jiayue/ssi/config/SaTokenConfiguration.java

@@ -0,0 +1,119 @@
+package com.jiayue.ssi.config;
+
+import cn.dev33.satoken.config.SaTokenConfig;
+//import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.filter.SaServletFilter;
+import cn.dev33.satoken.interceptor.SaInterceptor;
+import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
+import cn.dev33.satoken.router.SaHttpMethod;
+import cn.dev33.satoken.router.SaRouter;
+import cn.dev33.satoken.stp.StpLogic;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.util.SaResult;
+import com.jiayue.ssi.util.SaResultRefit;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * @author jy
+ * @since 2023/10/12
+ */
+@Configuration
+public class SaTokenConfiguration implements WebMvcConfigurer {
+    /****************方式2:这个配置会合并yml中的配置******************/
+    @Autowired
+    public void configSaToken(SaTokenConfig config) {
+        config.setTokenName("satoken");             // token名称 (同时也是cookie名称)
+        config.setTimeout(30 * 24 * 60 * 60);       // token有效期,单位s 默认30天, -1代表永不过期
+        config.setActiveTimeout(-1);              // token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
+        config.setIsConcurrent(false);               // 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
+        config.setIsShare(false);                    // 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
+//        config.setTokenStyle("uuid");               // token风格
+        config.setIsLog(false);
+        config.setIsReadCookie(false);
+        config.setJwtSecretKey("jiayue");
+        config.setTokenStyle("jwt");
+    }
+    // Sa-Token 整合 jwt (Simple 简单模式)
+    @Bean
+    public StpLogic getStpLogicJwt() {
+        return new StpLogicJwtForSimple();
+    }
+
+//    @Override
+//    public void addInterceptors(InterceptorRegistry registry) {
+//        System.out.println("11111");
+//        // 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。
+//        registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
+//                //拦截的路径
+//                .addPathPatterns("/**")
+//                //开放的登录接口,除此之外的接口都需要登陆才可以访问
+//                .excludePathPatterns("/login","/getVerifyCode");
+//    }
+
+    /**
+     * 注册 [Sa-Token全局过滤器]
+     */
+    @Bean
+    public SaServletFilter getSaServletFilter() {
+        return new SaServletFilter()
+
+                // 指定 拦截路由 与 放行路由
+                .addInclude("/**").addExclude("/favicon.ico","")    /* 排除掉 /favicon.ico */
+
+                // 认证函数: 每次请求执行
+                .setAuth(obj -> {
+                    System.out.println("---------- 进入Sa-Token全局认证 -----------");
+                    System.out.println(SaHolder.getRequest().getRequestPath());
+                    System.out.println(SaHttpMethod.OPTIONS);
+                    SaRouter
+                            .match("/**")	// 拦截的 path 列表,可以写多个 */
+                            .notMatch("/user/login","/getVerifyCode","/static/**","/assets/**","/")		// 排除掉的 path 列表,可以写多个
+                            .check(r -> StpUtil.checkLogin());		// 要执行的校验动作,可以写完整的 lambda 表达式
+
+                    // 更多拦截处理方式,请参考“路由拦截式鉴权”章节 */
+                })
+
+                // 异常处理函数:每次认证函数发生异常时执行此函数
+                .setError(e -> {
+                    System.out.println("---------- 进入Sa-Token异常处理 -----------");
+                    System.out.println(e.getMessage());
+                    return SaResultRefit.errorTips(402,"token无效,请重新登录。");
+                })
+
+                // 前置函数:在每次认证函数之前执行(BeforeAuth 不受 includeList 与 excludeList 的限制,所有请求都会进入)
+                .setBeforeAuth(r -> {
+                    // ---------- 设置一些安全响应头 ----------
+                    SaHolder.getResponse()
+                            // 服务器名称
+                            .setServer("sa-server")
+                            // 是否可以在iframe显示视图: DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
+                            .setHeader("X-Frame-Options", "SAMEORIGIN")
+                            // 是否启用浏览器默认XSS防护: 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时,停止渲染页面
+                            .setHeader("X-XSS-Protection", "1; mode=block")
+                            // 禁用浏览器内容嗅探
+                            .setHeader("X-Content-Type-Options", "nosniff")
+                            // 允许所有请求方式
+                            .setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
+                            // 允许的header参数
+                            .setHeader("Access-Control-Allow-Headers", "x-requested-with,satoken")
+                            // 有效时间
+                            .setHeader("Access-Control-Max-Age", "3600")
+                            // 允许所有请求方式
+                            .setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
+                            // 允许的header参数
+                            .setHeader("Access-Control-Allow-Headers", "x-requested-with,satoken")
+                            // 有效时间
+                            .setHeader("Access-Control-Max-Age", "3600");
+                    // 如果是预检请求,则立即返回到前端
+                    SaRouter.match(SaHttpMethod.OPTIONS)
+                            .free(r1 -> System.out.println("--------OPTIONS预检请求,不做处理"))
+                            .back();
+                });
+    }
+
+}

+ 13 - 0
backend/src/main/java/com/jiayue/ssi/constant/ApproveConstants.java

@@ -0,0 +1,13 @@
+package com.jiayue.ssi.constant;/**
+* 审批常量类
+*
+* @author xsl
+* @since 2023/04/13
+*/
+public class ApproveConstants {
+    /**
+     * 模块名称
+     */
+    public static final String MODULE_NAME_USER = "用户管理";
+
+}

+ 96 - 0
backend/src/main/java/com/jiayue/ssi/constant/CacheConstants.java

@@ -0,0 +1,96 @@
+package com.jiayue.ssi.constant;
+
+import com.jiayue.ssi.dto.ActiveUserDto;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * 缓存的key 常量
+ *
+ * @author ruoyi
+ */
+public class CacheConstants {
+    /**
+     * 登录用户 redis key
+     */
+    public static final String LOGIN_TOKEN_KEY = "login_tokens:";
+
+    /**
+     * 验证码 redis key
+     */
+    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
+
+    /**
+     * 邮箱口令 key
+     */
+    public static final String MAIL_CODE_KEY = "mail_codes:";
+    /**
+     * 非活动退出 key
+     */
+    public static final String REACTIVE_KEY = "reactive:";
+    /**
+     * 防止邮箱口令频繁访问
+     */
+    public static final String PREVENT_MAIL_CODE = "prevent_mail_codes:";
+
+    /**
+     * 参数管理 cache key
+     */
+    public static final String SYS_CONFIG_KEY = "sys_config:";
+
+    /**
+     * 字典管理 cache key
+     */
+    public static final String SYS_DICT_KEY = "sys_dict:";
+
+    /**
+     * 防重提交 redis key
+     */
+    public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
+
+    /**
+     * 限流 redis key
+     */
+    public static final String RATE_LIMIT_KEY = "rate_limit:";
+
+    /**
+     * 登录账户密码错误次数 redis key
+     */
+    public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
+    /**
+     * 服务端心跳
+     */
+    public static final String HEART_KEY = "heart:";
+    /**
+     * 登录token存储,map<用户名,token:授权时间>
+     */
+    public static Map<String,String> LOGIN_TOKEN_MAP = new ConcurrentHashMap<String,String>();
+    /**
+     * 是否使用邮箱口令 默认:true使用
+     */
+    public static boolean use_send_mail = true;
+
+    /**
+     * 用户名对应明文密码缓存
+     */
+    public static ConcurrentMap<String, String> usernamePasswordMap = new ConcurrentHashMap<>();
+    /**
+     * 当前服务端访问的IP集合<IP,用户名>
+     */
+    public static ConcurrentMap<String, String> IP_USER_MAP = new ConcurrentHashMap<>();
+    /**
+     * 用于用户心跳
+     */
+    public static Map<String, ActiveUserDto> ACTIVE_USER_MAP = new ConcurrentHashMap<String,ActiveUserDto>();
+    /**
+     * 用于被踢用户的request存储
+     */
+    public static Map<String, HttpServletRequest> KICKED_USER_MAP = new ConcurrentHashMap<String, HttpServletRequest>();
+    /**
+     * 用于被踢用户的sessionId
+     */
+    public static Map<String, String> SESSIONID_USER_MAP = new ConcurrentHashMap<String, String>();
+}

+ 146 - 0
backend/src/main/java/com/jiayue/ssi/constant/Constants.java

@@ -0,0 +1,146 @@
+package com.jiayue.ssi.constant;
+
+//import io.jsonwebtoken.Claims;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * 通用常量信息
+ *
+ * @author ruoyi
+ */
+public class Constants
+{
+    /**
+     * UTF-8 字符集
+     */
+    public static final String UTF8 = "UTF-8";
+
+    /**
+     * GBK 字符集
+     */
+    public static final String GBK = "GBK";
+
+    /**
+     * www主域
+     */
+    public static final String WWW = "www.";
+
+    /**
+     * http请求
+     */
+    public static final String HTTP = "http://";
+
+    /**
+     * https请求
+     */
+    public static final String HTTPS = "https://";
+
+    /**
+     * 通用成功标识
+     */
+    public static final String SUCCESS = "0";
+
+    /**
+     * 通用失败标识
+     */
+    public static final String FAIL = "1";
+
+    /**
+     * 登录成功
+     */
+    public static final String LOGIN_SUCCESS = "Success";
+
+    /**
+     * 注销
+     */
+    public static final String LOGOUT = "Logout";
+
+    /**
+     * 注册
+     */
+    public static final String REGISTER = "Register";
+
+    /**
+     * 登录失败
+     */
+    public static final String LOGIN_FAIL = "Error";
+
+    /**
+     * 验证码有效期(分钟)
+     */
+    public static final Integer CAPTCHA_EXPIRATION = 2;
+
+    /**
+     * 令牌
+     */
+    public static final String TOKEN = "token";
+
+    /**
+     * 令牌前缀
+     */
+    public static final String TOKEN_PREFIX = "Bearer ";
+
+    /**
+     * 令牌前缀
+     */
+    public static final String LOGIN_USER_KEY = "login_user_key";
+
+    /**
+     * 用户ID
+     */
+    public static final String JWT_USERID = "userid";
+
+    /**
+     * 用户名称
+     */
+    public static final String JWT_USERNAME = "";
+
+    /**
+     * 用户头像
+     */
+    public static final String JWT_AVATAR = "avatar";
+
+    /**
+     * 创建时间
+     */
+    public static final String JWT_CREATED = "created";
+
+    /**
+     * 用户权限
+     */
+    public static final String JWT_AUTHORITIES = "authorities";
+
+    /**
+     * 资源映射路径 前缀
+     */
+    public static final String RESOURCE_PREFIX = "/profile";
+
+    /**
+     * RMI 远程方法调用
+     */
+    public static final String LOOKUP_RMI = "rmi:";
+
+    /**
+     * LDAP 远程方法调用
+     */
+    public static final String LOOKUP_LDAP = "ldap:";
+
+    /**
+     * LDAPS 远程方法调用
+     */
+    public static final String LOOKUP_LDAPS = "ldaps:";
+
+    /**
+     * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
+     */
+    public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
+
+    /**
+     * 定时任务违规的字符
+     */
+    public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
+            "org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config" };
+
+}

+ 22 - 0
backend/src/main/java/com/jiayue/ssi/constant/CustomException.java

@@ -0,0 +1,22 @@
+package com.jiayue.ssi.constant;
+/**
+* 自定义异常
+*
+* @author xsl
+* @since 2023/04/28
+*/
+public class CustomException extends Exception{
+    private String message;
+    private Throwable cause;
+
+    public CustomException(String message,Exception e){
+        super(message,e);
+        this.message = message;
+        this.cause = e;
+    }
+
+    @Override
+    public String getMessage(){
+        return message;
+    }
+}

+ 27 - 0
backend/src/main/java/com/jiayue/ssi/constant/LoginConstants.java

@@ -0,0 +1,27 @@
+package com.jiayue.ssi.constant;
+
+import com.jiayue.ssi.dto.UserVisitInfoDto;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+* 登录用到的常量
+*
+* @author xsl
+* @since 2023/03/20
+*/
+public class LoginConstants {
+    /**
+     * 登录锁定后多久时间重试(毫秒)
+     */
+    public static final Long LOGIN_LOCK = 100000L;
+
+    /**
+     * 并发会话存储<用户名,访问信息>
+     */
+    public static ConcurrentMap<String, UserVisitInfoDto> sessionMap = new ConcurrentHashMap<>();
+}

+ 37 - 0
backend/src/main/java/com/jiayue/ssi/constant/PermissionCharacter.java

@@ -0,0 +1,37 @@
+package com.jiayue.ssi.constant;
+/**
+* 权限字符
+*
+* @author xsl
+* @since 2023/06/02
+*/
+public class PermissionCharacter {
+    /**
+     * 系统管理
+     */
+    public static final String P_XTGL = "XTGL";
+    /**
+     * 审计管理
+     */
+    public static final String P_SJGL = "SJGL";
+    /**
+     * 审计业务
+     */
+    public static final String P_SJYW = "SJYW";
+    /**
+     * 审核管理
+     */
+    public static final String P_SHGL = "SHGL";
+    /**
+     * 业务管理
+     */
+    public static final String P_YWGL = "YWGL";
+    /**
+     * 业务配置
+     */
+    public static final String P_YWPZ = "YWPZ";
+    /**
+     * 业务操作
+     */
+    public static final String P_YWCZ = "YWCZ";
+}

+ 27 - 0
backend/src/main/java/com/jiayue/ssi/constant/PermissionContextHolder.java

@@ -0,0 +1,27 @@
+package com.jiayue.ssi.constant;
+
+import cn.hutool.core.convert.Convert;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+
+/**
+ * 权限信息
+ *
+ * @author ruoyi
+ */
+public class PermissionContextHolder
+{
+    private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT";
+
+    public static void setContext(String permission)
+    {
+        RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission,
+                RequestAttributes.SCOPE_REQUEST);
+    }
+
+    public static String getContext()
+    {
+        return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES,
+                RequestAttributes.SCOPE_REQUEST));
+    }
+}

+ 24 - 0
backend/src/main/java/com/jiayue/ssi/constant/SecretKeyConstants.java

@@ -0,0 +1,24 @@
+package com.jiayue.ssi.constant;/**
+* 密钥类
+*
+* @author xsl
+* @since 2023/02/27
+*/
+public class SecretKeyConstants {
+    /**
+     * 客户端密钥对(公钥)
+     */
+    public static final String CLIENT_PUBLIC_KEY = "0460ff8c8c306fe62f6f9d11c5c82c30d10bbbc703da094e423072cac7dc663c97fad52eccb34f311f47a07f280de157ba4f2aa659cabe749121384b9376ea2ed2";
+    /**
+     * 客户端密钥对(私钥)
+     */
+    public static final String CLIENT_PRIVATE_KEY = "27ce6eec39dbf3b564a77c4da1e129fe1ba01a92f6d61055a33ed14ffcbc949e";
+    /**
+     * 服务端密钥对(公钥)
+     */
+    public static final String SERVER_PUBLIC_KEY = "041967638ca43d4577d8dba166bff4437fde944270101f398a95b846ec2f8177d09f8abc5d62b6cd2c7216274d7abe0c8e04b0bb691207a32dd2e12d6bd2798672";
+    /**
+     * 服务端密钥对(私钥)
+     */
+    public static final String SERVER_PRIVATE_KEY = "6155d63ee27cbeca07f3e40c4f8856f1be8119fcbda1aadc7e0e595e52bad7bd";
+}

+ 78 - 0
backend/src/main/java/com/jiayue/ssi/constant/UserConstants.java

@@ -0,0 +1,78 @@
+package com.jiayue.ssi.constant;
+
+/**
+ * 用户常量信息
+ *
+ * @author ruoyi
+ */
+public class UserConstants
+{
+    /**
+     * 平台内系统用户的唯一标志
+     */
+    public static final String SYS_USER = "SYS_USER";
+
+    /** 正常状态 */
+    public static final String NORMAL = "0";
+
+    /** 异常状态 */
+    public static final String EXCEPTION = "1";
+
+    /** 用户封禁状态 */
+    public static final String USER_DISABLE = "1";
+
+    /** 角色封禁状态 */
+    public static final String ROLE_DISABLE = "1";
+
+    /** 部门正常状态 */
+    public static final String DEPT_NORMAL = "0";
+
+    /** 部门停用状态 */
+    public static final String DEPT_DISABLE = "1";
+
+    /** 字典正常状态 */
+    public static final String DICT_NORMAL = "0";
+
+    /** 是否为系统默认(是) */
+    public static final String YES = "Y";
+
+    /** 是否菜单外链(是) */
+    public static final String YES_FRAME = "0";
+
+    /** 是否菜单外链(否) */
+    public static final String NO_FRAME = "1";
+
+    /** 菜单类型(目录) */
+    public static final String TYPE_DIR = "M";
+
+    /** 菜单类型(菜单) */
+    public static final String TYPE_MENU = "C";
+
+    /** 菜单类型(按钮) */
+    public static final String TYPE_BUTTON = "F";
+
+    /** Layout组件标识 */
+    public final static String LAYOUT = "Layout";
+
+    /** ParentView组件标识 */
+    public final static String PARENT_VIEW = "ParentView";
+
+    /** InnerLink组件标识 */
+    public final static String INNER_LINK = "InnerLink";
+
+    /** 校验返回结果码 */
+    public final static String UNIQUE = "0";
+    public final static String NOT_UNIQUE = "1";
+
+    /**
+     * 用户名长度限制
+     */
+    public static final int USERNAME_MIN_LENGTH = 2;
+    public static final int USERNAME_MAX_LENGTH = 20;
+
+    /**
+     * 密码长度限制
+     */
+    public static final int PASSWORD_MIN_LENGTH = 5;
+    public static final int PASSWORD_MAX_LENGTH = 20;
+}

+ 115 - 0
backend/src/main/java/com/jiayue/ssi/controller/ElectricFieldController.java

@@ -0,0 +1,115 @@
+package com.jiayue.ssi.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.entity.ElectricField;
+import com.jiayue.ssi.service.ElectricFieldService;
+import com.jiayue.ssi.util.ResponseVO;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+
+/**
+ * 场站接口
+ *
+ * @author xsl
+ * @since 2023/03/13
+ */
+@RestController
+@RequestMapping("/electricField")
+@Slf4j
+public class ElectricFieldController {
+
+    @Autowired
+    ElectricFieldService electricFieldService;
+
+    /**
+     * 获取场站信息
+     *
+     * @return 场站信息
+     */
+    @GetMapping(value = "/getAll")
+    public ResponseVO getAll() throws CustomException {
+        try {
+            ElectricField electricField = electricFieldService.getOne(new QueryWrapper<>());
+            return ResponseVO.success(electricField);
+        } catch (Exception e) {
+            throw new CustomException("获取场站信息异常", e);
+        }
+    }
+
+    /**
+     * 保存场站
+     */
+    @PostMapping
+    public ResponseVO update(@RequestBody ElectricField electricField) throws CustomException {
+        try {
+            if (StringUtils.isEmpty(electricField.getStationCode())) {
+                return ResponseVO.fail("场站编号不能为空!");
+            } else if (electricField.getStationCode().length() > 6) {
+                return ResponseVO.fail(electricField.getStationCode() + "场站编号长度不能超过6个字符!");
+            }
+
+            if (StringUtils.isEmpty(electricField.getName())) {
+                return ResponseVO.fail("场站名称不能为空!");
+            } else if (electricField.getName().length() > 50) {
+                return ResponseVO.fail(electricField.getName() + "场站名称长度不能超过50个字符!");
+            }
+
+            if (StringUtils.isEmpty(electricField.getSign())) {
+                return ResponseVO.fail("场站标识不能为空!");
+            } else if (electricField.getSign().length() > 10) {
+                return ResponseVO.fail(electricField.getSign() + "场站标识长度不能超过10个字符!");
+            }
+
+
+            String pattern = "^[0-9]{1,8}+\\.{0,1}[0-9]{0,2}$";
+            if (electricField.getCapacity() == null) {
+                return ResponseVO.fail("装机容量不能为空!");
+            } else if (!electricField.getCapacity().toString().matches(pattern)) {
+                return ResponseVO.fail("装机容量整数位不能超过8位,小数位不能超过2位!");
+            }
+
+            if (electricField.getGridce() == null) {
+                return ResponseVO.fail("并网设备数不能为空!");
+            } else if (!String.valueOf(electricField.getGridce()).matches("^([0-9][0-9]{0,1}|100)$")) {
+                return ResponseVO.fail("并网设备数请输入0-100整数");
+            }
+
+            if (StringUtils.isEmpty(electricField.getFieldType())) {
+                return ResponseVO.fail("场站类型不能为空!");
+            } else if (electricField.getFieldType().length() > 1) {
+                return ResponseVO.fail("场站类型长度不能超过1个字符!");
+            }
+
+            if (StringUtils.isEmpty(electricField.getCompany())) {
+                return ResponseVO.fail("所属公司不能为空!");
+            } else if (electricField.getCompany().length() > 100) {
+                return ResponseVO.fail("所属公司长度不能超过100个字符!");
+            }
+
+            if (StringUtils.isEmpty(electricField.getLocation())) {
+                return ResponseVO.fail("场站位置不能为空!");
+            } else if (electricField.getLocation().length() > 100) {
+                return ResponseVO.fail("场站位置长度不能超过100个字符!");
+            }
+
+            ElectricField oldElectricField = electricFieldService.getById(electricField.getId());
+            if (electricField.getVersion().intValue() != oldElectricField.getVersion().intValue()){
+                return ResponseVO.fail("此数据被操作过,自动刷新列表后请重试操作!");
+            }
+
+            boolean bo = electricFieldService.saveOrUpdate(electricField);
+            if (bo) {
+                return ResponseVO.success("场站信息保存成功");
+            } else {
+                return ResponseVO.fail("场站信息保存失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("场站信息保存异常", e);
+        }
+    }
+
+}

+ 275 - 0
backend/src/main/java/com/jiayue/ssi/controller/SysApproveController.java

@@ -0,0 +1,275 @@
+package com.jiayue.ssi.controller;
+
+import cn.hutool.core.util.DesensitizedUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.jiayue.ssi.backenum.ApproveOperaterEnum;
+import com.jiayue.ssi.constant.ApproveConstants;
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.constant.SecretKeyConstants;
+import com.jiayue.ssi.entity.SysApprove;
+import com.jiayue.ssi.entity.SysUser;
+import com.jiayue.ssi.service.SysApproveService;
+import com.jiayue.ssi.service.SysUserService;
+import com.jiayue.ssi.util.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DateFormatUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 审核接口
+ *
+ * @author xsl
+ * @since 2023/03/13
+ */
+@RestController
+@RequestMapping("/sysApproveController")
+@Slf4j
+public class SysApproveController {
+    @Autowired
+    SysApproveService sysApproveService;
+    @Autowired
+    SysUserService sysUserService;
+
+    /**
+     * 获取用户分页信息
+     *
+     * @return 用户信息
+     */
+    @GetMapping(value = "/getAll")
+    public ResponseVO getAll(String currentPage, String pageSize, String approveStatus, String approveResult) throws CustomException {
+        try {
+            Integer cp;
+            if (StringUtils.isNotEmpty(currentPage)) {
+                if (NumberUtil.isInteger(currentPage)){
+                    cp = Integer.parseInt(currentPage);
+                }
+                else{
+                    return ResponseVO.fail("currentPage不是整数!");
+                }
+            }
+            else{
+                return ResponseVO.fail("currentPage不能是空!");
+            }
+
+            Integer ps;
+            if (StringUtils.isNotEmpty(pageSize)) {
+                if (NumberUtil.isInteger(pageSize)){
+                    ps = Integer.parseInt(pageSize);
+                }
+                else{
+                    return ResponseVO.fail("pageSize不是整数!");
+                }
+            }
+            else{
+                return ResponseVO.fail("pageSize不能是空!");
+            }
+
+            QueryWrapper<SysApprove> wrapper = new QueryWrapper<>();
+            if (StringUtils.isNotEmpty(approveStatus)) {
+                wrapper.eq("approve_status", approveStatus);
+            }
+            if (StringUtils.isNotEmpty(approveResult)) {
+                wrapper.eq("approve_result", approveResult);
+            }
+            Page<SysApprove> result = sysApproveService.page(new Page<>(cp, ps), wrapper);
+            List<SysApprove> records = result.getRecords();
+            //遍历对象数组的方法
+            records.forEach(
+                    record -> {
+                        if (record.getModuleName().equals(ApproveConstants.MODULE_NAME_USER)){
+                            if (record.getOperation().equals(String.valueOf(ApproveOperaterEnum.INSERT.getCode()))){
+                                // 敏感数据先解密
+                                String idcard = SM2CryptUtils.decrypt(record.getIdcard(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String nickname = SM2CryptUtils.decrypt(record.getNickname(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String mailbox = SM2CryptUtils.decrypt(record.getMailbox(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String phonenumber = SM2CryptUtils.decrypt(record.getPhonenumber(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                // 脱密处理
+                                idcard = DesensitizedUtil.idCardNum(idcard,5,2);
+                                nickname = DesensitizedUtil.chineseName(nickname);
+                                mailbox = DesensitizedUtil.email(mailbox);
+                                phonenumber = DesensitizedUtil.mobilePhone(phonenumber);
+                                // 封装新增操作
+                                StringBuffer parameterContent = new StringBuffer("");
+                                parameterContent.append("账号:"+record.getUsername());
+                                parameterContent.append(",身份证号码:"+idcard);
+                                if ("0".equals(record.getUsertype())){
+                                    parameterContent.append(",用户类型:管理员");
+                                }
+                                else {
+                                    parameterContent.append(",用户类型:业务用户");
+                                }
+
+                                parameterContent.append(",姓名:"+nickname);
+                                parameterContent.append(",邮箱:"+mailbox);
+                                parameterContent.append(",手机号码:"+phonenumber);
+                                if (record.getExpDate()==null){
+
+                                }
+                                else {
+                                    parameterContent.append(",账号有效期:"+DateFormatUtils.format(record.getExpDate(),"yyyy-MM-dd"));
+                                }
+                                record.setParameterContent(parameterContent.toString());
+                            }
+                            else if (record.getOperation().equals(String.valueOf(ApproveOperaterEnum.UPDATE.getCode()))){
+                                // 根据用户主键ID获取用户表里原有数据
+                                StringBuffer parameterContent = new StringBuffer("");
+                                String oldInfo = record.getParameterContent();
+                                SysUser oldSysUser = JSONUtil.toBean(oldInfo,SysUser.class);
+                                parameterContent.append("修改前内容:\n");
+                                String oldidcard = SM2CryptUtils.decrypt(oldSysUser.getIdcard(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String oldnickname = SM2CryptUtils.decrypt(oldSysUser.getNickname(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String oldmailbox = SM2CryptUtils.decrypt(oldSysUser.getMailbox(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String oldphonenumber = SM2CryptUtils.decrypt(oldSysUser.getPhonenumber(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                // 脱密处理
+                                String tmidcard = DesensitizedUtil.idCardNum(oldidcard,5,2);
+                                String tmnickname = DesensitizedUtil.chineseName(oldnickname);
+                                String tmailbox = DesensitizedUtil.email(oldmailbox);
+                                String tmphonenumber = DesensitizedUtil.mobilePhone(oldphonenumber);
+                                parameterContent.append("账号:"+oldSysUser.getUsername());
+                                parameterContent.append(",身份证号码:"+tmidcard);
+                                if ("0".equals(oldSysUser.getUsertype())){
+                                    parameterContent.append(",用户类型:管理员");
+                                }
+                                else {
+                                    parameterContent.append(",用户类型:业务用户");
+                                }
+                                parameterContent.append(",姓名:"+tmnickname);
+                                parameterContent.append(",邮箱:"+tmailbox);
+                                parameterContent.append(",手机号码:"+tmphonenumber);
+                                if (oldSysUser.getExpDate()==null){
+                                }
+                                else {
+                                    parameterContent.append(",账号有效期:"+DateFormatUtils.format(oldSysUser.getExpDate(),"yyyy-MM-dd"));
+                                }
+                                parameterContent.append("\n");
+
+                                // 审批表里的记录敏感数据先解密
+                                String idcard = SM2CryptUtils.decrypt(record.getIdcard(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String nickname = SM2CryptUtils.decrypt(record.getNickname(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String mailbox = SM2CryptUtils.decrypt(record.getMailbox(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String phonenumber = SM2CryptUtils.decrypt(record.getPhonenumber(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                // 脱密处理
+                                idcard = DesensitizedUtil.idCardNum(idcard,5,2);
+                                nickname = DesensitizedUtil.chineseName(nickname);
+                                mailbox = DesensitizedUtil.email(mailbox);
+                                phonenumber = DesensitizedUtil.mobilePhone(phonenumber);
+                                parameterContent.append("修改后内容:\n");
+                                parameterContent.append("账号:"+record.getUsername());
+                                parameterContent.append(",身份证号码:"+idcard);
+                                if ("0".equals(record.getUsertype())){
+                                    parameterContent.append(",用户类型:管理员");
+                                }
+                                else {
+                                    parameterContent.append(",用户类型:业务用户");
+                                }
+
+                                parameterContent.append(",姓名:"+nickname);
+                                parameterContent.append(",邮箱:"+mailbox);
+                                parameterContent.append(",手机号码:"+phonenumber);
+                                if (record.getExpDate()==null){
+
+                                }
+                                else {
+                                    parameterContent.append(",账号有效期:"+DateFormatUtils.format(record.getExpDate(),"yyyy-MM-dd"));
+                                }
+                                record.setParameterContent(parameterContent.toString());
+                            }
+                            else if (record.getOperation().equals(String.valueOf(ApproveOperaterEnum.DELETE.getCode()))){
+                                // 敏感数据先解密
+                                String idcard = SM2CryptUtils.decrypt(record.getIdcard(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String nickname = SM2CryptUtils.decrypt(record.getNickname(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String mailbox = SM2CryptUtils.decrypt(record.getMailbox(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                String phonenumber = SM2CryptUtils.decrypt(record.getPhonenumber(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+                                // 脱密处理
+                                idcard = DesensitizedUtil.idCardNum(idcard,5,2);
+                                nickname = DesensitizedUtil.chineseName(nickname);
+                                mailbox = DesensitizedUtil.email(mailbox);
+                                phonenumber = DesensitizedUtil.mobilePhone(phonenumber);
+                                // 封装新增操作
+                                StringBuffer parameterContent = new StringBuffer("");
+                                parameterContent.append("账号:"+record.getUsername());
+                                parameterContent.append(",身份证号码:"+idcard);
+                                if ("0".equals(record.getUsertype())){
+                                    parameterContent.append(",用户类型:管理员");
+                                }
+                                else {
+                                    parameterContent.append(",用户类型:业务用户");
+                                }
+
+                                parameterContent.append(",姓名:"+nickname);
+                                parameterContent.append(",邮箱:"+mailbox);
+                                parameterContent.append(",手机号码:"+phonenumber);
+                                if (record.getExpDate()==null){
+
+                                }
+                                else {
+                                    parameterContent.append(",账号有效期:"+DateFormatUtils.format(record.getExpDate(),"yyyy-MM-dd"));
+                                }
+                                record.setParameterContent(parameterContent.toString());
+                            }
+//                            else if (record.getOperation().equals(String.valueOf(ApproveOperaterEnum.RELOCK.getCode()))){
+//                                // 敏感数据先解密
+//                                String idcard = SM2CryptUtils.decrypt(record.getIdcard(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+//                                String nickname = SM2CryptUtils.decrypt(record.getNickname(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+//                                String mailbox = SM2CryptUtils.decrypt(record.getMailbox(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+//                                String phonenumber = SM2CryptUtils.decrypt(record.getPhonenumber(), SecretKeyConstants.SERVER_PRIVATE_KEY);
+//                                // 脱密处理
+//                                idcard = DesensitizedUtil.idCardNum(idcard,5,2);
+//                                nickname = DesensitizedUtil.chineseName(nickname);
+//                                mailbox = DesensitizedUtil.email(mailbox);
+//                                phonenumber = DesensitizedUtil.mobilePhone(phonenumber);
+//                                // 封装新增操作
+//                                StringBuffer parameterContent = new StringBuffer("");
+//                                parameterContent.append("账号:"+record.getUsername());
+//                                parameterContent.append(",身份证号码:"+idcard);
+//                                if ("0".equals(record.getUsertype())){
+//                                    parameterContent.append(",用户类型:管理员");
+//                                }
+//                                else {
+//                                    parameterContent.append(",用户类型:业务用户");
+//                                }
+//
+//                                parameterContent.append(",姓名:"+nickname);
+//                                parameterContent.append(",邮箱:"+mailbox);
+//                                parameterContent.append(",手机号码:"+phonenumber);
+//                                if (record.getExpDate()==null){
+//
+//                                }
+//                                else {
+//                                    parameterContent.append(",账号有效期:"+DateFormatUtils.format(record.getExpDate(),"yyyy-MM-dd"));
+//                                }
+//                                record.setParameterContent(parameterContent.toString());
+//                            }
+                        }
+                    }
+            );
+            return ResponseVO.success(result);
+        } catch (Exception e) {
+            throw new CustomException("获取所有审核异常", e);
+        }
+    }
+
+    /**
+     * 审批结果提交
+     */
+    @PostMapping(value = "/submitApprove")
+    public ResponseVO submitApprove(@RequestBody SysApprove sysApprove) throws CustomException {
+        try {
+            SysApprove oldSysApprove = sysApproveService.getById(sysApprove.getId());
+            if (sysApprove.getVersion().intValue() != oldSysApprove.getVersion().intValue()){
+                return ResponseVO.fail("此数据被操作过,自动刷新列表后请重试操作!");
+            }
+            sysApproveService.submitApprove(sysApprove);
+            return ResponseVO.success("审核提交成功");
+        } catch (Exception e) {
+            throw new CustomException("审核提交异常", e);
+        }
+    }
+}

+ 205 - 0
backend/src/main/java/com/jiayue/ssi/controller/SysLogininforController.java

@@ -0,0 +1,205 @@
+package com.jiayue.ssi.controller;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.NumberUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.entity.SysLogininfor;
+import com.jiayue.ssi.service.SysLogininforService;
+import com.jiayue.ssi.util.DateUtils;
+import com.jiayue.ssi.util.ResponseVO;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 系统访问记录
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/sysLogininforController")
+@Slf4j
+public class SysLogininforController {
+    @Autowired
+    SysLogininforService sysLogininforService;
+
+    /**
+     * 获取列表分页信息
+     *
+     * @return 列表信息
+     */
+    @GetMapping(value = "/getAll")
+    public ResponseVO getAll(String currentPage, String pageSize, String ipaddr, String userName,
+                             String status, String startLoginTime, String endLoginTime, String sortOrder) throws CustomException {
+        try {
+            Integer cp;
+            if (StringUtils.isNotEmpty(currentPage)) {
+                if (NumberUtil.isInteger(currentPage)){
+                    cp = Integer.parseInt(currentPage);
+                }
+                else{
+                    return ResponseVO.fail("currentPage不是整数!");
+                }
+            }
+            else{
+                return ResponseVO.fail("currentPage不能是空!");
+            }
+
+            Integer ps;
+            if (StringUtils.isNotEmpty(pageSize)) {
+                if (NumberUtil.isInteger(pageSize)){
+                    ps = Integer.parseInt(pageSize);
+                }
+                else{
+                    return ResponseVO.fail("pageSize不是整数!");
+                }
+            }
+            else{
+                return ResponseVO.fail("pageSize不能是空!");
+            }
+
+            if (StringUtils.isNotEmpty(ipaddr)) {
+                if (ipaddr.length() > 128) {
+                    return ResponseVO.fail("登录地址长度不能超过128个字符!");
+                }
+            }
+            if (StringUtils.isNotEmpty(userName)) {
+                if (userName.length() > 50) {
+                    return ResponseVO.fail("用户账号长度不能超过50个字符!");
+                }
+            }
+            QueryWrapper<SysLogininfor> wrapper = new QueryWrapper<>();
+            if (StringUtils.isNotEmpty(ipaddr)) {
+                wrapper.eq("ipaddr", ipaddr);
+            }
+            if (StringUtils.isNotEmpty(userName)) {
+                wrapper.eq("user_name", userName);
+            }
+            if (StringUtils.isNotEmpty(status)) {
+                wrapper.eq("status", status);
+            }
+            if (StringUtils.isNotEmpty(startLoginTime)) {
+                wrapper.ge("login_time", DateUtils.getDayStartTime(DateUtil.parseDate(startLoginTime)));
+            }
+            if (StringUtils.isNotEmpty(endLoginTime)) {
+                wrapper.le("login_time", DateUtils.getDayLastTime(DateUtil.parseDate(endLoginTime)));
+            }
+
+            if (StringUtils.isNotEmpty(sortOrder)) {
+                String[] orders = sortOrder.replaceAll("&","&").split("&");
+                String sortDbField = "";
+                if ("loginTime".equals(orders[0])) {
+                    sortDbField = "login_time";
+                } else if ("userName".equals(orders[0])) {
+                    sortDbField = "user_name";
+                }
+
+                if ("asc".equals(orders[1])) {
+                    wrapper.orderByAsc(sortDbField);
+                } else {
+                    wrapper.orderByDesc(sortDbField);
+                }
+            }
+
+            Page<SysLogininfor> result = sysLogininforService.page(new Page<>(cp, ps), wrapper);
+            return ResponseVO.success(result);
+        } catch (Exception e) {
+            throw new CustomException("获取登录日志列表异常", e);
+        }
+    }
+
+    /**
+     * 删除登录信息
+     */
+    @PostMapping(value = "/delLoginInfo")
+    public ResponseVO delLoginInfo(String infoId) throws CustomException {
+        try {
+            if (StringUtils.isEmpty(infoId)) {
+                return ResponseVO.fail("id不能为空!");
+            }
+
+            boolean bo = sysLogininforService.removeLoginInfoById(Long.parseLong(infoId));
+            if (bo) {
+                return ResponseVO.success("删除登录信息成功");
+            } else {
+                log.error("删除用户信息失败");
+                return ResponseVO.fail("删除登录信息失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("删除登录信息异常", e);
+        }
+    }
+
+    /**
+     * 清空登录信息
+     */
+    @PostMapping("/cleanLogininfor")
+    public ResponseVO cleanLogininfor() throws CustomException {
+        try {
+            boolean bo = sysLogininforService.cleanLogininfor();
+            if (bo) {
+                return ResponseVO.success("登录日志清空成功");
+            } else {
+                log.error("登录日志清空失败");
+                return ResponseVO.fail("登录日志清空失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("登录日志清空异常", e);
+        }
+    }
+
+    /**
+     * 登录日志统计分析
+     *
+     * @return 列表信息
+     */
+    @GetMapping(value = "/logininforTotal")
+    public ResponseVO logininforTotal(String startLoginTime, String endLoginTime) throws CustomException {
+        try {
+            QueryWrapper<SysLogininfor> wrapper = new QueryWrapper<>();
+            if (StringUtils.isNotEmpty(startLoginTime)) {
+                wrapper.ge("login_time", DateUtils.getDayStartTime(DateUtil.parseDate(startLoginTime)));
+            }
+            if (StringUtils.isNotEmpty(endLoginTime)) {
+                wrapper.le("login_time", DateUtils.getDayLastTime(DateUtil.parseDate(endLoginTime)));
+            }
+            List<SysLogininfor> sysLogininforList = sysLogininforService.list(wrapper);
+            Map<String, List<SysLogininfor>> map = sysLogininforList.stream().collect(Collectors.groupingBy(item -> DateUtil.format(item.getLoginTime(), "yyyy-MM-dd")));
+            List<Map<String, String>> resultList = new ArrayList<>();
+            map.forEach((key, value) -> {
+                List<SysLogininfor> list = value;
+                Map<String, String> recordMap = new HashMap<>();
+                recordMap.put("day", key);
+                recordMap.put("count", list.size() + "");
+                // 登录成功统计
+                List<SysLogininfor> successList = list.stream().filter(sysLogininfor -> "0".equals(sysLogininfor.getStatus())).collect(Collectors.toList());
+                recordMap.put("success", successList.size() + "");
+                // 登录失败统计
+                List<SysLogininfor> failList = list.stream().filter(sysLogininfor -> "1".equals(sysLogininfor.getStatus()) && "登录失败".equals(sysLogininfor.getMsg())).collect(Collectors.toList());
+                recordMap.put("fail", failList.size() + "");
+                // 连续登录失败统计
+                List<SysLogininfor> lxfailList = list.stream().filter(sysLogininfor -> "1".equals(sysLogininfor.getStatus()) && "连续登录失败".equals(sysLogininfor.getMsg())).collect(Collectors.toList());
+                recordMap.put("lxfail", lxfailList.size() + "");
+                // 登录ip个数统计
+                int ips = list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(s -> s.getIpaddr()))), ArrayList::new)).size();
+                recordMap.put("ips", ips + "");
+                resultList.add(recordMap);
+            });
+            Collections.sort(resultList, new Comparator<Map<String, String>>() {
+                @Override
+                public int compare(Map<String, String> o1, Map<String, String> o2) {
+                    return o1.get("day").compareTo(o2.get("day"));
+                }
+            });
+            return ResponseVO.success(resultList);
+        } catch (Exception e) {
+            throw new CustomException("获取登录日志统计列表异常", e);
+        }
+    }
+}

+ 333 - 0
backend/src/main/java/com/jiayue/ssi/controller/SysMenuController.java

@@ -0,0 +1,333 @@
+package com.jiayue.ssi.controller;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import cn.hutool.core.util.NumberUtil;
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.constant.UserConstants;
+import com.jiayue.ssi.entity.SysMenu;
+import com.jiayue.ssi.entity.SysRole;
+import com.jiayue.ssi.service.SysMenuService;
+import com.jiayue.ssi.service.SysRoleService;
+import com.jiayue.ssi.util.RyStringUtils;
+import com.jiayue.ssi.util.SaResultRefit;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 菜单接口
+ *
+ * @author xsl
+ * @since 2023/03/21
+ */
+@RestController
+@RequestMapping("/sysMenuController")
+@Slf4j
+public class SysMenuController {
+
+    @Autowired
+    private SysMenuService sysMenuService;
+    @Autowired
+    private SysRoleService sysRoleService;
+
+    /**
+     * 获取菜单列表
+     */
+    @GetMapping("/list")
+    public SaResultRefit list(String menuName) throws CustomException {
+        try {
+            if (StringUtils.isNotEmpty(menuName)) {
+                if (menuName.length() > 50) {
+                    return SaResultRefit.errorTips("菜单名长度不能超过50个字符!");
+                }
+            }
+            SysMenu menu = new SysMenu();
+            menu.setMenuName(menuName);
+            List<SysMenu> menus = sysMenuService.selectMenuList(menu, 1L);
+            return SaResultRefit.data(menus);
+        } catch (Exception e) {
+            throw new CustomException("获取菜单列表异常", e);
+        }
+    }
+
+    /**
+     * 新增菜单
+     */
+    @PostMapping(value = "/addMenu")
+    public SaResultRefit addMenu(@RequestBody SysMenu menu) throws CustomException {
+        try {
+            if (RyStringUtils.isEmpty(menu.getMenuName()) || RyStringUtils.isEmpty(menu.getMenuName().trim())) {
+                return SaResultRefit.errorTips("菜单名称不能为空!");
+            } else {
+                String menuName = menu.getMenuName().trim();
+                if (menuName.length() > 50) {
+                    return SaResultRefit.errorTips("菜单名长度不能超过50个字符!");
+                }
+            }
+
+            if (!"F".equals(menu.getMenuType())) {
+                if (RyStringUtils.isEmpty(menu.getPath()) || RyStringUtils.isEmpty(menu.getPath().trim())) {
+                    return SaResultRefit.errorTips("路由地址不能为空!");
+                } else {
+                    String routerPath = menu.getPath().trim();
+                    if (routerPath.length() > 200) {
+                        return SaResultRefit.errorTips("路由地址长度不能超过200个字符!");
+                    }
+                }
+            }
+            if (menu.getOrderNum() == null) {
+                return SaResultRefit.errorTips("排序不能为空!");
+            } else if (!NumberUtil.isInteger(menu.getOrderNum() + "")) {
+                return SaResultRefit.errorTips("排序不是整型数值!");
+            }
+
+            if (RyStringUtils.isNotEmpty(menu.getComponent())) {
+                if (menu.getComponent().length() > 200) {
+                    return SaResultRefit.errorTips("组件路径长度不能超过200个字符!");
+                }
+            }
+
+            if (RyStringUtils.isNotEmpty(menu.getQuery())) {
+                if (menu.getQuery().length() > 200) {
+                    return SaResultRefit.errorTips("路由参数长度不能超过200个字符!");
+                }
+            }
+
+            if (RyStringUtils.isNotEmpty(menu.getPerms())) {
+                if (menu.getPerms().length() > 100) {
+                    return SaResultRefit.errorTips("权限字符长度不能超过100个字符!");
+                }
+            }
+            if (UserConstants.NOT_UNIQUE.equals(sysMenuService.checkMenuNameUnique(menu))) {
+                return SaResultRefit.errorTips(menu.getMenuName() + "'失败,菜单名称已存在!");
+            }
+            if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !RyStringUtils.ishttp(menu.getPath())) {
+                return SaResultRefit.errorTips(menu.getMenuName() + "'失败,地址必须以http(s)://开头!");
+            }
+            menu.setCreateBy("");
+            int bo = sysMenuService.insertMenu(menu);
+            if (bo == 1) {
+                return SaResultRefit.ok("添加菜单成功");
+            } else {
+                log.error("添加菜单失败");
+                return SaResultRefit.errorTips("添加菜单失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("添加菜单异常", e);
+        }
+    }
+
+    /**
+     * 更新系统参数
+     *
+     * @param menu 参数
+     * @return 执行结果
+     */
+    @PostMapping(value = "/updateMenu")
+    public SaResultRefit updateMenu(@RequestBody SysMenu menu) throws CustomException {
+        try {
+            if (menu.getMenuId() == null) {
+                return SaResultRefit.errorTips("主键为空不能修改!");
+            }
+
+            String menuName = menu.getMenuName().trim();
+            if (RyStringUtils.isEmpty(menuName)) {
+                return SaResultRefit.errorTips("菜单名称不能为空!");
+            } else if (menuName.length() > 50) {
+                return SaResultRefit.errorTips("菜单名长度不能超过50个字符!");
+            }
+
+            if (!"F".equals(menu.getMenuType())) {
+                String routerPath = menu.getPath().trim();
+                if (RyStringUtils.isEmpty(routerPath)) {
+                    return SaResultRefit.errorTips("路由地址不能为空!");
+                } else if (routerPath.length() > 200) {
+                    return SaResultRefit.errorTips("路由地址长度不能超过200个字符!");
+                }
+            }
+            if (menu.getOrderNum() == null) {
+                return SaResultRefit.errorTips("排序不能为空!");
+            } else if (!NumberUtil.isInteger(menu.getOrderNum() + "")) {
+                return SaResultRefit.errorTips("排序不是整型数值!");
+            }
+
+            if (RyStringUtils.isNotEmpty(menu.getComponent())) {
+                if (menu.getComponent().length() > 200) {
+                    return SaResultRefit.errorTips("组件路径长度不能超过200个字符!");
+                }
+            }
+
+            if (RyStringUtils.isNotEmpty(menu.getQuery())) {
+                if (menu.getQuery().length() > 200) {
+                    return SaResultRefit.errorTips("路由参数长度不能超过200个字符!");
+                }
+            }
+
+            if (RyStringUtils.isNotEmpty(menu.getPerms())) {
+                if (menu.getPerms().length() > 100) {
+                    return SaResultRefit.errorTips("权限字符长度不能超过100个字符!");
+                }
+            }
+            if (sysMenuService.selectMenuById(menu.getMenuId()) == null) {
+                return SaResultRefit.errorTips("非法访问不能修改!");
+            }
+            if (UserConstants.NOT_UNIQUE.equals(sysMenuService.checkMenuNameUnique(menu))) {
+                return SaResultRefit.errorTips("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在!");
+            } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !RyStringUtils.ishttp(menu.getPath())) {
+                return SaResultRefit.errorTips("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
+            } else if (menu.getMenuId().equals(menu.getParentId())) {
+                return SaResultRefit.errorTips("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
+            }
+
+            int bo = sysMenuService.updateMenu(menu);
+            if (bo == 1) {
+                return SaResultRefit.ok("修改菜单成功");
+            } else {
+                log.error("修改菜单失败");
+                return SaResultRefit.errorTips("修改菜单失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("修改菜单异常", e);
+        }
+    }
+
+    /**
+     * 根据菜单编号获取详细信息
+     */
+    @GetMapping(value = "/{getDetailInfo}")
+    public SaResultRefit getDetailInfo(String menuId) throws CustomException {
+        try {
+            Long menuid;
+            if (StringUtils.isNotEmpty(menuId)) {
+                if (NumberUtil.isLong(menuId)){
+                    menuid = Long.parseLong(menuId);
+                }
+                else{
+                    return SaResultRefit.errorTips("菜单id不是类型不对!");
+                }
+            }
+            else{
+                return SaResultRefit.errorTips("菜单id不能为空!");
+            }
+            SysMenu sysMenu = sysMenuService.selectMenuById(menuid);
+            return SaResultRefit.data(sysMenu);
+        } catch (Exception e) {
+            throw new CustomException("获取菜单明细异常", e);
+        }
+    }
+
+    /**
+     * 删除菜单信息
+     */
+    @PostMapping(value = "delMenu")
+    public SaResultRefit delMenu(String menuId) throws CustomException {
+        try {
+            if (org.apache.commons.lang3.StringUtils.isEmpty(menuId)) {
+                return SaResultRefit.errorTips("删除菜单的id不能为空!");
+            }
+            if (sysMenuService.hasChildByMenuId(Long.parseLong(menuId))) {
+                return SaResultRefit.errorTips("存在子菜单,不允许删除");
+            }
+
+            if (sysMenuService.checkMenuExistRole(Long.parseLong(menuId))) {
+                return SaResultRefit.errorTips("菜单已分配,不允许删除");
+            }
+            int bo = sysMenuService.deleteMenuById(Long.parseLong(menuId));
+            if (bo == 1) {
+                return SaResultRefit.ok("删除菜单成功");
+            } else {
+                log.error("删除菜单失败");
+                return SaResultRefit.errorTips("删除菜单失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("删除菜单异常", e);
+        }
+    }
+
+    /**
+     * 获取菜单下拉树列表
+     */
+    @GetMapping("/treeselect")
+    public SaResultRefit treeselect(String pc) throws CustomException {
+        try {
+            SysMenu menu = new SysMenu();
+            // 查询出所有菜单
+            List<SysMenu> menus = sysMenuService.selectMenuList(menu, 1L);
+            return SaResultRefit.data(sysMenuService.buildMenuTreeSelect(menus,pc));
+        } catch (Exception e) {
+            throw new CustomException("获取菜单下拉树列表异常", e);
+        }
+    }
+
+    /**
+     * 加载对应角色菜单列表树
+     */
+    @GetMapping(value = "/roleMenuTreeselect")
+    public SaResultRefit roleMenuTreeselect(String roleId) throws CustomException {
+        try {
+            Long roleid;
+            if (StringUtils.isNotEmpty(roleId)) {
+                if (NumberUtil.isLong(roleId)){
+                    roleid = Long.parseLong(roleId);
+                }
+                else{
+                    return SaResultRefit.errorTips("角色id不是类型不对!");
+                }
+            }
+            else{
+                return SaResultRefit.errorTips("角色id不能为空!");
+            }
+            SysRole sysRole = sysRoleService.selectRoleById(roleid);
+            if (sysRole==null){
+                return SaResultRefit.errorTips("找不到角色不能查找角色关联树!");
+            }
+
+            List<SysMenu> menus = sysMenuService.selectMenuList(1L);
+            Map<String, Object> map = new HashMap<>();
+            map.put("checkedKeys", sysMenuService.selectMenuListByRoleId(roleid));
+            map.put("menus", sysMenuService.buildMenuTreeSelect(menus,sysRole.getRoleKey()));
+            return SaResultRefit.data(map);
+        } catch (Exception e) {
+            throw new CustomException("获取对应角色菜单列表树异常", e);
+        }
+    }
+    /**
+     * 加载对应角色菜单列表树
+     */
+    @GetMapping(value = "/roleMenuTreeselectForUser")
+    public SaResultRefit roleMenuTreeselectForUser(String roleId) throws CustomException {
+        try {
+            Long roleid;
+            if (StringUtils.isNotEmpty(roleId)) {
+                if (NumberUtil.isLong(roleId)){
+                    roleid = Long.parseLong(roleId);
+                }
+                else{
+                    return SaResultRefit.errorTips("角色id不是类型不对!");
+                }
+            }
+            else{
+                return SaResultRefit.errorTips("角色id不能为空!");
+            }
+            SysRole sysRole = sysRoleService.selectRoleById(roleid);
+            if (sysRole==null){
+                return SaResultRefit.errorTips("找不到角色不能查找角色关联树!");
+            }
+
+            List<SysMenu> menus = sysMenuService.selectMenuList(1L);
+            Map<String, Object> map = new HashMap<>();
+            map.put("checkedKeys", sysMenuService.selectMenuListByRoleId(roleid));
+            // 将获取的菜单过滤出按钮级别
+            List<SysMenu> notMenuTypeList = menus.stream().filter(menu -> !menu.getMenuType().equals("F")).collect(Collectors.toList());
+            map.put("menus", sysMenuService.buildMenuTreeSelect(notMenuTypeList,sysRole.getRoleKey()));
+            return SaResultRefit.data(map);
+        } catch (Exception e) {
+            throw new CustomException("获取对应角色菜单列表树异常", e);
+        }
+    }
+}

+ 260 - 0
backend/src/main/java/com/jiayue/ssi/controller/SysOperlogController.java

@@ -0,0 +1,260 @@
+package com.jiayue.ssi.controller;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.NumberUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.constant.PermissionCharacter;
+import com.jiayue.ssi.entity.SysOperLog;
+import com.jiayue.ssi.entity.SysRole;
+import com.jiayue.ssi.service.SysOperLogService;
+import com.jiayue.ssi.service.SysRoleService;
+import com.jiayue.ssi.util.DateUtils;
+import com.jiayue.ssi.util.ResponseVO;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 操作日志记录
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/sysOperlogController")
+@Slf4j
+public class SysOperlogController {
+    @Autowired
+    SysOperLogService sysOperLogService;
+    @Autowired
+    SysRoleService sysRoleService;
+
+    /**
+     * 获取审计类型
+     */
+    @GetMapping(value = "/getAuditType")
+    public ResponseVO getAuditType() throws CustomException {
+        List<Map> list = new ArrayList<>();
+        try {
+            // 根据当前用户获取角色
+            SysRole sysRole = sysRoleService.selectRoleListByUserId();
+            if (PermissionCharacter.P_SJYW.equals(sysRole.getRoleKey())){
+                // 业务审计员
+                Map<String,String> map = new HashMap<>();
+                map.put("value","1");
+                map.put("label","业务");
+                list.add(map);
+            }
+            else {
+                Map<String,String> map1 = new HashMap<>();
+                map1.put("value","0");
+                map1.put("label","系统");
+                list.add(map1);
+                Map<String,String> map2 = new HashMap<>();
+                map2.put("value","1");
+                map2.put("label","业务");
+                list.add(map2);
+            }
+        } catch (Exception e) {
+            throw new CustomException("获取审计类型异常", e);
+        }
+        return ResponseVO.success(list);
+    }
+
+    /**
+     * 获取列表分页信息
+     *
+     * @return 列表信息
+     */
+    @GetMapping(value = "/getAll")
+    public ResponseVO getAll(String currentPage, String pageSize, String title, String operName, String auditType,
+                             String businessType, String status, String startOperTime, String endOperTime, String sortOrder) throws CustomException {
+        try {
+            Integer cp;
+            if (StringUtils.isNotEmpty(currentPage)) {
+                if (NumberUtil.isInteger(currentPage)){
+                    cp = Integer.parseInt(currentPage);
+                }
+                else{
+                    return ResponseVO.fail("currentPage不是整数!");
+                }
+            }
+            else{
+                return ResponseVO.fail("currentPage不能是空!");
+            }
+
+            Integer ps;
+            if (StringUtils.isNotEmpty(pageSize)) {
+                if (NumberUtil.isInteger(pageSize)){
+                    ps = Integer.parseInt(pageSize);
+                }
+                else{
+                    return ResponseVO.fail("pageSize不是整数!");
+                }
+            }
+            else{
+                return ResponseVO.fail("pageSize不能是空!");
+            }
+            if (StringUtils.isNotEmpty(title)) {
+                if (title.length() > 50) {
+                    return ResponseVO.fail("系统模块长度不能超过50个字符!");
+                }
+            }
+            if (StringUtils.isNotEmpty(operName)) {
+                if (operName.length() > 50) {
+                    return ResponseVO.fail("操作人员长度不能超过50个字符!");
+                }
+            }
+            QueryWrapper<SysOperLog> wrapper = new QueryWrapper<>();
+            if (StringUtils.isNotEmpty(title)) {
+                wrapper.like("title", title);
+            }
+            if (StringUtils.isNotEmpty(operName)) {
+                wrapper.like("oper_name", operName);
+            }
+            if (StringUtils.isNotEmpty(auditType)) {
+                wrapper.eq("audit_type", auditType);
+            }
+            if (StringUtils.isNotEmpty(businessType)) {
+                wrapper.eq("business_type", businessType);
+            }
+            if (StringUtils.isNotEmpty(status)) {
+                wrapper.eq("status", status);
+            }
+            if (StringUtils.isNotEmpty(startOperTime)) {
+                wrapper.ge("oper_time", DateUtils.getDayStartTime(DateUtil.parseDate(startOperTime)));
+            }
+            if (StringUtils.isNotEmpty(endOperTime)) {
+                wrapper.le("oper_time", DateUtils.getDayLastTime(DateUtil.parseDate(endOperTime)));
+            }
+            if (StringUtils.isNotEmpty(sortOrder)) {
+                String[] orders = sortOrder.replaceAll("&","&").split("&");
+                String sortDbField = "";
+                if ("operTime".equals(orders[0])) {
+                    sortDbField = "oper_time";
+                } else if ("title".equals(orders[0])) {
+                    sortDbField = "title";
+                } else if ("operName".equals(orders[0])) {
+                    sortDbField = "oper_name";
+                }
+
+                if ("asc".equals(orders[1])) {
+                    wrapper.orderByAsc(sortDbField);
+                } else {
+                    wrapper.orderByDesc(sortDbField);
+                }
+            }
+            SysRole sysRole = sysRoleService.selectRoleListByUserId();
+            if (PermissionCharacter.P_SJYW.equals(sysRole.getRoleKey())){
+                // 业务审计员
+                wrapper.eq("audit_type", "1");
+            }
+
+            Page<SysOperLog> result = sysOperLogService.page(new Page<>(cp, ps), wrapper);
+            return ResponseVO.success(result);
+        } catch (Exception e) {
+            throw new CustomException("获取操作日志列表异常", e);
+        }
+    }
+
+    /**
+     * 删除登录信息
+     */
+    @PostMapping(value = "/delOperlog")
+    public ResponseVO delOperlog(String operId) throws CustomException {
+        try {
+            if (StringUtils.isEmpty(operId)) {
+                return ResponseVO.fail("id不能为空!");
+            }
+            boolean bo = sysOperLogService.removeOperlogById(Long.parseLong(operId));
+            if (bo) {
+                return ResponseVO.success("操作日志删除成功");
+            } else {
+                log.error("操作日志删除失败");
+                return ResponseVO.fail("操作日志删除失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("操作日志删除异常", e);
+        }
+    }
+
+    /**
+     * 操作日志清空
+     */
+    @PostMapping("/cleanOperLog")
+    public ResponseVO cleanOperLog() throws CustomException {
+        try {
+            boolean bo = sysOperLogService.cleanOperLog();
+            if (bo) {
+                return ResponseVO.success("操作日志清空成功");
+            } else {
+                log.error("操作日志清空失败");
+                return ResponseVO.fail("操作日志清空失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("操作日志清空异常", e);
+        }
+    }
+
+    /**
+     * 操作日志统计分析
+     *
+     * @return 列表信息
+     */
+    @GetMapping(value = "/sysOperlogTotal")
+    public ResponseVO sysOperlogTotal(String startLoginTime, String endLoginTime) throws CustomException {
+        try {
+            QueryWrapper<SysOperLog> wrapper = new QueryWrapper<>();
+            if (StringUtils.isNotEmpty(startLoginTime)) {
+                wrapper.ge("oper_time", DateUtils.getDayStartTime(DateUtil.parseDate(startLoginTime)));
+            }
+            if (StringUtils.isNotEmpty(endLoginTime)) {
+                wrapper.le("oper_time", DateUtils.getDayLastTime(DateUtil.parseDate(endLoginTime)));
+            }
+            List<SysOperLog> sysOperLogList = sysOperLogService.list(wrapper);
+            Map<String, List<SysOperLog>> map = sysOperLogList.stream().collect(Collectors.groupingBy(item -> DateUtil.format(item.getOperTime(), "yyyy-MM-dd")));
+            List<Map<String, String>> resultList = new ArrayList<>();
+            map.forEach((key, value) -> {
+                List<SysOperLog> list = value;
+                Map<String, String> recordMap = new HashMap<>();
+                recordMap.put("day", key);
+                // 系统类型统计
+                List<SysOperLog> sysTypeList = list.stream().filter(sysOperLog -> sysOperLog.getAuditType() == 0).collect(Collectors.toList());
+                recordMap.put("syscount", sysTypeList.size() + "");
+                // 业务类型统计
+                List<SysOperLog> bizTypeList = list.stream().filter(sysOperLog -> sysOperLog.getAuditType() == 1).collect(Collectors.toList());
+                recordMap.put("bizcount", bizTypeList.size() + "");
+                // 操作成功统计
+                List<SysOperLog> successList = list.stream().filter(sysOperLog -> sysOperLog.getStatus() == 0).collect(Collectors.toList());
+                recordMap.put("success", successList.size() + "");
+                // 操作失败统计
+                List<SysOperLog> failList = list.stream().filter(sysOperLog -> sysOperLog.getStatus() == 1).collect(Collectors.toList());
+                recordMap.put("fail", failList.size() + "");
+                // 操作ip个数统计
+                int ips = list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(s -> s.getOperIp()))), ArrayList::new)).size();
+                recordMap.put("ips", ips + "");
+                // 越权访问统计
+                List<SysOperLog> yqfwList = list.stream().filter(sysOperLog -> sysOperLog.getBusinessType() == 2).collect(Collectors.toList());
+                recordMap.put("yqfw", yqfwList.size() + "");
+                // IP地址异常统计
+                List<SysOperLog> ipycList = list.stream().filter(sysOperLog -> sysOperLog.getBusinessType() == 17).collect(Collectors.toList());
+                recordMap.put("ipyc", ipycList.size() + "");
+                resultList.add(recordMap);
+            });
+            Collections.sort(resultList, new Comparator<Map<String, String>>() {
+                @Override
+                public int compare(Map<String, String> o1, Map<String, String> o2) {
+                    return o1.get("day").compareTo(o2.get("day"));
+                }
+            });
+            return ResponseVO.success(resultList);
+        } catch (Exception e) {
+            throw new CustomException("获取操作日志统计列表异常", e);
+        }
+    }
+}

+ 233 - 0
backend/src/main/java/com/jiayue/ssi/controller/SysParameterController.java

@@ -0,0 +1,233 @@
+package com.jiayue.ssi.controller;
+
+import cn.hutool.core.util.NumberUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.jiayue.ssi.constant.CacheConstants;
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.entity.SysParameter;
+import com.jiayue.ssi.service.SysParameterService;
+import com.jiayue.ssi.util.ResponseVO;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 系统参数接口
+ *
+ * @author xsl
+ * @since 2023/03/20
+ */
+@RestController
+@RequestMapping(value = "/sysParameterController")
+@Slf4j
+public class SysParameterController {
+    @Autowired
+    SysParameterService sysParameterService;
+
+    /**
+     * 新增系统参数
+     *
+     * @param sysParameter 参数
+     * @return 执行结果
+     */
+    @PostMapping(value = "/addParameter")
+    public ResponseVO addParameter(@RequestBody SysParameter sysParameter) throws CustomException {
+        try {
+            if (StringUtils.isEmpty(sysParameter.getSysKey()) || StringUtils.isEmpty(sysParameter.getSysKey().trim())) {
+                return ResponseVO.fail("参数名不能为空!");
+            } else {
+                String sysKey = sysParameter.getSysKey().trim();
+                if (sysKey.contains(" ")){
+                    return ResponseVO.fail("参数名不能含有空格!");
+                }
+                if (!"null".equals(sysParameterService.queryByKey(sysKey, "null"))) {
+                    return ResponseVO.fail(sysKey + "参数名已存在!");
+                } else if (sysKey.length() > 50) {
+                    return ResponseVO.fail("参数名长度不能超过50个字符!");
+                }
+            }
+
+            if (StringUtils.isEmpty(sysParameter.getSysValue())) {
+                return ResponseVO.fail("参数值不能为空!");
+            } else if (sysParameter.getSysValue().length() > 50) {
+                return ResponseVO.fail("参数值长度不能超过50个字符!");
+            }
+
+            if (StringUtils.isEmpty(sysParameter.getSysDescribe())) {
+                return ResponseVO.fail("参数描述不能为空!");
+            } else if (sysParameter.getSysDescribe().length() > 200) {
+                return ResponseVO.fail("参数描述长度不能超过200个字符!");
+            }
+            boolean bo = sysParameterService.save(sysParameter);
+            if (bo) {
+                if ("useSendMail".equals(sysParameter.getSysKey())) {
+                    CacheConstants.use_send_mail = Boolean.parseBoolean(sysParameter.getSysValue());
+                }
+                return ResponseVO.success("添加参数信息成功");
+            } else {
+                log.error("添加参数信息失败");
+                return ResponseVO.fail("添加参数信息失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("添加参数信息异常", e);
+        }
+    }
+
+    /**
+     * 更新系统参数
+     *
+     * @param sysParameter 参数
+     * @return 执行结果
+     */
+    @PostMapping(value = "/updateParameter")
+    public ResponseVO updateParameter(@RequestBody SysParameter sysParameter) throws CustomException {
+        try {
+            SysParameter existSysParameter = sysParameterService.getById(sysParameter.getId());
+            if (existSysParameter == null) {
+                return ResponseVO.fail("非法访问不能修改!");
+            }
+
+            if (StringUtils.isEmpty(sysParameter.getSysKey()) || StringUtils.isEmpty(sysParameter.getSysKey().trim())) {
+                return ResponseVO.fail("参数名不能为空!");
+            } else {
+                String sysKey = sysParameter.getSysKey().trim();
+                if (sysKey.contains(" ")){
+                    return ResponseVO.fail("参数名不能含有空格!");
+                }
+                if (sysKey.length() > 50) {
+                    return ResponseVO.fail("参数名长度不能超过50个字符!");
+                } else if (!existSysParameter.getSysKey().equals(sysKey)) {
+                    if (!"null".equals(sysParameterService.queryByKey(sysKey, "null"))) {
+                        return ResponseVO.fail(sysKey + "参数名已存在!");
+                    }
+                }
+            }
+
+            if (StringUtils.isEmpty(sysParameter.getSysValue())) {
+                return ResponseVO.fail("参数值不能为空!");
+            } else if (sysParameter.getSysValue().length() > 50) {
+                return ResponseVO.fail("参数值长度不能超过50个字符!");
+            }
+
+            if (StringUtils.isEmpty(sysParameter.getSysDescribe())) {
+                return ResponseVO.fail("参数描述不能为空!");
+            } else if (sysParameter.getSysDescribe().length() > 200) {
+                return ResponseVO.fail("参数描述长度不能超过200个字符!");
+            }
+
+            SysParameter oldSysParameter = sysParameterService.getById(sysParameter.getId());
+            if (sysParameter.getVersion().intValue() != oldSysParameter.getVersion().intValue()){
+                return ResponseVO.fail("此数据被操作过,自动刷新列表后请重试操作!");
+            }
+
+            boolean bo = sysParameterService.updateById(sysParameter);
+            if (bo) {
+                if ("useSendMail".equals(sysParameter.getSysKey())) {
+                    CacheConstants.use_send_mail = Boolean.parseBoolean(sysParameter.getSysValue());
+                }
+                return ResponseVO.success("修改参数信息成功");
+            } else {
+                log.error("修改参数信息失败");
+                return ResponseVO.fail("修改参数信息失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("修改参数信息异常", e);
+        }
+    }
+
+    /**
+     * 删除用户信息
+     */
+    @PostMapping(value = "/deleteParameter")
+    public ResponseVO deleteParameter(String id) throws CustomException {
+        try {
+            if (StringUtils.isEmpty(id)) {
+                return ResponseVO.fail("id不能为空!");
+            }
+            SysParameter sysParameter = sysParameterService.getById(Integer.parseInt(id));
+            if (sysParameter == null) {
+                return ResponseVO.fail("此数据被操作过,自动刷新列表后请重试操作!");
+            }
+
+            if ("useSendMail".equals(sysParameter.getSysKey())) {
+                CacheConstants.use_send_mail = true;
+            }
+            boolean bo = sysParameterService.removeById(Integer.parseInt(id));
+            if (bo) {
+                return ResponseVO.success("删除参数信息成功");
+            } else {
+                log.error("删除用户信息失败");
+                return ResponseVO.fail("删除参数信息失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("删除参数信息异常", e);
+        }
+    }
+
+    /**
+     * 获取参数分页信息
+     *
+     * @return 参数信息
+     */
+    @GetMapping(value = "/getAll")
+    public ResponseVO getAll(String currentPage, String pageSize, String keywords) throws CustomException {
+        try {
+            Integer cp;
+            if (StringUtils.isNotEmpty(currentPage)) {
+                if (NumberUtil.isInteger(currentPage)){
+                    cp = Integer.parseInt(currentPage);
+                }
+                else{
+                    return ResponseVO.fail("currentPage不是整数!");
+                }
+            }
+            else{
+                return ResponseVO.fail("currentPage不能是空!");
+            }
+
+            Integer ps;
+            if (StringUtils.isNotEmpty(pageSize)) {
+                if (NumberUtil.isInteger(pageSize)){
+                    ps = Integer.parseInt(pageSize);
+                }
+                else{
+                    return ResponseVO.fail("pageSize不是整数!");
+                }
+            }
+            else{
+                return ResponseVO.fail("pageSize不能是空!");
+            }
+            if (StringUtils.isNotEmpty(keywords)) {
+                if (keywords.length() > 200) {
+                    return ResponseVO.fail("参数描述长度不能超过200个字符!");
+                }
+            }
+            QueryWrapper<SysParameter> wrapper = new QueryWrapper<>();
+            if (StringUtils.isNotEmpty(keywords)) {
+                wrapper.eq("sys_key", keywords);
+            }
+            Page<SysParameter> result = sysParameterService.page(new Page<>(cp, ps), wrapper);
+            return ResponseVO.success(result);
+        } catch (Exception e) {
+            throw new CustomException("获取参数异常", e);
+        }
+    }
+
+    /**
+     * 获取是否邮箱口令参数
+     *
+     * @return 邮箱口令
+     */
+    @GetMapping(value = "/getUseSendMail")
+    public ResponseVO getUseSendMail() throws CustomException {
+        try {
+            String useSendMail = sysParameterService.queryByKey("useSendMail", "true");
+            return ResponseVO.success(useSendMail);
+        } catch (Exception e) {
+            throw new CustomException("获取邮箱口令参数异常", e);
+        }
+    }
+}

+ 75 - 0
backend/src/main/java/com/jiayue/ssi/controller/SysPolicyController.java

@@ -0,0 +1,75 @@
+package com.jiayue.ssi.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.entity.SysPolicy;
+import com.jiayue.ssi.service.SysPolicyService;
+import com.jiayue.ssi.util.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 策略配置接口
+ *
+ * @author xsl
+ * @since 2023/03/13
+ */
+@RestController
+@RequestMapping("/sysPolicyController")
+@Slf4j
+public class SysPolicyController {
+    @Autowired
+    SysPolicyService sysPolicyService;
+
+    /**
+     * 获取策略配置
+     *
+     * @return 用户信息
+     */
+    @GetMapping(value = "/getAll")
+    public SaResultRefit getAll() throws CustomException {
+        try {
+            SysPolicy sysPolicy = sysPolicyService.getOne(new QueryWrapper<>());
+            return SaResultRefit.data(sysPolicy);
+        } catch (Exception e) {
+            throw new CustomException("获取策略配置信息异常", e);
+        }
+    }
+
+    /**
+     * 保存策略配置
+     */
+    @PostMapping
+    public SaResultRefit update(@RequestBody SysPolicy sysPolicy) throws CustomException {
+        try {
+            if (sysPolicy.getLoginFails() == null) {
+                return SaResultRefit.errorTips("登录失败次数限制不能为空!");
+            } else if (!String.valueOf(sysPolicy.getLoginFails()).matches("^([1-9]|10)$")) {
+                return SaResultRefit.errorTips("登录失败次数限制请输入1-10整数");
+            }
+
+            if (StringUtils.isEmpty(sysPolicy.getPasswordRule())) {
+                return SaResultRefit.errorTips("密码规则不能为空!");
+            } else if (sysPolicy.getPasswordRule().split(",").length < 3) {
+                return SaResultRefit.errorTips("密码规则至少需要3种以上的组合!");
+            }
+
+            if (sysPolicy.getLoginLock() == null) {
+                return SaResultRefit.errorTips("登录失败锁定时长不能为空!");
+            } else if (!String.valueOf(sysPolicy.getLoginLock()).matches("^(?:[2-9]\\d|100)$")) {
+                return SaResultRefit.errorTips("登录失败锁定时长请输入20-100整数");
+            }
+
+            boolean bo = sysPolicyService.saveOrUpdate(sysPolicy);
+            if (bo) {
+                return SaResultRefit.ok("策略配置保存成功");
+            } else {
+                return SaResultRefit.errorTips("策略配置保存失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("策略配置保存异常", e);
+        }
+    }
+}

+ 293 - 0
backend/src/main/java/com/jiayue/ssi/controller/SysRoleController.java

@@ -0,0 +1,293 @@
+package com.jiayue.ssi.controller;
+
+import cn.hutool.core.util.NumberUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.constant.PermissionCharacter;
+import com.jiayue.ssi.constant.UserConstants;
+import com.jiayue.ssi.entity.SysRole;
+import com.jiayue.ssi.service.SysRoleService;
+import com.jiayue.ssi.util.ResponseVO;
+import com.jiayue.ssi.util.SaResultRefit;
+import com.jiayue.ssi.util.SecurityContextUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+
+/**
+ * 角色接口
+ *
+ * @author xsl
+ * @since 2023/03/21
+ */
+@RestController
+@RequestMapping("/sysRoleController")
+@Slf4j
+public class SysRoleController {
+    @Autowired
+    SysRoleService roleService;
+
+    /**
+     * 获取参数分页信息
+     *
+     * @return 参数信息
+     */
+    @GetMapping(value = "/getAll")
+    public SaResultRefit getAll(String currentPage, String pageSize, String roleName, String status, String roleKey) throws CustomException {
+        try {
+            Integer cp;
+            if (StringUtils.isNotEmpty(currentPage)) {
+                if (NumberUtil.isInteger(currentPage)){
+                    cp = Integer.parseInt(currentPage);
+                }
+                else{
+                    return SaResultRefit.errorTips("currentPage不是整数!");
+                }
+            }
+            else{
+                return SaResultRefit.errorTips("currentPage不能是空!");
+            }
+
+            Integer ps;
+            if (StringUtils.isNotEmpty(pageSize)) {
+                if (NumberUtil.isInteger(pageSize)){
+                    ps = Integer.parseInt(pageSize);
+                }
+                else{
+                    return SaResultRefit.errorTips("pageSize不是整数!");
+                }
+            }
+            else{
+                return SaResultRefit.errorTips("pageSize不能是空!");
+            }
+            if (StringUtils.isNotEmpty(roleName)) {
+                if (roleName.length() > 15) {
+                    return SaResultRefit.errorTips("角色名称不能超过15个字符!");
+                }
+            }
+            if (StringUtils.isNotEmpty(roleKey)) {
+                if (roleKey.length() > 50) {
+                    return SaResultRefit.errorTips("角色权限不能超过50个字符!");
+                }
+            }
+            QueryWrapper<SysRole> wrapper = new QueryWrapper<>();
+            if (StringUtils.isNotEmpty(roleName)) {
+                wrapper.like("role_name", roleName);
+            }
+            if (StringUtils.isNotEmpty(status)) {
+                wrapper.eq("status", status);
+            }
+            if (StringUtils.isNotEmpty(roleKey)) {
+                wrapper.like("role_key", roleKey);
+            }
+            wrapper.orderByAsc("role_sort");
+
+            // 获取当前操作用户的角色
+            SysRole sysRole = roleService.selectRoleListByUserId();
+            if (!PermissionCharacter.P_XTGL.equals(sysRole.getRoleKey())){
+                // 不是系统管理员角色,只查询业务类角色
+                wrapper.eq("role_type","1");
+            }
+
+            Page<SysRole> result = roleService.page(new Page<>(cp, ps), wrapper);
+            return SaResultRefit.data(result);
+        } catch (Exception e) {
+            throw new CustomException("获取角色列表异常", e);
+        }
+    }
+
+    /**
+     * 新增角色
+     *
+     * @param role 角色
+     * @return 执行结果
+     */
+    @PostMapping(value = "/addRole")
+    public SaResultRefit addRole(@RequestBody SysRole role) throws CustomException {
+        try {
+            if (StringUtils.isEmpty(role.getRoleName()) || StringUtils.isEmpty(role.getRoleName().trim())) {
+                return SaResultRefit.errorTips("角色名称不能为空!");
+            } else {
+                String roleName = role.getRoleName().trim();
+                if (roleName.length() > 15) {
+                    return SaResultRefit.errorTips("角色名称不能超过15个字符!");
+                }
+            }
+            if (StringUtils.isEmpty(role.getRoleKey())) {
+                return SaResultRefit.errorTips("角色权限不能为空!");
+            } else if (role.getRoleKey().length() > 50) {
+                return SaResultRefit.errorTips("角色权限不能超过50个字符!");
+            } else if ("XTGL".equals(role.getRoleKey())) {
+                return SaResultRefit.errorTips("内置角色不能再次添加!");
+            }
+            if (role.getRoleSort() == null) {
+                return SaResultRefit.errorTips("角色排序不能为空!");
+            }
+            if (StringUtils.isEmpty(role.getStatus())) {
+                return SaResultRefit.errorTips("角色状态不能为空!");
+            }
+            if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) {
+                return SaResultRefit.errorTips("新增角色'" + role.getRoleName() + "'失败,"+role.getRoleKey()+"字符标识已存在!");
+            }
+            if (StringUtils.isNotEmpty(role.getRemark())) {
+                if (role.getRemark().length() > 250) {
+                    return SaResultRefit.errorTips("备注不能超过250个字符!");
+                }
+            }
+
+            role.setCreateBy("");
+            int i = roleService.insertRole(role);
+            if (i > 0) {
+                return SaResultRefit.ok("添加角色成功");
+            } else {
+                log.error("添加角色失败");
+                return SaResultRefit.errorTips("添加角色失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("添加角色异常", e);
+        }
+    }
+
+    /**
+     * 更新角色
+     *
+     * @param role 参数
+     * @return 执行结果
+     */
+    @PostMapping(value = "/updateRole")
+    public SaResultRefit updateRole(@RequestBody SysRole role) throws CustomException {
+        try {
+            SysRole existRole = roleService.getById(role.getRoleId());
+            if (existRole == null) {
+                return SaResultRefit.errorTips("没有角色,不能修改!");
+            }
+
+            if (StringUtils.isEmpty(role.getRoleName()) || StringUtils.isEmpty(role.getRoleName().trim())) {
+                return SaResultRefit.errorTips("角色名称不能为空!");
+            } else {
+                String roleName = role.getRoleName().trim();
+                if (roleName.length() > 15) {
+                    return SaResultRefit.errorTips("角色名称不能超过15个字符!");
+                }
+            }
+            if (StringUtils.isEmpty(role.getRoleKey())) {
+                return SaResultRefit.errorTips("角色权限不能为空!");
+            } else if (role.getRoleKey().length() > 50) {
+                return SaResultRefit.errorTips("角色权限不能超过50个字符!");
+            }
+
+            if (role.getRoleSort() == null) {
+                return SaResultRefit.errorTips("角色排序不能为空!");
+            }
+            if (StringUtils.isEmpty(role.getStatus())) {
+                return SaResultRefit.errorTips("角色状态不能为空!");
+            }
+            if (!existRole.getRoleName().equals(role.getRoleName())) {
+                if (roleService.queryRoleName(role.getRoleName()) != null) {
+                    return SaResultRefit.errorTips(role.getRoleName() + "角色名称已存在!");
+                }
+            }
+            if (!existRole.getRoleKey().equals(role.getRoleKey())) {
+                if (roleService.queryRoleKey(role.getRoleKey()) != null) {
+                    return SaResultRefit.errorTips(role.getRoleKey() + "角色权限已存在!");
+                }
+            }
+
+            if (role.getRemark().length() > 250) {
+                return SaResultRefit.errorTips("备注不能超过250个字符!");
+            }
+
+            if (roleService.updateRole(role) > 0) {
+                return SaResultRefit.ok("修改角色信息成功");
+            } else {
+                log.error("修改用户信息失败");
+                return SaResultRefit.errorTips("修改角色信息失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("修改角色信息异常", e);
+        }
+    }
+
+    /**
+     * 根据角色编号获取详细信息
+     */
+    @GetMapping(value = "getInfo")
+    public SaResultRefit getInfo(String roleId) throws CustomException {
+        try {
+            Long roleid;
+            if (StringUtils.isNotEmpty(roleId)) {
+                if (NumberUtil.isLong(roleId)){
+                    roleid = Long.parseLong(roleId);
+                }
+                else{
+                    return SaResultRefit.errorTips("角色id不是类型不对!");
+                }
+            }
+            else{
+                return SaResultRefit.errorTips("角色id不能为空!");
+            }
+
+            return SaResultRefit.data(roleService.selectRoleById(roleid));
+        } catch (Exception e) {
+            throw new CustomException("角色编号获取异常", e);
+        }
+    }
+
+    /**
+     * 删除角色
+     */
+    @PostMapping(value = "/delRole")
+    public SaResultRefit delRole(String roleId) throws CustomException {
+        try {
+            if (StringUtils.isEmpty(roleId)) {
+                return SaResultRefit.errorTips("id不能为空!");
+            }
+            SysRole existRole = roleService.getById(Long.parseLong(roleId));
+            if (existRole == null) {
+                return SaResultRefit.errorTips("没有角色,不能修改!");
+            }
+            else if ("XTGL".equals(existRole.getRoleKey())){
+                return SaResultRefit.errorTips("内置角色不能删除!");
+            }
+
+            if (roleService.countUserRoleByRoleId(Long.parseLong(roleId))>0) {
+                return SaResultRefit.errorTips("角色已被用户使用,不允许删除");
+            }
+            int bo = roleService.deleteRoleById(Long.parseLong(roleId));
+            if (bo > 0) {
+                return SaResultRefit.ok("删除角色信息成功");
+            } else {
+                log.error("删除用户信息失败");
+                return SaResultRefit.errorTips("删除角色信息失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("删除角色信息异常", e);
+        }
+    }
+
+    /**
+     * 获取分配角色信息
+     *
+     * @return
+     */
+    @GetMapping(value = "/getRoleByType")
+    public SaResultRefit getRoleByType(String usertype) throws CustomException {
+        try {
+            if (StringUtils.isEmpty(usertype)) {
+                return SaResultRefit.errorTips("用户类型为空不能获取角色列表!");
+            }
+            QueryWrapper<SysRole> wrapper = new QueryWrapper<>();
+            wrapper.eq("role_type", usertype);
+            List<SysRole> result = roleService.list(wrapper);
+            return SaResultRefit.data(result);
+        } catch (Exception e) {
+            throw new CustomException("获取分配角色列表异常", e);
+        }
+    }
+}

+ 512 - 0
backend/src/main/java/com/jiayue/ssi/controller/SysUserController.java

@@ -0,0 +1,512 @@
+package com.jiayue.ssi.controller;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.util.SaResult;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.Validator;
+import cn.hutool.core.util.IdcardUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.crypto.SmUtil;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.jiayue.ssi.annotation.AgainVerify;
+import com.jiayue.ssi.backenum.ApproveOperaterEnum;
+import com.jiayue.ssi.backenum.ApproveStatusEnum;
+import com.jiayue.ssi.constant.ApproveConstants;
+import com.jiayue.ssi.constant.CacheConstants;
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.constant.SecretKeyConstants;
+import com.jiayue.ssi.entity.*;
+import com.jiayue.ssi.service.*;
+import com.jiayue.ssi.service.impl.SysPermissionService;
+import com.jiayue.ssi.util.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 用户信息接口
+ *
+ * @author xsl
+ * @since 2023/03/13
+ */
+@RestController
+@RequestMapping("/sysUserController")
+@Slf4j
+public class SysUserController {
+    @Autowired
+    SysUserService sysUserService;
+    @Autowired
+    SysPermissionService sysPermissionService;
+    @Autowired
+    SysUserRoleService sysUserRoleService;
+    @Autowired
+    SysApproveService sysApproveService;
+    @Autowired
+    SysRoleService sysRoleService;
+    @Autowired
+    SysPolicyService sysPolicyService;
+
+    /**
+     * 获取用户分页信息
+     *
+     * @return 用户信息
+     */
+    @GetMapping(value = "/getAll")
+    public SaResultRefit getAll(String username, String phonenumber,
+                             String status) throws CustomException {
+        try {
+            if (StringUtils.isNotEmpty(username)) {
+                if (username.length() > 20) {
+                    return SaResultRefit.errorTips("用户账号长度不能超过20个字符!");
+                }
+            }
+            if (StringUtils.isNotEmpty(phonenumber)) {
+                if (phonenumber.length() > 11) {
+                    return SaResultRefit.errorTips("手机号码长度不能超过11个字符!");
+                }
+            }
+
+            // 查询全部
+            List<SysUser> sysUserList = sysUserService.queryAllUserByDecrypt();
+
+            // 根据查询条件过滤结果
+            if (StringUtils.isNotEmpty(username)) {
+                sysUserList =  sysUserList.stream().filter(user -> username.equals(user.getUsername())).collect(Collectors.toList());
+            }
+            if (StringUtils.isNotEmpty(phonenumber)) {
+                sysUserList =  sysUserList.stream().filter(user -> phonenumber.equals(user.getPhonenumber())).collect(Collectors.toList());
+            }
+            if (StringUtils.isNotEmpty(status)) {
+                sysUserList =  sysUserList.stream().filter(user -> status.equals(user.getStatus())).collect(Collectors.toList());
+            }
+
+            return SaResultRefit.data(sysUserList);
+        } catch (Exception e) {
+            throw new CustomException("获取所有用户异常", e);
+        }
+    }
+
+    /**
+     * 获取当前用户信息
+     *
+     * @return 用户信息
+     */
+    @GetMapping(value = "/getCurrentUser")
+    public SaResultRefit getCurrentUser() throws CustomException {
+        try {
+            long userId = Long.parseLong(StpUtil.getLoginId().toString());
+            SysUser sysUser = sysUserService.getById(userId);;
+            // 权限集合
+            Set<String> permissions = sysPermissionService.getMenuPermission(userId);
+            Map<String, Object> map = new HashMap<>();
+            sysUser.setMailbox(SM2CryptUtils.decrypt(sysUser.getMailbox(), SecretKeyConstants.SERVER_PRIVATE_KEY));
+            sysUser.setPhonenumber(SM2CryptUtils.decrypt(sysUser.getPhonenumber(), SecretKeyConstants.SERVER_PRIVATE_KEY));
+            sysUser.setNickname(SM2CryptUtils.decrypt(sysUser.getNickname(), SecretKeyConstants.SERVER_PRIVATE_KEY));
+            sysUser.setIdcard(SM2CryptUtils.decrypt(sysUser.getIdcard(), SecretKeyConstants.SERVER_PRIVATE_KEY));
+            map.put("sysUser", sysUser);
+            map.put("permissions", permissions);
+            return SaResultRefit.data(map);
+        } catch (Exception e) {
+            throw new CustomException("获取当前用户异常", e);
+        }
+    }
+
+    /**
+     * 新增用户
+     */
+    @AgainVerify
+    @PostMapping(value = "/addUser")
+    public SaResultRefit addUser(@RequestBody SysUser user) throws CustomException {
+        try {
+            String username="";
+            SysUser sysUser = new SysUser();
+            if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getUsername().trim())) {
+                return SaResultRefit.errorTips("用户账号不能为空!");
+            }
+            else {
+                username = user.getUsername();
+                if (username.contains(" ")){
+                    return SaResultRefit.errorTips("账号中不能含有空格!");
+                }
+
+                String regExp = "^[^0-9][\\w_]{4,19}$";
+                if (!username.matches(regExp)) {
+                    return SaResultRefit.errorTips("用户账号长度必须是5-20位,只能包含字母、数字和下划线,不能数字开头!");
+                }
+                sysUser.setUsername(username);
+            }
+
+            SysPolicy sysPolicy = sysPolicyService.getOne(new QueryWrapper<>());
+            String ruleInfo = PasswordRuleUtil.ruleRegx(sysPolicy.getPasswordRule(),user.getPassword());
+            if (!"true".equals(ruleInfo)){
+                return SaResultRefit.errorTips(ruleInfo);
+            }
+
+            if (!IdcardUtil.isValidCard(user.getIdcard())) {
+                return SaResultRefit.errorTips("身份证无效!");
+            }
+            // sm2加密身份证
+            sysUser.setIdcard(SM2CryptUtils.encrypt(user.getIdcard(), SecretKeyConstants.SERVER_PUBLIC_KEY));
+
+            if (StringUtils.isEmpty(user.getPhonenumber())) {
+                return SaResultRefit.errorTips("手机号码不能为空!");
+            } else if (!Validator.isMobile(user.getPhonenumber())) {
+                return SaResultRefit.errorTips("请输入正确的手机号码!");
+            }
+            // 加密手机号
+            sysUser.setPhonenumber(SM2CryptUtils.encrypt(user.getPhonenumber(), SecretKeyConstants.SERVER_PUBLIC_KEY));
+
+            if (StringUtils.isEmpty(user.getNickname()) || StringUtils.isEmpty(user.getNickname().trim())) {
+                return SaResultRefit.errorTips("姓名不能为空!");
+            } else {
+                String nickName = user.getNickname().trim();
+                if (nickName.length() > 30) {
+                    return SaResultRefit.errorTips("姓名长度不能超过30个字符!");
+                }
+                // 加密姓名
+                sysUser.setNickname(SM2CryptUtils.encrypt(nickName, SecretKeyConstants.SERVER_PUBLIC_KEY));
+            }
+
+            if (StringUtils.isEmpty(user.getMailbox())) {
+                return SaResultRefit.errorTips("邮箱不能为空!");
+            } else if (!Validator.isEmail(user.getMailbox())) {
+                return SaResultRefit.errorTips("请输入正确的邮箱地址!");
+            } else if (user.getMailbox().length() > 50) {
+                return SaResultRefit.errorTips("邮箱长度不能超过50个字符!");
+            }
+            // 加密邮箱
+            sysUser.setMailbox(SM2CryptUtils.encrypt(user.getMailbox(), SecretKeyConstants.SERVER_PUBLIC_KEY));
+            List<SysUser> sysUserList = sysUserService.queryAllUserByDecrypt();
+            for (SysUser ul:sysUserList){
+                // 判断手机号和邮箱是否存在
+                if (user.getUsername().equals(ul.getUsername())){
+                    return SaResultRefit.errorTips("账号"+user.getUsername() + "存在,不能新增!");
+                }
+                if (user.getIdcard().equals(ul.getIdcard())){
+                    return SaResultRefit.errorTips("身份证"+user.getIdcard() + "存在,不能新增!");
+                }
+                if (user.getPhonenumber().equals(ul.getPhonenumber())){
+                    return SaResultRefit.errorTips("手机号码"+user.getPhonenumber() + "存在,不能新增!");
+                }
+                if (user.getMailbox().equals(ul.getMailbox())){
+                    return SaResultRefit.errorTips("用户邮箱"+user.getMailbox() + "存在,不能新增!");
+                }
+            }
+
+            sysUser.setUsertype(user.getUsertype());
+            sysUser.setStatus("0");
+            sysUser.setSignstr("0");
+            // 新密码加密
+            String sm3newpwd = SmUtil.sm3(user.getPassword()).toUpperCase();
+            // 再次sm2加密
+            String sm2password = SM2CryptUtils.encrypt(sm3newpwd,SecretKeyConstants.SERVER_PUBLIC_KEY);
+            sysUser.setPassword(sm2password);
+            sysUser.setCheckPassword(sm3newpwd);
+            sysUser.setBakPassword(sm2password);
+
+            boolean bo = sysUserService.save(sysUser);
+
+            if (bo) {
+                return SaResultRefit.ok("添加用户信息成功!");
+            } else {
+                return SaResultRefit.errorTips("添加用户信息失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("添加用户信息异常", e);
+        }
+    }
+
+    /**
+     * 更新用户
+     *
+     * @param user 参数
+     * @return 执行结果
+     */
+    @PostMapping(value = "/updateUser")
+    public SaResultRefit updateUser(@RequestBody SysUser user) throws CustomException {
+        try {
+            SysUser existUser = sysUserService.getById(user.getId());
+            if (existUser == null) {
+                return SaResultRefit.errorTips("id为空不能修改!");
+            }
+
+            String username = "";
+            if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getUsername().trim())) {
+                return SaResultRefit.errorTips("用户账号不能为空!");
+            } else{
+                username = user.getUsername().trim();
+                if (username.contains(" ")){
+                    return SaResultRefit.errorTips("账号中不能含有空格!");
+                }
+                String regExp = "^[^0-9][\\w_]{4,19}$";
+                if (!username.matches(regExp)){
+                    return SaResultRefit.errorTips("用户账号长度必须是5-20位,只能包含字母、数字和下划线,不能数字开头!");
+                }
+            }
+
+            if (!IdcardUtil.isValidCard(user.getIdcard())) {
+                return SaResultRefit.errorTips("身份证无效!");
+            }
+            // sm2加密身份证
+            user.setIdcard(SM2CryptUtils.encrypt(user.getIdcard(), SecretKeyConstants.SERVER_PUBLIC_KEY));
+
+            if (StringUtils.isEmpty(user.getMailbox())) {
+                return SaResultRefit.errorTips("邮箱不能为空!");
+            } else if (!Validator.isEmail(user.getMailbox())) {
+                return SaResultRefit.errorTips("请输入正确的邮箱地址!");
+            } else if (user.getMailbox().length() > 50) {
+                return SaResultRefit.errorTips("邮箱长度不能超过50个字符!");
+            }
+            // 加密邮箱
+            user.setMailbox(SM2CryptUtils.encrypt(user.getMailbox(), SecretKeyConstants.SERVER_PUBLIC_KEY));
+
+            if (StringUtils.isEmpty(user.getPhonenumber())) {
+                return SaResultRefit.errorTips("手机号码不能为空!");
+            } else if (!Validator.isMobile(user.getPhonenumber())) {
+                return SaResultRefit.errorTips("请输入正确的手机号码!");
+            }
+            // 加密手机号
+            user.setPhonenumber(SM2CryptUtils.encrypt(user.getPhonenumber(), SecretKeyConstants.SERVER_PUBLIC_KEY));
+
+            if (StringUtils.isEmpty(user.getNickname()) || StringUtils.isEmpty(user.getNickname().trim())) {
+                return SaResultRefit.errorTips("姓名不能为空!");
+            } else {
+                String nickName = user.getNickname().trim();
+                if (nickName.length() > 30) {
+                    return SaResultRefit.errorTips("姓名长度不能超过30个字符!");
+                }
+                // 加密姓名
+                user.setNickname(SM2CryptUtils.encrypt(nickName, SecretKeyConstants.SERVER_PUBLIC_KEY));
+            }
+
+            List<SysUser> sysUserList = sysUserService.queryAllUserByDecrypt();
+            for (SysUser sysUser:sysUserList){
+                // 判断手机号和邮箱是否存在
+                if (user.getPhonenumber().equals(sysUser.getPhonenumber()) && user.getId().longValue()!=sysUser.getId().longValue()){
+                    return SaResultRefit.errorTips("手机号码"+user.getPhonenumber() + "存在,不能修改!");
+                }
+                if (user.getMailbox().equals(sysUser.getMailbox()) && user.getId().longValue()!=sysUser.getId().longValue()){
+                    return SaResultRefit.errorTips("用户邮箱"+user.getMailbox() + "存在,不能修改!");
+                }
+            }
+            // 新密码加密
+            String sm3newpwd = SmUtil.sm3(user.getPassword()).toUpperCase();
+            // 再次sm2加密
+            String sm2password = SM2CryptUtils.encrypt(sm3newpwd,SecretKeyConstants.SERVER_PUBLIC_KEY);
+            user.setPassword(sm2password);
+            user.setCheckPassword(sm3newpwd);
+            user.setBakPassword(sm2password);
+
+            boolean bo = sysUserService.updateUser(user);
+            if (bo) {
+                return SaResultRefit.ok("修改用户信息成功!");
+            } else {
+                return SaResultRefit.errorTips("修改用户信息失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("修改用户信息异常", e);
+        }
+    }
+
+    /**
+     * 修改密码
+     */
+    @PostMapping(value = "/updatePassword")
+    public SaResultRefit updatePassword(String id, String oldPassword, String newPassword, String confirmPassword) throws CustomException {
+        try {
+            oldPassword = oldPassword.trim();
+            newPassword = newPassword.trim();
+            confirmPassword = confirmPassword.trim();
+
+            if (StringUtils.isEmpty(id)) {
+                return SaResultRefit.errorTips("修改密码缺失id!");
+            }
+            // id获取用户
+            SysUser sysUser = sysUserService.getById(id);
+            if (sysUser == null) {
+                return SaResultRefit.errorTips("找不到用户,修改密码失败!");
+            }
+
+            if (newPassword == null) {
+                // 返回密码长度问题
+                return SaResultRefit.errorTips("密码长度需要8~20位之间!");
+            }
+
+            if (StringUtils.isEmpty(oldPassword)) {
+                return SaResultRefit.errorTips("旧密码不能为空!");
+            }
+
+            if (StringUtils.isEmpty(newPassword)) {
+                return SaResultRefit.errorTips("新密码不能为空!");
+            } else if (StringUtils.isEmpty(confirmPassword)) {
+                return SaResultRefit.errorTips("确认密码不能为空!");
+            } else if (!newPassword.equals(confirmPassword)) {
+                return SaResultRefit.errorTips("新密码两次输入的密码不一致!");
+            }
+
+            if (newPassword.contains(" ")){
+                return SaResultRefit.errorTips("新密码中不能含有空格!");
+            }
+
+            // 对新密码规则验证
+            if (newPassword.contains(sysUser.getUsername())) {
+                return SaResultRefit.errorTips("密码不能含有账号!");
+            }
+            String oldpwd = SM2CryptUtils.decrypt(sysUser.getPassword(),SecretKeyConstants.SERVER_PRIVATE_KEY);
+            if (SmUtil.sm3(newPassword).toUpperCase().equals(oldpwd)) {
+                return SaResultRefit.errorTips("新密码不能与上次密码相同!");
+            }
+
+            SysPolicy sysPolicy = sysPolicyService.getOne(new QueryWrapper<>());
+            String ruleInfo = PasswordRuleUtil.ruleRegx(sysPolicy.getPasswordRule(),newPassword);
+            if (!"true".equals(ruleInfo)){
+                return SaResultRefit.errorTips(ruleInfo);
+            }
+
+            // 验证旧密码是否正确
+            if (!oldpwd.equals(SmUtil.sm3(oldPassword).toUpperCase())){
+                return SaResultRefit.errorTips("旧密码不正确!");
+            }
+
+            // 新密码加密
+            String sm3newpwd = SmUtil.sm3(newPassword).toUpperCase();
+            // 再次sm2加密
+            String sm2password = SM2CryptUtils.encrypt(sm3newpwd,SecretKeyConstants.SERVER_PUBLIC_KEY);
+            sysUser.setPassword(sm2password);
+            sysUser.setCheckPassword(sm3newpwd);
+            sysUser.setBakPassword(sm2password);
+            sysUser.setLastUpdatePwdTime(new Date());
+            boolean bo = sysUserService.updateById(sysUser);
+            if (!bo) {
+                return SaResultRefit.errorTips("修改密码失败!");
+            }
+            return SaResultRefit.ok();
+        } catch (Exception e) {
+            throw new CustomException("修改密码异常", e);
+        }
+    }
+
+    /**
+     * 解锁用户信息
+     */
+    @PostMapping(value = "/relockUser")
+    public SaResultRefit relockUser(String id) throws CustomException {
+        try {
+            if (StringUtils.isEmpty(id)) {
+                return SaResultRefit.errorTips("id不能为空!");
+            }
+            // id获取用户
+            SysUser sysUser = sysUserService.getById(id);
+            if (sysUser == null) {
+                return SaResultRefit.errorTips("不能解锁用户!");
+            }
+            if (!"1".equals(sysUser.getStatus())) {
+                return SaResultRefit.errorTips("只能对【锁定】状态的进行解锁!");
+            }
+
+            boolean bo = sysUserService.relockUserById(Integer.parseInt(id));
+            if (bo) {
+                return SaResultRefit.ok("解锁成功");
+            } else {
+                log.error("解锁失败");
+                return SaResultRefit.errorTips("解锁失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("解锁异常", e);
+        }
+    }
+
+    /**
+     * 用户授权角色
+     */
+    @AgainVerify
+    @PostMapping("/authRole")
+    public SaResultRefit authRole(String userId, String roleId) throws CustomException {
+        try {
+            if (StringUtils.isEmpty(userId)) {
+                return SaResultRefit.errorTips("id不能为空!");
+            }
+            // id获取用户
+            SysUser sysUser = sysUserService.getById(userId);
+            if (sysUser == null) {
+                return SaResultRefit.errorTips("id为空不能授权用户!");
+            }
+
+            // id获取用户
+            SysRole sysRole = sysRoleService.getById(roleId);
+            if (sysRole == null) {
+                return SaResultRefit.errorTips("角色id为空不能授权用户!");
+            }
+
+            int i = sysUserService.insertUserAuth(Long.parseLong(userId), Long.parseLong(roleId));
+
+            if (i>0) {
+                return SaResultRefit.ok("授权用户成功!");
+            } else {
+                log.error("授权用户信息失败");
+                return SaResultRefit.errorTips("授权用户信息失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("分配角色异常", e);
+        }
+    }
+
+    /**
+     * 根据用户ID获取角色
+     *
+     * @param userId
+     * @return
+     */
+    @GetMapping("/getUserRole")
+    public SaResultRefit getUserRole(Long userId) throws CustomException {
+        try {
+            if (userId == null) {
+                return SaResultRefit.errorTips("用户id不能为空!");
+            }
+
+            QueryWrapper<SysUserRole> wrapper = new QueryWrapper<>();
+            wrapper.eq("user_id", userId);
+            SysUserRole sysUserRole = sysUserRoleService.getOne(wrapper);
+            return SaResultRefit.data(sysUserRole);
+        } catch (Exception e) {
+            throw new CustomException("获取用户角色异常", e);
+        }
+    }
+
+    /**
+     * 注销用户信息
+     */
+    @AgainVerify
+    @PostMapping(value = "/logOffUser")
+    public SaResultRefit logOffUser(String id) throws CustomException {
+        try {
+            if (StringUtils.isEmpty(id)) {
+                return SaResultRefit.errorTips("id不能为空!");
+            }
+            // id获取用户
+            SysUser sysUser = sysUserService.getById(id);
+            if (sysUser == null) {
+                return SaResultRefit.errorTips("id为空不能删除用户!");
+            }
+
+            sysUser.setStatus("2");
+            boolean bo = sysUserService.updateById(sysUser);
+
+            if (bo) {
+                return SaResultRefit.ok("注销用户成功!");
+            } else {
+                return SaResultRefit.errorTips("注销用户失败");
+            }
+        } catch (Exception e) {
+            throw new CustomException("注销用户异常", e);
+        }
+    }
+}

+ 259 - 0
backend/src/main/java/com/jiayue/ssi/controller/UserLoginController.java

@@ -0,0 +1,259 @@
+package com.jiayue.ssi.controller;
+
+import cn.dev33.satoken.stp.SaTokenInfo;
+import cn.dev33.satoken.stp.StpUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.jiayue.ssi.constant.CacheConstants;
+import com.jiayue.ssi.constant.Constants;
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.entity.SysMenu;
+import com.jiayue.ssi.entity.SysPolicy;
+import com.jiayue.ssi.entity.SysUser;
+import com.jiayue.ssi.factory.LoginFactory;
+import com.jiayue.ssi.service.SysMenuService;
+import com.jiayue.ssi.service.SysPolicyService;
+import com.jiayue.ssi.service.SysUserService;
+import com.jiayue.ssi.service.UmsAdminService;
+import com.jiayue.ssi.util.*;
+import com.wf.captcha.SpecCaptcha;
+import com.wf.captcha.base.Captcha;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 用户登录控制类
+ *
+ * @author xsl
+ * @since 2023/02/20
+ */
+@RestController
+@Slf4j
+public class UserLoginController {
+    @Autowired
+    SysUserService sysUserService;
+    @Autowired
+    SysMenuService sysMenuService;
+    @Autowired
+    SysPolicyService sysPolicyService;
+    @Autowired
+    UmsAdminService umsAdminService;
+
+    /**
+     * 生成验证码
+     *
+     * @param httpServletResponse
+     * @throws IOException
+     */
+    @GetMapping("/getVerifyCode")
+    public SaResultRefit getVerifyCode(String murmur,HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws CustomException {
+        // 三个参数分别为宽、高、位数
+        try {
+            if (murmur==null || murmur.length()!=32){
+                return SaResultRefit.errorTips("不能生成验证码!");
+            }
+            String textcode = "";
+            String base64 = "";
+            while (true) {
+                SpecCaptcha captcha = new SpecCaptcha(150, 40, 4);
+                /**
+                 * 验证码字符类型 TYPE_DEFAULT 数字和字母混合 TYPE_ONLY_NUMBER 纯数字 TYPE_ONLY_CHAR 纯字母 TYPE_ONLY_UPPER 纯大写字母 TYPE_ONLY_LOWER
+                 * 纯小写字母 TYPE_NUM_AND_UPPER 数字和大写字母
+                 **/
+                // 设置类型 数字和字母混合
+                captcha.setCharType(Captcha.TYPE_DEFAULT);
+                // 设置字体
+                captcha.setCharType(Captcha.FONT_9);
+                int digits = 0;
+                char[] text = captcha.textChar();
+                for (int i = 0; i < text.length; i++) {
+                    if (Character.isDigit(text[i])) {
+                        digits++;
+                    }
+                }
+                if (digits == 0 || digits == 4) {
+                    // 重新生成
+                } else {
+                    textcode = String.valueOf(text);
+                    base64 = captcha.toBase64();
+                    break;
+                }
+            }
+
+            String uuid = IdUtils.simpleUUID();
+            String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid+murmur;
+            // uuid存入缓存,失效时间默认5分钟
+            LocalCache.set(verifyKey, textcode);
+            // 输出图片流
+            // captcha.out(httpServletResponse.getOutputStream());
+
+            int index = base64.indexOf(",");
+            String substring = base64.substring(index + 1);
+
+            Map<String, String> dataMap = new HashMap<>(16);
+            dataMap.put("uuid", uuid);
+            dataMap.put("imgBase64", substring);
+            dataMap.put("captchaText", textcode);
+            return SaResultRefit.data(dataMap);
+        } catch (Exception e) {
+            throw new CustomException("生成验证码异常", e);
+        }
+    }
+
+    /**
+     * 刷新token
+     *
+     * @param httpServletRequest
+     * @param httpServletResponse
+     * @throws IOException
+     */
+    @PostMapping("/refreshToken")
+    public ResponseVO refreshToken(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
+            throws CustomException {
+        try {
+            String oldToken = httpServletRequest.getHeader("Authorization");
+            String newToken = "";
+            CacheConstants.LOGIN_TOKEN_MAP.put("", newToken);
+            return ResponseVO.success(newToken);
+        } catch (Exception e) {
+            throw new CustomException("刷新token异常", e);
+        }
+    }
+
+    /**
+     * 获取路由菜单信息
+     *
+     * @return 路由菜单信息
+     */
+    @GetMapping("getRouters")
+    public SaResultRefit getRouters() throws CustomException {
+        try {
+            long userId = Long.parseLong(StpUtil.getLoginId().toString());
+            List<SysMenu> menus;
+            if (userId==1L){
+                // 内置管理员查询所有
+                menus = sysMenuService.selectMenuTreeByUserId(null);
+            }
+            else{
+                menus = sysMenuService.selectMenuTreeByUserId(userId);
+            }
+
+            return SaResultRefit.data(sysMenuService.buildMenus(menus));
+        } catch (Exception e) {
+            throw new CustomException("获取路由菜单信息异常", e);
+        }
+    }
+
+    @PostMapping("/user/login")
+    public SaResultRefit login(HttpServletRequest request,
+                          @RequestParam("username") String username,
+                          @RequestParam("password") String password,
+                          @RequestParam("murmur") String murmur,
+                          @RequestParam("verifyuuid") String verifyuuid,
+                          @RequestParam("code") String code){
+        Object uuidObj = LocalCache.get(CacheConstants.CAPTCHA_CODE_KEY + verifyuuid + murmur);
+        LocalCache.remove(CacheConstants.CAPTCHA_CODE_KEY + verifyuuid + murmur);
+        // 校验服务端验证码
+        if (uuidObj == null || "".equals(uuidObj)) {
+            // 记录验证码失败日志
+            return SaResultRefit.errorTips(401,"验证码错误");
+        }
+        // 校验页面验证码
+        if (StringUtils.isEmpty(code)) {
+            // 记录验证码失败日志
+            return SaResultRefit.errorTips(401,"验证码错误");
+        }
+        if (code.length() != 4) {
+            // 记录验证码失败日志
+            return SaResultRefit.errorTips(401,"验证码错误");
+        }
+        if (!String.valueOf(uuidObj).toLowerCase().equals(code.toLowerCase())) {
+            // 记录验证码失败日志
+            return SaResultRefit.errorTips(401,"验证码错误");
+        }
+
+        SysUser sysUser = sysUserService.queryUserName(username);
+        if (sysUser==null){
+            return SaResultRefit.errorTips(401,"用户名或密码错误");
+        }
+
+        SaTokenInfo saTokenInfo = umsAdminService.login(password,sysUser);
+        if (saTokenInfo==null){
+            // 记录失败次数
+            SysPolicy sysPolicy = sysPolicyService.getOne(new QueryWrapper<>());
+            // 失败总次数
+            int errCount = sysPolicy.getLoginFails();
+            // 锁定时长
+            int lockedTime = sysPolicy.getLoginLock()*1000*60;
+            String errorTips = "";
+            if (sysUser.getStatus().equals("1")) {
+                Long xz = (sysUser.getLockTime() + lockedTime - System.currentTimeMillis()) / 1000;
+                if (xz < 0) {
+                    if (errCount-1==0) {
+                        errorTips = "用户锁定,稍后再试";
+                        sysUser.setErrNum(0);
+                        sysUser.setStatus("1");
+                        sysUser.setLockTime(System.currentTimeMillis());
+                    }
+                    else{
+                        sysUser.setStatus("0");
+                        sysUser.setErrNum(1);
+                        errorTips = "用户名或密码不正确,还有"+(errCount-1)+"次机会";
+                    }
+                    sysUserService.updateUser(sysUser);
+                } else {
+                    //锁定状态
+                    errorTips = "用户已锁定,请隔" + xz + "秒再登录";
+                }
+            } else {
+                int errNum = sysUser.getErrNum();
+                if (errNum < errCount-1) {
+                    errorTips = "用户名或密码不正确,还有" + (errCount-1 - errNum) + "次机会";
+                    errNum++;
+                    sysUser.setErrNum(errNum);
+                    sysUserService.updateUser(sysUser);
+                } else {
+                    errorTips = "用户锁定,稍后再试";
+                    sysUser.setErrNum(0);
+                    sysUser.setStatus("1");
+                    sysUser.setLockTime(System.currentTimeMillis());
+                    sysUserService.updateUser(sysUser);
+                }
+            }
+
+            return SaResultRefit.errorTips(401,errorTips);
+        }
+
+        sysUser.setErrNum(0);
+        sysUser.setLockTime(0L);
+        sysUser.setStatus("0");
+        sysUser.setOnlineStatus("0");
+        // 加入登录IP和时间
+        String ip = IPUtils.getIpAddr(request);
+        sysUser.setLoginIp(ip);
+        sysUser.setLoginDate(new Date());
+        sysUserService.updateUser(sysUser);
+
+        Map<String,String> tokenMap = new HashMap<>();
+        tokenMap.put("token",saTokenInfo.getTokenValue());
+        tokenMap.put("tokenHead",saTokenInfo.getTokenName());
+        return  SaResultRefit.data(tokenMap);
+    }
+
+    // 会话注销
+    @RequestMapping("/logout")
+    public SaResultRefit logout() {
+        StpUtil.logout();
+        return SaResultRefit.ok("退出登录成功");
+    }
+}

+ 19 - 0
backend/src/main/java/com/jiayue/ssi/dto/ActiveUserDto.java

@@ -0,0 +1,19 @@
+package com.jiayue.ssi.dto;
+
+import com.jiayue.ssi.entity.SysUser;
+import lombok.Data;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+* 应用Map<String, HttpServletRequest> ACTIVE_USER的DTO
+*
+* @author xsl
+* @since 2023/08/17
+*/
+@Data
+public class ActiveUserDto {
+    private HttpServletRequest httpServletRequest;
+    private SysUser sysUser;
+    private Long loginTime;
+}

+ 25 - 0
backend/src/main/java/com/jiayue/ssi/dto/UserVisitInfoDto.java

@@ -0,0 +1,25 @@
+package com.jiayue.ssi.dto;
+
+import lombok.Data;
+
+/**
+* 用户访问信息
+*
+* @author xsl
+* @since 2023/06/28
+*/
+@Data
+public class UserVisitInfoDto {
+    /** 账号 */
+    private String username;
+    /** 登录ip */
+    private String ip;
+    /** 访问时间 */
+    private Long vtime;
+    /** 登录地点 */
+    private String loginLocation;
+    /** 客户端浏览器 */
+    private String browser;
+    /** 客户端操作系统 */
+    private String os;
+}

+ 47 - 0
backend/src/main/java/com/jiayue/ssi/entity/BaseEntity.java

@@ -0,0 +1,47 @@
+package com.jiayue.ssi.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+/**
+ * Entity基类
+ *
+ * @author xsl
+ */
+@Data
+public class BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /** 创建者 */
+    @TableField(fill = FieldFill.INSERT)
+    private String createBy;
+
+    /** 创建时间 */
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
+    private Date createTime;
+
+    /** 更新者 */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private String updateBy;
+
+    /** 更新时间 */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
+    private Date updateTime;
+
+    /** 备注 */
+    private String remark;
+
+}

+ 62 - 0
backend/src/main/java/com/jiayue/ssi/entity/ElectricField.java

@@ -0,0 +1,62 @@
+package com.jiayue.ssi.entity;
+
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.Version;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.Digits;
+import java.math.BigDecimal;
+
+/**
+ * 场站表
+ *
+ * @author xsl
+ * @version 3.0
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName(value = "t_electric_field")
+public class ElectricField extends BaseEntity {
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+    /**
+     * 场站编码
+     */
+    private String stationCode;
+    /**
+     * 场站名称
+     */
+    private String name;
+    /**
+     * 场站标识
+     */
+    private String sign;
+    /**
+     * 场站装机容量(MW)
+     */
+    @Digits(integer = 10, fraction = 2)
+    private BigDecimal capacity;
+    /**
+     * 并网设备数
+     */
+    private Integer gridce;
+    /**
+     * 场站类型(0光伏 1风电)
+     */
+    private String fieldType;
+    /**
+     * 场站所属公司
+     */
+    private String company;
+    /**
+     * 场站位置
+     */
+    private String location;
+    //乐观锁
+    @Version
+    private Integer version;
+}

+ 243 - 0
backend/src/main/java/com/jiayue/ssi/entity/Server.java

@@ -0,0 +1,243 @@
+package com.jiayue.ssi.entity;
+
+import com.jiayue.ssi.entity.server.*;
+import java.net.UnknownHostException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+
+import java.net.UnknownHostException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+
+import com.jiayue.ssi.util.Arith;
+import com.jiayue.ssi.util.IPUtils;
+import oshi.SystemInfo;
+import oshi.hardware.CentralProcessor;
+import oshi.hardware.CentralProcessor.TickType;
+import oshi.hardware.GlobalMemory;
+import oshi.hardware.HardwareAbstractionLayer;
+import oshi.software.os.FileSystem;
+import oshi.software.os.OSFileStore;
+import oshi.software.os.OperatingSystem;
+import oshi.util.Util;
+
+
+/**
+ * 服务器相关信息
+ *
+ * @author ruoyi
+ */
+public class Server
+{
+    private static final int OSHI_WAIT_SECOND = 1000;
+
+    /**
+     * CPU相关信息
+     */
+    private Cpu cpu = new Cpu();
+
+    /**
+     * 內存相关信息
+     */
+    private Mem mem = new Mem();
+
+    /**
+     * JVM相关信息
+     */
+    private Jvm jvm = new Jvm();
+
+    /**
+     * 服务器相关信息
+     */
+    private Sys sys = new Sys();
+
+    /**
+     * 磁盘相关信息
+     */
+    private List<SysFile> sysFiles = new LinkedList<SysFile>();
+
+    public Cpu getCpu()
+    {
+        return cpu;
+    }
+
+    public void setCpu(Cpu cpu)
+    {
+        this.cpu = cpu;
+    }
+
+    public Mem getMem()
+    {
+        return mem;
+    }
+
+    public void setMem(Mem mem)
+    {
+        this.mem = mem;
+    }
+
+    public Jvm getJvm()
+    {
+        return jvm;
+    }
+
+    public void setJvm(Jvm jvm)
+    {
+        this.jvm = jvm;
+    }
+
+    public Sys getSys()
+    {
+        return sys;
+    }
+
+    public void setSys(Sys sys)
+    {
+        this.sys = sys;
+    }
+
+    public List<SysFile> getSysFiles()
+    {
+        return sysFiles;
+    }
+
+    public void setSysFiles(List<SysFile> sysFiles)
+    {
+        this.sysFiles = sysFiles;
+    }
+
+    public void copyTo() throws Exception
+    {
+        SystemInfo si = new SystemInfo();
+        HardwareAbstractionLayer hal = si.getHardware();
+
+        setCpuInfo(hal.getProcessor());
+
+        setMemInfo(hal.getMemory());
+
+        setSysInfo();
+
+        setJvmInfo();
+
+        setSysFiles(si.getOperatingSystem());
+    }
+
+    /**
+     * 设置CPU信息
+     */
+    private void setCpuInfo(CentralProcessor processor)
+    {
+        // CPU信息
+        long[] prevTicks = processor.getSystemCpuLoadTicks();
+        Util.sleep(OSHI_WAIT_SECOND);
+        long[] ticks = processor.getSystemCpuLoadTicks();
+        long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];
+        long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];
+        long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];
+        long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];
+        long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];
+        long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];
+        long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];
+        long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];
+        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
+        cpu.setCpuNum(processor.getLogicalProcessorCount());
+        cpu.setTotal(totalCpu);
+        cpu.setSys(cSys);
+        cpu.setUsed(user);
+        cpu.setWait(iowait);
+        cpu.setFree(idle);
+    }
+
+    /**
+     * 设置内存信息
+     */
+    private void setMemInfo(GlobalMemory memory)
+    {
+        mem.setTotal(memory.getTotal());
+        mem.setUsed(memory.getTotal() - memory.getAvailable());
+        mem.setFree(memory.getAvailable());
+    }
+
+    /**
+     * 设置服务器信息
+     */
+    private void setSysInfo()
+    {
+        Properties props = System.getProperties();
+        sys.setComputerName(IPUtils.getHostName());
+        sys.setComputerIp(IPUtils.getHostIp());
+        sys.setOsName(props.getProperty("os.name"));
+        sys.setOsArch(props.getProperty("os.arch"));
+        sys.setUserDir(props.getProperty("user.dir"));
+    }
+
+    /**
+     * 设置Java虚拟机
+     */
+    private void setJvmInfo() throws UnknownHostException
+    {
+        Properties props = System.getProperties();
+        jvm.setTotal(Runtime.getRuntime().totalMemory());
+        jvm.setMax(Runtime.getRuntime().maxMemory());
+        jvm.setFree(Runtime.getRuntime().freeMemory());
+        jvm.setVersion(props.getProperty("java.version"));
+        jvm.setHome(props.getProperty("java.home"));
+    }
+
+    /**
+     * 设置磁盘信息
+     */
+    private void setSysFiles(OperatingSystem os)
+    {
+        FileSystem fileSystem = os.getFileSystem();
+        List<OSFileStore> fsArray = fileSystem.getFileStores();
+        for (OSFileStore fs : fsArray)
+        {
+            long free = fs.getUsableSpace();
+            long total = fs.getTotalSpace();
+            long used = total - free;
+            SysFile sysFile = new SysFile();
+            sysFile.setDirName(fs.getMount());
+            sysFile.setSysTypeName(fs.getType());
+            sysFile.setTypeName(fs.getName());
+            sysFile.setTotal(convertFileSize(total));
+            sysFile.setFree(convertFileSize(free));
+            sysFile.setUsed(convertFileSize(used));
+            sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100));
+            sysFiles.add(sysFile);
+        }
+    }
+
+    /**
+     * 字节转换
+     *
+     * @param size 字节大小
+     * @return 转换后值
+     */
+    public String convertFileSize(long size)
+    {
+        long kb = 1024;
+        long mb = kb * 1024;
+        long gb = mb * 1024;
+        if (size >= gb)
+        {
+            return String.format("%.1f GB", (float) size / gb);
+        }
+        else if (size >= mb)
+        {
+            float f = (float) size / mb;
+            return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f);
+        }
+        else if (size >= kb)
+        {
+            float f = (float) size / kb;
+            return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f);
+        }
+        else
+        {
+            return String.format("%d B", size);
+        }
+    }
+}

+ 94 - 0
backend/src/main/java/com/jiayue/ssi/entity/SysApprove.java

@@ -0,0 +1,94 @@
+package com.jiayue.ssi.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 审批表
+ *
+ * @author xsl
+ * @version 3.0
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName(value = "sys_approve")
+public class SysApprove extends BaseEntity{
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 模块名称
+     */
+    private String moduleName;
+    /**
+     * 操作
+     */
+    private String operation;
+    /**
+     * 审批状态(0待审批 1已审批)
+     */
+    private String approveStatus;
+    /**
+     * 审批结果(0通过 1未通过)
+     */
+    private String approveResult;
+    /**
+     * 实体名称
+     */
+    private String entityName;
+    /**
+     * 实体内容
+     */
+    private String parameterContent;
+    /**
+     * 用户表主键ID号
+     */
+    private String masterId;
+    /**
+     * 用户账号
+     */
+    private String username;
+    /**
+     * 身份证号
+     */
+    private String idcard;
+    /**
+     * 用户姓名
+     */
+    private String nickname;
+    /**
+     * 账号状态(0正常、1锁定(休眠)、2注销)
+     */
+    private String status;
+    /**
+     * 邮箱
+     */
+    private String mailbox;
+    /**
+     * 手机号码
+     */
+    private String phonenumber;
+    /**
+     * 用户类型(0管理员、1业务用户)
+     */
+    private String usertype;
+    /**
+     * 分配角色ID
+     */
+    private String roleId;
+    /**
+     * 账号有效期
+     */
+    @TableField(value = "exp_date",fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+8")
+    private Date expDate;
+
+    //乐观锁
+    @Version
+    private Integer version;
+}

+ 136 - 0
backend/src/main/java/com/jiayue/ssi/entity/SysLogininfor.java

@@ -0,0 +1,136 @@
+package com.jiayue.ssi.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.util.Date;
+
+/**
+ * 系统访问记录表 sys_logininfor
+ *
+ * @author ruoyi
+ */
+public class SysLogininfor extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** ID */
+    @TableId(type = IdType.AUTO)
+    private Long infoId;
+
+    /** 用户账号 */
+    private String userName;
+
+    /** 登录状态 0成功 1失败 */
+    private String status;
+
+    /** 登录IP地址 */
+    private String ipaddr;
+
+    /** 登录地点 */
+    private String loginLocation;
+
+    /** 浏览器类型 */
+    private String browser;
+
+    /** 操作系统 */
+    private String os;
+
+    /** 提示消息 */
+    private String msg;
+
+    /** 访问时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
+    private Date loginTime;
+
+    public Long getInfoId()
+    {
+        return infoId;
+    }
+
+    public void setInfoId(Long infoId)
+    {
+        this.infoId = infoId;
+    }
+
+    public String getUserName()
+    {
+        return userName;
+    }
+
+    public void setUserName(String userName)
+    {
+        this.userName = userName;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    public String getIpaddr()
+    {
+        return ipaddr;
+    }
+
+    public void setIpaddr(String ipaddr)
+    {
+        this.ipaddr = ipaddr;
+    }
+
+    public String getLoginLocation()
+    {
+        return loginLocation;
+    }
+
+    public void setLoginLocation(String loginLocation)
+    {
+        this.loginLocation = loginLocation;
+    }
+
+    public String getBrowser()
+    {
+        return browser;
+    }
+
+    public void setBrowser(String browser)
+    {
+        this.browser = browser;
+    }
+
+    public String getOs()
+    {
+        return os;
+    }
+
+    public void setOs(String os)
+    {
+        this.os = os;
+    }
+
+    public String getMsg()
+    {
+        return msg;
+    }
+
+    public void setMsg(String msg)
+    {
+        this.msg = msg;
+    }
+
+    public Date getLoginTime()
+    {
+        return loginTime;
+    }
+
+    public void setLoginTime(Date loginTime)
+    {
+        this.loginTime = loginTime;
+    }
+}

+ 295 - 0
backend/src/main/java/com/jiayue/ssi/entity/SysMenu.java

@@ -0,0 +1,295 @@
+package com.jiayue.ssi.entity;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 菜单权限表
+ *
+ * @author xsl
+ * @version 3.0
+ */
+public class SysMenu extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /** 菜单ID */
+    @TableId(type = IdType.AUTO)
+    private Long menuId;
+
+    /** 菜单名称 */
+    private String menuName;
+
+    /** 父菜单名称 */
+    private String parentName;
+
+    /** 父菜单ID */
+    private Long parentId;
+
+    /** 显示顺序 */
+    private Integer orderNum;
+
+    /** 路由地址 */
+    private String path;
+
+    /** 组件路径 */
+    private String component;
+
+    public String getPc() {
+        return pc;
+    }
+
+    public void setPc(String pc) {
+        this.pc = pc;
+    }
+
+    /** 字符标识 */
+    private String pc;
+
+    /** 路由参数 */
+    private String query;
+
+    /** 是否为外链(0是 1否) */
+    private String isFrame;
+
+    /** 是否缓存(0缓存 1不缓存) */
+    private String isCache;
+
+    /** 类型(M目录 C菜单 F按钮) */
+    private String menuType;
+
+    /** 显示状态(0显示 1隐藏) */
+    private String visible;
+
+    /** 菜单状态(0正常 1停用) */
+    private String status;
+
+    /** 权限字符串 */
+    private String perms;
+
+    /** 菜单图标 */
+    private String icon;
+
+    /** 请求参数 */
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    private Map<String, Object> params;
+
+    /** 子菜单 */
+    private List<SysMenu> children = new ArrayList<SysMenu>();
+
+    public Long getMenuId()
+    {
+        return menuId;
+    }
+
+    public void setMenuId(Long menuId)
+    {
+        this.menuId = menuId;
+    }
+
+    @NotBlank(message = "菜单名称不能为空")
+    @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
+    public String getMenuName()
+    {
+        return menuName;
+    }
+
+    public void setMenuName(String menuName)
+    {
+        this.menuName = menuName;
+    }
+
+    public String getParentName()
+    {
+        return parentName;
+    }
+
+    public void setParentName(String parentName)
+    {
+        this.parentName = parentName;
+    }
+
+    public Long getParentId()
+    {
+        return parentId;
+    }
+
+    public void setParentId(Long parentId)
+    {
+        this.parentId = parentId;
+    }
+
+    @NotNull(message = "显示顺序不能为空")
+    public Integer getOrderNum()
+    {
+        return orderNum;
+    }
+
+    public void setOrderNum(Integer orderNum)
+    {
+        this.orderNum = orderNum;
+    }
+
+    @Size(min = 0, max = 200, message = "路由地址不能超过200个字符")
+    public String getPath()
+    {
+        return path;
+    }
+
+    public void setPath(String path)
+    {
+        this.path = path;
+    }
+
+    @Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
+    public String getComponent()
+    {
+        return component;
+    }
+
+    public void setComponent(String component)
+    {
+        this.component = component;
+    }
+
+    public String getQuery()
+    {
+        return query;
+    }
+
+    public void setQuery(String query)
+    {
+        this.query = query;
+    }
+
+    public String getIsFrame()
+    {
+        return isFrame;
+    }
+
+    public void setIsFrame(String isFrame)
+    {
+        this.isFrame = isFrame;
+    }
+
+    public String getIsCache()
+    {
+        return isCache;
+    }
+
+    public void setIsCache(String isCache)
+    {
+        this.isCache = isCache;
+    }
+
+    @NotBlank(message = "菜单类型不能为空")
+    public String getMenuType()
+    {
+        return menuType;
+    }
+
+    public void setMenuType(String menuType)
+    {
+        this.menuType = menuType;
+    }
+
+    public String getVisible()
+    {
+        return visible;
+    }
+
+    public void setVisible(String visible)
+    {
+        this.visible = visible;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
+    public String getPerms()
+    {
+        return perms;
+    }
+
+    public void setPerms(String perms)
+    {
+        this.perms = perms;
+    }
+
+    public String getIcon()
+    {
+        return icon;
+    }
+
+    public void setIcon(String icon)
+    {
+        this.icon = icon;
+    }
+
+    public List<SysMenu> getChildren()
+    {
+        return children;
+    }
+
+    public void setChildren(List<SysMenu> children)
+    {
+        this.children = children;
+    }
+
+    public Map<String, Object> getParams()
+    {
+        if (params == null)
+        {
+            params = new HashMap<>();
+        }
+        return params;
+    }
+
+    public void setParams(Map<String, Object> params)
+    {
+        this.params = params;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("menuId", getMenuId())
+            .append("menuName", getMenuName())
+            .append("parentId", getParentId())
+            .append("orderNum", getOrderNum())
+            .append("path", getPath())
+            .append("component", getComponent())
+            .append("isFrame", getIsFrame())
+            .append("IsCache", getIsCache())
+            .append("menuType", getMenuType())
+            .append("visible", getVisible())
+            .append("status ", getStatus())
+            .append("perms", getPerms())
+            .append("icon", getIcon())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .append("remark", getRemark())
+            .toString();
+    }
+}

+ 276 - 0
backend/src/main/java/com/jiayue/ssi/entity/SysOperLog.java

@@ -0,0 +1,276 @@
+package com.jiayue.ssi.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.util.Date;
+
+/**
+ * 操作日志记录表 oper_lo
+ *
+ * @author ruoyi
+ */
+public class SysOperLog extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /** 日志主键 */
+    @TableId(type = IdType.AUTO)
+    private Long operId;
+
+    /** 操作模块 */
+    private String title;
+
+    /** 业务类型(0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据) */
+    private Integer businessType;
+
+    /** 审计类型(0=系统,1=业务) */
+    private Integer auditType;
+
+    /** 业务类型数组 */
+    @TableField(exist = false)
+    private Integer[] businessTypes;
+
+    public String getOperdesc() {
+        return operdesc;
+    }
+
+    public void setOperdesc(String operdesc) {
+        this.operdesc = operdesc;
+    }
+
+    /** 操作描述 */
+    private String operdesc;
+
+    /** 请求方法 */
+    private String method;
+
+    /** 请求方式 */
+    private String requestMethod;
+
+    /** 操作类别(0其它 1后台用户 2手机端用户) */
+    private Integer operatorType;
+
+    /** 操作人员 */
+    private String operName;
+
+    /** 部门名称 */
+    private String deptName;
+
+    /** 请求url */
+    private String operUrl;
+
+    /** 操作地址 */
+    private String operIp;
+
+    /** 操作地点 */
+    private String operLocation;
+
+    /** 请求参数 */
+    private String operParam;
+
+    /** 返回参数 */
+//    private String jsonResult;
+
+    /** 操作状态(0正常 1异常) */
+    private Integer status;
+
+    /** 错误消息 */
+    private String errorMsg;
+
+    /** 操作时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
+    private Date operTime;
+
+    /** 消耗时间 毫秒*/
+    private Long costTime;
+
+    public Long getOperId()
+    {
+        return operId;
+    }
+
+    public void setOperId(Long operId)
+    {
+        this.operId = operId;
+    }
+
+    public String getTitle()
+    {
+        return title;
+    }
+
+    public void setTitle(String title)
+    {
+        this.title = title;
+    }
+
+    public Integer getBusinessType()
+    {
+        return businessType;
+    }
+
+    public void setBusinessType(Integer businessType)
+    {
+        this.businessType = businessType;
+    }
+
+    public Integer[] getBusinessTypes()
+    {
+        return businessTypes;
+    }
+
+    public void setBusinessTypes(Integer[] businessTypes)
+    {
+        this.businessTypes = businessTypes;
+    }
+
+    public String getMethod()
+    {
+        return method;
+    }
+
+    public void setMethod(String method)
+    {
+        this.method = method;
+    }
+
+    public String getRequestMethod()
+    {
+        return requestMethod;
+    }
+
+    public void setRequestMethod(String requestMethod)
+    {
+        this.requestMethod = requestMethod;
+    }
+
+    public Integer getOperatorType()
+    {
+        return operatorType;
+    }
+
+    public void setOperatorType(Integer operatorType)
+    {
+        this.operatorType = operatorType;
+    }
+
+    public String getOperName()
+    {
+        return operName;
+    }
+
+    public void setOperName(String operName)
+    {
+        this.operName = operName;
+    }
+
+    public String getDeptName()
+    {
+        return deptName;
+    }
+
+    public void setDeptName(String deptName)
+    {
+        this.deptName = deptName;
+    }
+
+    public String getOperUrl()
+    {
+        return operUrl;
+    }
+
+    public void setOperUrl(String operUrl)
+    {
+        this.operUrl = operUrl;
+    }
+
+    public String getOperIp()
+    {
+        return operIp;
+    }
+
+    public void setOperIp(String operIp)
+    {
+        this.operIp = operIp;
+    }
+
+    public String getOperLocation()
+    {
+        return operLocation;
+    }
+
+    public void setOperLocation(String operLocation)
+    {
+        this.operLocation = operLocation;
+    }
+
+    public String getOperParam()
+    {
+        return operParam;
+    }
+
+    public void setOperParam(String operParam)
+    {
+        this.operParam = operParam;
+    }
+
+//    public String getJsonResult()
+//    {
+//        return jsonResult;
+//    }
+//
+//    public void setJsonResult(String jsonResult)
+//    {
+//        this.jsonResult = jsonResult;
+//    }
+
+    public Integer getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(Integer status)
+    {
+        this.status = status;
+    }
+
+    public String getErrorMsg()
+    {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg)
+    {
+        this.errorMsg = errorMsg;
+    }
+
+    public Date getOperTime()
+    {
+        return operTime;
+    }
+
+    public void setOperTime(Date operTime)
+    {
+        this.operTime = operTime;
+    }
+
+    public Long getCostTime()
+    {
+        return costTime;
+    }
+
+    public void setCostTime(Long costTime)
+    {
+        this.costTime = costTime;
+    }
+
+    public Integer getAuditType() {
+        return auditType;
+    }
+
+    public void setAuditType(Integer auditType) {
+        this.auditType = auditType;
+    }
+}

+ 41 - 0
backend/src/main/java/com/jiayue/ssi/entity/SysParameter.java

@@ -0,0 +1,41 @@
+package com.jiayue.ssi.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.Version;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 参数实体
+ *
+ * @author xsl
+ * @version 3.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "sys_parameter")
+public class SysParameter extends BaseEntity {
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 参数标识
+     */
+    private String sysKey;
+
+    /**
+     * 参数值
+     */
+    private String sysValue;
+
+    /**
+     * 参数描述
+     */
+    private String sysDescribe;
+    //乐观锁
+    @Version
+    private Integer version;
+}

+ 93 - 0
backend/src/main/java/com/jiayue/ssi/entity/SysPolicy.java

@@ -0,0 +1,93 @@
+package com.jiayue.ssi.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 安全策略表
+ *
+ * @author xsl
+ * @version 3.0
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName(value = "sys_policy")
+public class SysPolicy extends BaseEntity{
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 登录失败次数限制(1-10次)
+     */
+    private Integer loginFails;
+    /**
+     * 登录失败锁定时长(至少20分钟)
+     */
+    private Integer loginLock;
+    /**
+     * 扫描未使用的账号(单位:月)
+     */
+    private Integer scanAccount;
+    /**
+     * 并发会话数设置(单位:个)
+     */
+    private Integer bfhhs;
+    /**
+     * 非活动状态时登出系统(单位:分钟)
+     */
+    private Integer inactiveLogout;
+    /**
+     * 审计日志保留月数
+     */
+    private Integer auditLog;
+    /**
+     * 审计日志保留月数
+     */
+    private Float auditLogBakCapAlarm;
+    /**
+     * 日志存储空间过低报警
+     */
+    private Integer logSpaceWarn;
+    /**
+     * 内存检测小于百分几之报警
+     */
+    private Integer memoryWarn;
+    /**
+     * 密码规则,多个规则用逗号分隔
+     */
+    private String passwordRule;
+    /**
+     * 连续登录失败异常级别设置(0:A 1:B两个级别)
+     */
+    private String excLevelLogin;
+    /**
+     * 同一用户多点登录异常级别设置(0:A 1:B两个级别)
+     */
+    private String excLevelSameUser;
+    /**
+     * 越权访问异常级别(0:A 1:B两个级别)
+     */
+    private String unauthorizedAccessExceptionLevel;
+    /**
+     * IP地址异常级别(0:A 1:B两个级别)
+     */
+    private String ipAddressExceptionLevel;
+    /**
+     * 异常A级别通知方式(0邮件 1告警)
+     */
+    private String excNoticeWayA;
+    /**
+     * 异常B级别通知方式(0邮件 1告警)
+     */
+    private String excNoticeWayB;
+    /**
+     * 可审计事件(0-NWP;1-DQ)
+     */
+    @TableField(value = "auditable_event",fill = FieldFill.INSERT_UPDATE)
+    private String auditableEvent;
+
+    //乐观锁
+    @Version
+    private Integer version;
+}

+ 52 - 0
backend/src/main/java/com/jiayue/ssi/entity/SysRole.java

@@ -0,0 +1,52 @@
+package com.jiayue.ssi.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+
+/**
+ * 角色表
+ *
+ * @author xsl
+ * @version 3.0
+ */
+@Data
+public class SysRole extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /** 角色ID */
+    @TableId(type = IdType.AUTO)
+    private Long roleId;
+
+    /** 角色名称 */
+    private String roleName;
+
+    /** 角色权限 */
+    private String roleKey;
+
+    /** 角色排序 */
+    private Integer roleSort;
+
+    /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */
+    private String dataScope;
+
+    /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */
+    private boolean menuCheckStrictly;
+
+    /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */
+    private boolean deptCheckStrictly;
+
+    /** 角色状态(0正常 1停用) */
+    private String status;
+
+    /** 角色类型(0管理 1业务) */
+    private String roleType;
+
+    /** 是否内置角色(0是 1否) */
+    private String builtIn;
+
+    /** 菜单组 */
+    @TableField(exist = false)
+    private Long[] menuIds;
+}

+ 48 - 0
backend/src/main/java/com/jiayue/ssi/entity/SysRoleMenu.java

@@ -0,0 +1,48 @@
+package com.jiayue.ssi.entity;
+
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 角色表
+ *
+ * @author xsl
+ * @version 3.0
+ */
+@Data
+public class SysRoleMenu extends BaseEntity {
+    /** 角色ID */
+    private Long roleId;
+
+    /** 菜单ID */
+    private Long menuId;
+
+    public Long getRoleId()
+    {
+        return roleId;
+    }
+
+    public void setRoleId(Long roleId)
+    {
+        this.roleId = roleId;
+    }
+
+    public Long getMenuId()
+    {
+        return menuId;
+    }
+
+    public void setMenuId(Long menuId)
+    {
+        this.menuId = menuId;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("roleId", getRoleId())
+            .append("menuId", getMenuId())
+            .toString();
+    }
+}

+ 101 - 0
backend/src/main/java/com/jiayue/ssi/entity/SysUser.java

@@ -0,0 +1,101 @@
+package com.jiayue.ssi.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import java.util.Date;
+
+/**
+ * 用户实体
+ *
+ * @author xsl
+ * @version 3.0
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName(value = "sys_user")
+public class SysUser extends BaseEntity{
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 用户账号
+     */
+    private String username;
+    /**
+     * 身份证号
+     */
+    private String idcard;
+    /**
+     * 用户密码
+     */
+    private String password;
+    /**
+     * 用户姓名
+     */
+    private String nickname;
+    /**
+     * 账号状态(0正常、1锁定(休眠)、2注销)
+     */
+    private String status;
+    /**
+     * 账号在线状态(0在线、1离线)
+     */
+    private String onlineStatus;
+    /**
+     * 邮箱
+     */
+    private String mailbox;
+    /**
+     * 手机号码
+     */
+    private String phonenumber;
+    /**
+     * 失败次数
+     */
+    private Integer errNum;
+    /**
+     * 锁定时间
+     */
+    private Long lockTime;
+    /** 最后修改密码时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
+    private Date lastUpdatePwdTime;
+    /** 最后登录IP */
+    private String loginIp;
+
+    /** 最后登录时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
+    private Date loginDate;
+
+    /**
+     * 用户类型(0管理员、1业务用户)
+     */
+    private String usertype;
+    /**
+     * 判别页面是否需要刷新数据标识(0正常 1需要刷新),由于审批后,用户页面一直不刷新会导致数据不一致。修改操作和注销操作时用
+     */
+    private String signstr;
+
+    /**
+     * 账号有效期
+     */
+//    @TableField(value = "exp_date",fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+8")
+    private Date expDate;
+
+    /**
+     * 检测密码
+     */
+    private String checkPassword;
+
+    /**
+     * 备份密码,用于保护性恢复
+     */
+    private String bakPassword;
+    /** 密码鉴别 */
+    @TableField(exist = false)
+    private String againPwd;
+
+}

+ 45 - 0
backend/src/main/java/com/jiayue/ssi/entity/SysUserRole.java

@@ -0,0 +1,45 @@
+package com.jiayue.ssi.entity;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 用户和角色关联 sys_user_role
+ *
+ * @author ruoyi
+ */
+public class SysUserRole extends BaseEntity {
+    /** 用户ID */
+    private Long userId;
+
+    /** 角色ID */
+    private Long roleId;
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    public Long getRoleId()
+    {
+        return roleId;
+    }
+
+    public void setRoleId(Long roleId)
+    {
+        this.roleId = roleId;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("userId", getUserId())
+            .append("roleId", getRoleId())
+            .toString();
+    }
+}

+ 76 - 0
backend/src/main/java/com/jiayue/ssi/entity/TreeSelect.java

@@ -0,0 +1,76 @@
+package com.jiayue.ssi.entity;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Treeselect树结构实体类
+ *
+ * @author ruoyi
+ */
+public class TreeSelect implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 节点ID */
+    private Long id;
+
+    /** 节点名称 */
+    private String label;
+
+    /** 子节点 */
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    private List<TreeSelect> children;
+
+    public TreeSelect()
+    {
+
+    }
+
+//    public TreeSelect(SysDept dept)
+//    {
+//        this.id = dept.getDeptId();
+//        this.label = dept.getDeptName();
+//        this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
+//    }
+
+    public TreeSelect(SysMenu menu)
+    {
+        this.id = menu.getMenuId();
+        this.label = menu.getMenuName();
+        this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
+    }
+
+    public Long getId()
+    {
+        return id;
+    }
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public String getLabel()
+    {
+        return label;
+    }
+
+    public void setLabel(String label)
+    {
+        this.label = label;
+    }
+
+    public List<TreeSelect> getChildren()
+    {
+        return children;
+    }
+
+    public void setChildren(List<TreeSelect> children)
+    {
+        this.children = children;
+    }
+}

+ 102 - 0
backend/src/main/java/com/jiayue/ssi/entity/server/Cpu.java

@@ -0,0 +1,102 @@
+package com.jiayue.ssi.entity.server;
+
+
+import com.jiayue.ssi.util.Arith;
+
+/**
+ * CPU相关信息
+ *
+ * @author ruoyi
+ */
+public class Cpu
+{
+    /**
+     * 核心数
+     */
+    private int cpuNum;
+
+    /**
+     * CPU总的使用率
+     */
+    private double total;
+
+    /**
+     * CPU系统使用率
+     */
+    private double sys;
+
+    /**
+     * CPU用户使用率
+     */
+    private double used;
+
+    /**
+     * CPU当前等待率
+     */
+    private double wait;
+
+    /**
+     * CPU当前空闲率
+     */
+    private double free;
+
+    public int getCpuNum()
+    {
+        return cpuNum;
+    }
+
+    public void setCpuNum(int cpuNum)
+    {
+        this.cpuNum = cpuNum;
+    }
+
+    public double getTotal()
+    {
+        return Arith.round(Arith.mul(total, 100), 2);
+    }
+
+    public void setTotal(double total)
+    {
+        this.total = total;
+    }
+
+    public double getSys()
+    {
+        return Arith.round(Arith.mul(sys / total, 100), 2);
+    }
+
+    public void setSys(double sys)
+    {
+        this.sys = sys;
+    }
+
+    public double getUsed()
+    {
+        return Arith.round(Arith.mul(used / total, 100), 2);
+    }
+
+    public void setUsed(double used)
+    {
+        this.used = used;
+    }
+
+    public double getWait()
+    {
+        return Arith.round(Arith.mul(wait / total, 100), 2);
+    }
+
+    public void setWait(double wait)
+    {
+        this.wait = wait;
+    }
+
+    public double getFree()
+    {
+        return Arith.round(Arith.mul(free / total, 100), 2);
+    }
+
+    public void setFree(double free)
+    {
+        this.free = free;
+    }
+}

+ 132 - 0
backend/src/main/java/com/jiayue/ssi/entity/server/Jvm.java

@@ -0,0 +1,132 @@
+package com.jiayue.ssi.entity.server;
+
+import com.jiayue.ssi.util.Arith;
+import com.jiayue.ssi.util.DateUtils;
+
+import java.lang.management.ManagementFactory;
+
+
+/**
+ * JVM相关信息
+ *
+ * @author ruoyi
+ */
+public class Jvm
+{
+    /**
+     * 当前JVM占用的内存总数(M)
+     */
+    private double total;
+
+    /**
+     * JVM最大可用内存总数(M)
+     */
+    private double max;
+
+    /**
+     * JVM空闲内存(M)
+     */
+    private double free;
+
+    /**
+     * JDK版本
+     */
+    private String version;
+
+    /**
+     * JDK路径
+     */
+    private String home;
+
+    public double getTotal()
+    {
+        return Arith.div(total, (1024 * 1024), 2);
+    }
+
+    public void setTotal(double total)
+    {
+        this.total = total;
+    }
+
+    public double getMax()
+    {
+        return Arith.div(max, (1024 * 1024), 2);
+    }
+
+    public void setMax(double max)
+    {
+        this.max = max;
+    }
+
+    public double getFree()
+    {
+        return Arith.div(free, (1024 * 1024), 2);
+    }
+
+    public void setFree(double free)
+    {
+        this.free = free;
+    }
+
+    public double getUsed()
+    {
+        return Arith.div(total - free, (1024 * 1024), 2);
+    }
+
+    public double getUsage()
+    {
+        return Arith.mul(Arith.div(total - free, total, 4), 100);
+    }
+
+    /**
+     * 获取JDK名称
+     */
+    public String getName()
+    {
+        return ManagementFactory.getRuntimeMXBean().getVmName();
+    }
+
+    public String getVersion()
+    {
+        return version;
+    }
+
+    public void setVersion(String version)
+    {
+        this.version = version;
+    }
+
+    public String getHome()
+    {
+        return home;
+    }
+
+    public void setHome(String home)
+    {
+        this.home = home;
+    }
+
+    /**
+     * JDK启动时间
+     */
+    public String getStartTime()
+    {
+        return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate());
+    }
+
+    /**
+     * JDK运行时间
+     */
+    public String getRunTime()
+    {
+        return DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate());
+    }
+
+    /**
+     * 运行参数
+     */
+    public String getInputArgs()
+    {
+        return ManagementFactory.getRuntimeMXBean().getInputArguments().toString();
+    }
+}

+ 62 - 0
backend/src/main/java/com/jiayue/ssi/entity/server/Mem.java

@@ -0,0 +1,62 @@
+package com.jiayue.ssi.entity.server;
+
+
+import com.jiayue.ssi.util.Arith;
+
+/**
+ * 內存相关信息
+ *
+ * @author ruoyi
+ */
+public class Mem
+{
+    /**
+     * 内存总量
+     */
+    private double total;
+
+    /**
+     * 已用内存
+     */
+    private double used;
+
+    /**
+     * 剩余内存
+     */
+    private double free;
+
+    public double getTotal()
+    {
+        return Arith.div(total, (1024 * 1024 * 1024), 2);
+    }
+
+    public void setTotal(long total)
+    {
+        this.total = total;
+    }
+
+    public double getUsed()
+    {
+        return Arith.div(used, (1024 * 1024 * 1024), 2);
+    }
+
+    public void setUsed(long used)
+    {
+        this.used = used;
+    }
+
+    public double getFree()
+    {
+        return Arith.div(free, (1024 * 1024 * 1024), 2);
+    }
+
+    public void setFree(long free)
+    {
+        this.free = free;
+    }
+
+    public double getUsage()
+    {
+        return Arith.mul(Arith.div(used, total, 4), 100);
+    }
+}

+ 84 - 0
backend/src/main/java/com/jiayue/ssi/entity/server/Sys.java

@@ -0,0 +1,84 @@
+package com.jiayue.ssi.entity.server;
+
+/**
+ * 系统相关信息
+ *
+ * @author ruoyi
+ */
+public class Sys
+{
+    /**
+     * 服务器名称
+     */
+    private String computerName;
+
+    /**
+     * 服务器Ip
+     */
+    private String computerIp;
+
+    /**
+     * 项目路径
+     */
+    private String userDir;
+
+    /**
+     * 操作系统
+     */
+    private String osName;
+
+    /**
+     * 系统架构
+     */
+    private String osArch;
+
+    public String getComputerName()
+    {
+        return computerName;
+    }
+
+    public void setComputerName(String computerName)
+    {
+        this.computerName = computerName;
+    }
+
+    public String getComputerIp()
+    {
+        return computerIp;
+    }
+
+    public void setComputerIp(String computerIp)
+    {
+        this.computerIp = computerIp;
+    }
+
+    public String getUserDir()
+    {
+        return userDir;
+    }
+
+    public void setUserDir(String userDir)
+    {
+        this.userDir = userDir;
+    }
+
+    public String getOsName()
+    {
+        return osName;
+    }
+
+    public void setOsName(String osName)
+    {
+        this.osName = osName;
+    }
+
+    public String getOsArch()
+    {
+        return osArch;
+    }
+
+    public void setOsArch(String osArch)
+    {
+        this.osArch = osArch;
+    }
+}

+ 114 - 0
backend/src/main/java/com/jiayue/ssi/entity/server/SysFile.java

@@ -0,0 +1,114 @@
+package com.jiayue.ssi.entity.server;
+
+/**
+ * 系统文件相关信息
+ *
+ * @author ruoyi
+ */
+public class SysFile
+{
+    /**
+     * 盘符路径
+     */
+    private String dirName;
+
+    /**
+     * 盘符类型
+     */
+    private String sysTypeName;
+
+    /**
+     * 文件类型
+     */
+    private String typeName;
+
+    /**
+     * 总大小
+     */
+    private String total;
+
+    /**
+     * 剩余大小
+     */
+    private String free;
+
+    /**
+     * 已经使用量
+     */
+    private String used;
+
+    /**
+     * 资源的使用率
+     */
+    private double usage;
+
+    public String getDirName()
+    {
+        return dirName;
+    }
+
+    public void setDirName(String dirName)
+    {
+        this.dirName = dirName;
+    }
+
+    public String getSysTypeName()
+    {
+        return sysTypeName;
+    }
+
+    public void setSysTypeName(String sysTypeName)
+    {
+        this.sysTypeName = sysTypeName;
+    }
+
+    public String getTypeName()
+    {
+        return typeName;
+    }
+
+    public void setTypeName(String typeName)
+    {
+        this.typeName = typeName;
+    }
+
+    public String getTotal()
+    {
+        return total;
+    }
+
+    public void setTotal(String total)
+    {
+        this.total = total;
+    }
+
+    public String getFree()
+    {
+        return free;
+    }
+
+    public void setFree(String free)
+    {
+        this.free = free;
+    }
+
+    public String getUsed()
+    {
+        return used;
+    }
+
+    public void setUsed(String used)
+    {
+        this.used = used;
+    }
+
+    public double getUsage()
+    {
+        return usage;
+    }
+
+    public void setUsage(double usage)
+    {
+        this.usage = usage;
+    }
+}

+ 106 - 0
backend/src/main/java/com/jiayue/ssi/entity/vo/MetaVo.java

@@ -0,0 +1,106 @@
+package com.jiayue.ssi.entity.vo;
+
+import com.jiayue.ssi.util.RyStringUtils;
+
+/**
+ * 路由显示信息
+ *
+ * @author ruoyi
+ */
+public class MetaVo
+{
+    /**
+     * 设置该路由在侧边栏和面包屑中展示的名字
+     */
+    private String title;
+
+    /**
+     * 设置该路由的图标,对应路径src/assets/icons/svg
+     */
+    private String icon;
+
+    /**
+     * 设置为true,则不会被 <keep-alive>缓存
+     */
+    private boolean noCache;
+
+    /**
+     * 内链地址(http(s)://开头)
+     */
+    private String link;
+
+    public MetaVo()
+    {
+    }
+
+    public MetaVo(String title, String icon)
+    {
+        this.title = title;
+        this.icon = icon;
+    }
+
+    public MetaVo(String title, String icon, boolean noCache)
+    {
+        this.title = title;
+        this.icon = icon;
+        this.noCache = noCache;
+    }
+
+    public MetaVo(String title, String icon, String link)
+    {
+        this.title = title;
+        this.icon = icon;
+        this.link = link;
+    }
+
+    public MetaVo(String title, String icon, boolean noCache, String link)
+    {
+        this.title = title;
+        this.icon = icon;
+        this.noCache = noCache;
+        if (RyStringUtils.ishttp(link))
+        {
+            this.link = link;
+        }
+    }
+
+    public boolean isNoCache()
+    {
+        return noCache;
+    }
+
+    public void setNoCache(boolean noCache)
+    {
+        this.noCache = noCache;
+    }
+
+    public String getTitle()
+    {
+        return title;
+    }
+
+    public void setTitle(String title)
+    {
+        this.title = title;
+    }
+
+    public String getIcon()
+    {
+        return icon;
+    }
+
+    public void setIcon(String icon)
+    {
+        this.icon = icon;
+    }
+
+    public String getLink()
+    {
+        return link;
+    }
+
+    public void setLink(String link)
+    {
+        this.link = link;
+    }
+}

+ 149 - 0
backend/src/main/java/com/jiayue/ssi/entity/vo/RouterVo.java

@@ -0,0 +1,149 @@
+package com.jiayue.ssi.entity.vo;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+/**
+ * 路由配置信息
+ *
+ * @author ruoyi
+ */
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+public class RouterVo
+{
+    /**
+     * 路由名字
+     */
+    private String name;
+
+    /**
+     * 路由地址
+     */
+    private String path;
+
+    /**
+     * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现
+     */
+    private boolean hidden;
+
+    /**
+     * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
+     */
+    private String redirect;
+
+    /**
+     * 组件地址
+     */
+    private String component;
+
+    /**
+     * 路由参数:如 {"id": 1, "name": "ry"}
+     */
+    private String query;
+
+    /**
+     * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
+     */
+    private Boolean alwaysShow;
+
+    /**
+     * 其他元素
+     */
+    private MetaVo meta;
+
+    /**
+     * 子路由
+     */
+    private List<RouterVo> children;
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getPath()
+    {
+        return path;
+    }
+
+    public void setPath(String path)
+    {
+        this.path = path;
+    }
+
+    public boolean getHidden()
+    {
+        return hidden;
+    }
+
+    public void setHidden(boolean hidden)
+    {
+        this.hidden = hidden;
+    }
+
+    public String getRedirect()
+    {
+        return redirect;
+    }
+
+    public void setRedirect(String redirect)
+    {
+        this.redirect = redirect;
+    }
+
+    public String getComponent()
+    {
+        return component;
+    }
+
+    public void setComponent(String component)
+    {
+        this.component = component;
+    }
+
+    public String getQuery()
+    {
+        return query;
+    }
+
+    public void setQuery(String query)
+    {
+        this.query = query;
+    }
+
+    public Boolean getAlwaysShow()
+    {
+        return alwaysShow;
+    }
+
+    public void setAlwaysShow(Boolean alwaysShow)
+    {
+        this.alwaysShow = alwaysShow;
+    }
+
+    public MetaVo getMeta()
+    {
+        return meta;
+    }
+
+    public void setMeta(MetaVo meta)
+    {
+        this.meta = meta;
+    }
+
+    public List<RouterVo> getChildren()
+    {
+        return children;
+    }
+
+    public void setChildren(List<RouterVo> children)
+    {
+        this.children = children;
+    }
+}

+ 105 - 0
backend/src/main/java/com/jiayue/ssi/factory/LoginFactory.java

@@ -0,0 +1,105 @@
+package com.jiayue.ssi.factory;
+
+import com.jiayue.ssi.constant.Constants;
+import com.jiayue.ssi.dto.UserVisitInfoDto;
+import com.jiayue.ssi.entity.SysLogininfor;
+import com.jiayue.ssi.service.SysLogininforService;
+import com.jiayue.ssi.util.*;
+import eu.bitwalker.useragentutils.UserAgent;
+
+import java.util.Date;
+
+
+/**
+* 登录日志工厂
+*
+* @author xsl
+* @since 2023/03/29
+*/
+public class LoginFactory {
+    /**
+     * 记录登录信息
+     *
+     * @param username 用户名
+     * @param status 状态
+     * @param message 消息
+     * @return 任务task
+     */
+    public static void recordLogininfor(final String username, final String status, final String message) {
+        final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
+        final String ip = IPUtils.getIpAddr();
+        String address = AddressUtils.getRealAddressByIP(ip);
+        StringBuilder s = new StringBuilder();
+        s.append(getBlock(ip));
+        s.append(address);
+        s.append(getBlock(username));
+        s.append(getBlock(status));
+        s.append(getBlock(message));
+        // 获取客户端操作系统
+        String os = userAgent.getOperatingSystem().getName();
+        // 获取客户端浏览器
+        String browser = userAgent.getBrowser().getName();
+        // 封装对象
+        SysLogininfor logininfor = new SysLogininfor();
+        logininfor.setUserName(username);
+        logininfor.setIpaddr(ip);
+        logininfor.setLoginLocation(address);
+        logininfor.setBrowser(browser);
+        logininfor.setOs(os);
+        logininfor.setMsg(message);
+        logininfor.setLoginTime(new Date());
+        logininfor.setCreateBy(username);
+        // 日志状态
+        if (RyStringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
+        {
+            logininfor.setStatus(Constants.SUCCESS);
+        }
+        else if (Constants.LOGIN_FAIL.equals(status))
+        {
+            logininfor.setStatus(Constants.FAIL);
+        }
+        // 插入数据
+        SpringUtils.getBean(SysLogininforService.class).insertLogininfor(logininfor);
+    }
+
+    /**
+     * 记录登录信息
+     *
+     * @param userVisitInfoDto 用户名
+     * @param status 状态
+     * @param message 消息
+     * @return 任务task
+     */
+    public static void autoRecordLogininfor(UserVisitInfoDto userVisitInfoDto, final String status, final String message) {
+        // 封装对象
+        SysLogininfor logininfor = new SysLogininfor();
+        logininfor.setUserName(userVisitInfoDto.getUsername());
+        logininfor.setIpaddr(userVisitInfoDto.getIp());
+        logininfor.setLoginLocation(userVisitInfoDto.getLoginLocation());
+        logininfor.setBrowser(userVisitInfoDto.getBrowser());
+        logininfor.setOs(userVisitInfoDto.getOs());
+        logininfor.setMsg(message);
+        logininfor.setLoginTime(new Date());
+        logininfor.setCreateBy(userVisitInfoDto.getUsername());
+        // 日志状态
+        if (RyStringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
+        {
+            logininfor.setStatus(Constants.SUCCESS);
+        }
+        else if (Constants.LOGIN_FAIL.equals(status))
+        {
+            logininfor.setStatus(Constants.FAIL);
+        }
+        // 插入数据
+        SpringUtils.getBean(SysLogininforService.class).insertLogininfor(logininfor);
+    }
+
+    public static String getBlock(Object msg)
+    {
+        if (msg == null)
+        {
+            msg = "";
+        }
+        return "[" + msg.toString() + "]";
+    }
+}

+ 26 - 0
backend/src/main/java/com/jiayue/ssi/factory/OperateLogFactory.java

@@ -0,0 +1,26 @@
+package com.jiayue.ssi.factory;
+
+import com.jiayue.ssi.entity.SysOperLog;
+import com.jiayue.ssi.service.SysOperLogService;
+import com.jiayue.ssi.util.AddressUtils;
+import com.jiayue.ssi.util.SpringUtils;
+
+/**
+ * 操作日志工厂
+ *
+ * @author xsl
+ * @since 2023/03/30
+ */
+public class OperateLogFactory {
+    /**
+     * 操作日志记录
+     *
+     * @param operLog 操作日志信息
+     * @return
+     */
+    public static void recordOper(final SysOperLog operLog) {
+        // 远程查询操作地点
+        operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
+        SpringUtils.getBean(SysOperLogService.class).insertOperlog(operLog);
+    }
+}

+ 51 - 0
backend/src/main/java/com/jiayue/ssi/filter/InterfaceLimitFilter.java

@@ -0,0 +1,51 @@
+package com.jiayue.ssi.filter;
+
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+
+/**
+ * 接口访问限制过滤器
+ *
+ * @author xsl
+ * @since 2023/02/24
+ */
+@RequiredArgsConstructor
+@Order(1)
+@Slf4j
+@Component
+public class InterfaceLimitFilter extends OncePerRequestFilter {
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+        throws ServletException, IOException {
+        response.setHeader("Strict-Transport-Security", "max-age=63072000;includeSubDomains;preload");
+        response.setHeader("Content-Security-Policy","script-src 'self';object-src 'self'");
+        response.setHeader("X-Content-Type-Options","nosniff");
+        response.setHeader("X-XSS-Protection","1; mode=block");
+        response.setHeader("X-Frame-Options","SAMEORIGIN");
+        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0");
+        response.setHeader("Cache-Control", "no-cache='set-cookie'");
+        response.setHeader("Pragma", "no-cache");
+
+        String method = request.getMethod();
+        if (!(method.toUpperCase().equals("GET") || method.toUpperCase().equals("POST"))){
+            response.setHeader("Access-Control-Allow-Origin", "*");
+            response.setStatus(405);
+            response.setContentType("text/html;charset=utf-8");
+            response.getWriter().write("方法不允许访问");
+            return;
+        }
+
+        filterChain.doFilter(request, response);
+    }
+}

+ 43 - 0
backend/src/main/java/com/jiayue/ssi/handler/MyMetaObjectHandler.java

@@ -0,0 +1,43 @@
+package com.jiayue.ssi.handler;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+* mybatis自动填充策略
+*
+* @author xsl
+* @since 2023/03/10
+*/
+@Component
+public class MyMetaObjectHandler implements MetaObjectHandler {
+    /**
+     * 插入时的填充策略
+     *
+     * @param metaObject
+     */
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        String username = "";
+        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
+        this.strictInsertFill(metaObject, "createBy", String.class, username);
+    }
+
+    /**
+     * 更新时的填充策略
+     *
+     * @param metaObject
+     */
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        String username = "";
+        // 解决不会覆盖原值的情况
+        metaObject.setValue("updateTime", null);
+        metaObject.setValue("updateBy", null);
+        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
+        this.strictInsertFill(metaObject, "updateBy", String.class, username);
+    }
+}

+ 16 - 0
backend/src/main/java/com/jiayue/ssi/mapper/ElectricFieldMapper.java

@@ -0,0 +1,16 @@
+package com.jiayue.ssi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jiayue.ssi.entity.ElectricField;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ *  场站Mapper
+ *
+ * @author xsl
+ * @since 2023-03-10
+ */
+@Mapper
+public interface ElectricFieldMapper extends BaseMapper<ElectricField> {
+
+}

+ 16 - 0
backend/src/main/java/com/jiayue/ssi/mapper/SysApproveMapper.java

@@ -0,0 +1,16 @@
+package com.jiayue.ssi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jiayue.ssi.entity.SysApprove;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ *  审批Mapper
+ *
+ * @author xsl
+ * @since 2023-03-10
+ */
+@Mapper
+public interface SysApproveMapper extends BaseMapper<SysApprove> {
+
+}

+ 47 - 0
backend/src/main/java/com/jiayue/ssi/mapper/SysLogininforMapper.java

@@ -0,0 +1,47 @@
+package com.jiayue.ssi.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jiayue.ssi.entity.SysLogininfor;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ *  系统访问记录表
+ *
+ * @author xsl
+ * @since 2023-03-17
+ */
+@Mapper
+public interface SysLogininforMapper extends BaseMapper<SysLogininfor> {
+    /**
+     * 新增系统登录日志
+     *
+     * @param logininfor 访问日志对象
+     */
+    public void insertLogininfor(SysLogininfor logininfor);
+
+    /**
+     * 查询系统登录日志集合
+     *
+     * @param logininfor 访问日志对象
+     * @return 登录记录集合
+     */
+    public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor);
+
+    /**
+     * 批量删除系统登录日志
+     *
+     * @param infoIds 需要删除的登录日志ID
+     * @return 结果
+     */
+    public int deleteLogininforByIds(Long[] infoIds);
+
+    /**
+     * 清空系统登录日志
+     *
+     * @return 结果
+     */
+    public int cleanLogininfor();
+}

+ 130 - 0
backend/src/main/java/com/jiayue/ssi/mapper/SysMenuMapper.java

@@ -0,0 +1,130 @@
+package com.jiayue.ssi.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jiayue.ssi.entity.SysMenu;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ *  菜单管理Mapper
+ *
+ * @author xsl
+ * @since 2023-03-17
+ */
+@Mapper
+public interface SysMenuMapper  extends BaseMapper<SysMenu> {
+    /**
+     * 查询系统菜单列表
+     *
+     * @param menu 菜单信息
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuList(SysMenu menu);
+
+    /**
+     * 根据用户所有权限
+     *
+     * @return 权限列表
+     */
+    public List<String> selectMenuPerms();
+
+    /**
+     * 根据用户查询系统菜单列表
+     *
+     * @param menu 菜单信息
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuListByUserId(SysMenu menu);
+
+    /**
+     * 根据角色ID查询权限
+     *
+     * @param roleId 角色ID
+     * @return 权限列表
+     */
+    public List<String> selectMenuPermsByRoleId(Long roleId);
+
+    /**
+     * 根据用户ID查询权限
+     *
+     * @param userId 用户ID
+     * @return 权限列表
+     */
+    public List<String> selectMenuPermsByUserId(Long userId);
+
+    /**
+     * 根据用户ID查询菜单
+     *
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuTreeAll();
+
+    /**
+     * 根据用户ID查询菜单
+     *
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuTreeByUserId(Long userId);
+
+    /**
+     * 根据角色ID查询菜单树信息
+     *
+     * @param roleId 角色ID
+     * @param menuCheckStrictly 菜单树选择项是否关联显示
+     * @return 选中菜单列表
+     */
+    public List<Long> selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly);
+
+    /**
+     * 根据菜单ID查询信息
+     *
+     * @param menuId 菜单ID
+     * @return 菜单信息
+     */
+    public SysMenu selectMenuById(Long menuId);
+
+    /**
+     * 是否存在菜单子节点
+     *
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    public int hasChildByMenuId(Long menuId);
+
+    /**
+     * 新增菜单信息
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public int insertMenu(SysMenu menu);
+
+    /**
+     * 修改菜单信息
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public int updateMenu(SysMenu menu);
+
+    /**
+     * 删除菜单管理信息
+     *
+     * @param updateBy 修改人
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    public int deleteMenuById(String updateBy,Long menuId);
+
+    /**
+     * 校验菜单名称是否唯一
+     *
+     * @param menuName 菜单名称
+     * @param parentId 父菜单ID
+     * @return 结果
+     */
+    public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId);
+}

+ 51 - 0
backend/src/main/java/com/jiayue/ssi/mapper/SysOperLogMapper.java

@@ -0,0 +1,51 @@
+package com.jiayue.ssi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jiayue.ssi.entity.SysOperLog;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 操作日志 数据层
+ *
+ * @author ruoyi
+ */
+@Mapper
+public interface SysOperLogMapper extends BaseMapper<SysOperLog> {
+    /**
+     * 新增操作日志
+     *
+     * @param operLog 操作日志对象
+     */
+    public void insertOperlog(SysOperLog operLog);
+
+    /**
+     * 查询系统操作日志集合
+     *
+     * @param operLog 操作日志对象
+     * @return 操作日志集合
+     */
+    public List<SysOperLog> selectOperLogList(SysOperLog operLog);
+
+    /**
+     * 批量删除系统操作日志
+     *
+     * @param operIds 需要删除的操作日志ID
+     * @return 结果
+     */
+    public int deleteOperLogByIds(Long[] operIds);
+
+    /**
+     * 查询操作日志详细
+     *
+     * @param operId 操作ID
+     * @return 操作日志对象
+     */
+    public SysOperLog selectOperLogById(Long operId);
+
+    /**
+     * 清空操作日志
+     */
+    public void cleanOperLog();
+}

+ 17 - 0
backend/src/main/java/com/jiayue/ssi/mapper/SysParameterMapper.java

@@ -0,0 +1,17 @@
+package com.jiayue.ssi.mapper;
+
+import com.jiayue.ssi.entity.SysParameter;
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ *  参数管理Mapper
+ *
+ * @author xsl
+ * @since 2023-03-17
+ */
+@Mapper
+public interface SysParameterMapper extends BaseMapper<SysParameter> {
+
+}

+ 16 - 0
backend/src/main/java/com/jiayue/ssi/mapper/SysPolicyMapper.java

@@ -0,0 +1,16 @@
+package com.jiayue.ssi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jiayue.ssi.entity.SysPolicy;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ *  策略配置Mapper
+ *
+ * @author xsl
+ * @since 2023-03-10
+ */
+@Mapper
+public interface SysPolicyMapper extends BaseMapper<SysPolicy> {
+
+}

+ 123 - 0
backend/src/main/java/com/jiayue/ssi/mapper/SysRoleMapper.java

@@ -0,0 +1,123 @@
+package com.jiayue.ssi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jiayue.ssi.entity.SysRole;
+import org.apache.ibatis.annotations.Mapper;
+
+
+/**
+ * 角色表 数据层
+ *
+ * @author ruoyi
+ */
+@Mapper
+public interface SysRoleMapper extends BaseMapper<SysRole> {
+    /**
+     * 新增角色信息
+     *
+     * @param role 角色信息
+     * @return 结果
+     */
+    public int insertRole(SysRole role);
+    /**
+     * 通过角色ID查询角色
+     *
+     * @param roleId 角色ID
+     * @return 角色对象信息
+     */
+    public SysRole selectRoleById(Long roleId);
+//    /**
+//     * 根据条件分页查询角色数据
+//     *
+//     * @param role 角色信息
+//     * @return 角色数据集合信息
+//     */
+//    public List<SysRole> selectRoleList(SysRole role);
+//
+//    /**
+//     * 根据用户ID查询角色
+//     *
+//     * @param userId 用户ID
+//     * @return 角色列表
+//     */
+//    public List<SysRole> selectRolePermissionByUserId(Long userId);
+//
+//    /**
+//     * 查询所有角色
+//     *
+//     * @return 角色列表
+//     */
+//    public List<SysRole> selectRoleAll();
+//
+    /**
+     * 根据用户ID获取角色选择框列表
+     *
+     * @param userId 用户ID
+     * @return 选中角色ID列表
+     */
+    public SysRole selectRoleListByUserId(Long userId);
+//
+//    /**
+//     * 通过角色ID查询角色
+//     *
+//     * @param roleId 角色ID
+//     * @return 角色对象信息
+//     */
+//    public SysRole selectRoleById(Long roleId);
+//
+//    /**
+//     * 根据用户ID查询角色
+//     *
+//     * @param userName 用户名
+//     * @return 角色列表
+//     */
+//    public List<SysRole> selectRolesByUserName(String userName);
+//
+    /**
+     * 校验角色名称是否唯一
+     *
+     * @param roleName 角色名称
+     * @return 角色信息
+     */
+    public SysRole checkRoleNameUnique(String roleName);
+//
+    /**
+     * 校验角色权限是否唯一
+     *
+     * @param roleKey 角色权限
+     * @return 角色信息
+     */
+    public SysRole checkRoleKeyUnique(String roleKey);
+//
+//    /**
+//     * 修改角色信息
+//     *
+//     * @param role 角色信息
+//     * @return 结果
+//     */
+//    public int updateRole(SysRole role);
+//
+//    /**
+//     * 新增角色信息
+//     *
+//     * @param role 角色信息
+//     * @return 结果
+//     */
+//    public int insertRole(SysRole role);
+//
+    /**
+     * 通过角色ID删除角色
+     * @param update_by 修改人
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    public int deleteRoleById(String update_by,Long roleId);
+//
+//    /**
+//     * 批量删除角色信息
+//     *
+//     * @param roleIds 需要删除的角色ID
+//     * @return 结果
+//     */
+//    public int deleteRoleByIds(Long[] roleIds);
+}

+ 48 - 0
backend/src/main/java/com/jiayue/ssi/mapper/SysRoleMenuMapper.java

@@ -0,0 +1,48 @@
+package com.jiayue.ssi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jiayue.ssi.entity.SysRole;
+import com.jiayue.ssi.entity.SysRoleMenu;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 角色与菜单关联表 数据层
+ *
+ * @author ruoyi
+ */
+@Mapper
+public interface SysRoleMenuMapper extends BaseMapper<SysRole> {
+    /**
+     * 查询菜单使用数量
+     *
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    public int checkMenuExistRole(Long menuId);
+
+    /**
+     * 通过角色ID删除角色和菜单关联
+     *
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    public int deleteRoleMenuByRoleId(String update_by,Long roleId);
+
+    /**
+     * 批量删除角色菜单关联信息
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteRoleMenu(Long[] ids);
+
+    /**
+     * 批量新增角色菜单信息
+     *
+     * @param roleMenuList 角色菜单列表
+     * @return 结果
+     */
+    public int batchRoleMenu(List<SysRoleMenu> roleMenuList);
+}

+ 48 - 0
backend/src/main/java/com/jiayue/ssi/mapper/SysUserMapper.java

@@ -0,0 +1,48 @@
+package com.jiayue.ssi.mapper;
+
+import com.jiayue.ssi.entity.SysUser;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Update;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ *  用户管理Mapper
+ *
+ * @author xsl
+ * @since 2023-03-10
+ */
+@Mapper
+public interface SysUserMapper extends BaseMapper<SysUser> {
+    /**
+     * 通过角色ID查找用户
+     *
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    public List<SysUser> selectUserByRole(String roleId);
+    /**
+     * 更改密码
+     *
+     * @param password 新密码
+     * @return 结果
+     */
+    @Update("update sys_user t set t.password=#{password},t.check_password=#{checkPassword},t.bak_password=#{bakPassword},t.last_update_pwd_time=null where t.id=#{id}")
+    public int resetPassword(Long id, String password,String checkPassword,String bakPassword);
+    /**
+     * 修改审核标识
+     *
+     * @param signstr 新密码
+     * @return 结果
+     */
+    @Update("update sys_user t set t.signstr=#{signstr} where t.id=#{id}")
+    public int updateSignstr(Long id, String signstr);
+    /**
+     * 修改基本信息
+     * @return 结果
+     */
+    @Update("update sys_user t set t.signstr=#{signstr},t.nickname=#{nickname},t.mailbox=#{mailbox},t.phonenumber=#{phonenumber},t.exp_date=#{exp_date} where t.id=#{id}")
+    public int updateBaseInfo(Long id, String signstr, String nickname, String mailbox, String phonenumber, Date exp_date);
+}

+ 65 - 0
backend/src/main/java/com/jiayue/ssi/mapper/SysUserRoleMapper.java

@@ -0,0 +1,65 @@
+package com.jiayue.ssi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jiayue.ssi.entity.SysUserRole;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 用户与角色关联表 数据层
+ *
+ * @author ruoyi
+ */
+@Mapper
+public interface SysUserRoleMapper extends BaseMapper<SysUserRole> {
+    /**
+     * 通过用户ID删除用户和角色关联
+     *
+     * @param userId 用户ID
+     * @return 结果
+     */
+    public int deleteUserRoleByUserId(Long userId);
+
+    /**
+     * 批量删除用户和角色关联
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteUserRole(Long[] ids);
+
+    /**
+     * 通过角色ID查询角色使用数量
+     *
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    public int countUserRoleByRoleId(Long roleId);
+
+    /**
+     * 批量新增用户角色信息
+     *
+     * @param userRoleList 用户角色列表
+     * @return 结果
+     */
+    public int batchUserRole(List<SysUserRole> userRoleList);
+
+    /**
+     * 删除用户和角色关联信息
+     *
+     * @param userRole 用户和角色关联信息
+     * @return 结果
+     */
+    public int deleteUserRoleInfo(SysUserRole userRole);
+
+    /**
+     * 批量取消授权用户角色
+     *
+     * @param roleId 角色ID
+     * @param userIds 需要删除的用户数据ID
+     * @return 结果
+     */
+    public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds);
+}

+ 14 - 0
backend/src/main/java/com/jiayue/ssi/service/ElectricFieldService.java

@@ -0,0 +1,14 @@
+package com.jiayue.ssi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.jiayue.ssi.entity.ElectricField;
+
+
+/**
+* 场站接口
+* @author xsl
+* @date 2023/2/16
+*/
+public interface ElectricFieldService extends IService<ElectricField> {
+
+}

+ 20 - 0
backend/src/main/java/com/jiayue/ssi/service/SysApproveService.java

@@ -0,0 +1,20 @@
+package com.jiayue.ssi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.entity.SysApprove;
+
+
+/**
+ * 审核服务类
+ * @author xsl
+ * @date 2023/2/16
+ */
+public interface SysApproveService extends IService<SysApprove> {
+    /**
+     * 审核提交
+     * @param sysApprove
+     * @return
+     */
+    void submitApprove(SysApprove sysApprove) throws CustomException;
+}

+ 35 - 0
backend/src/main/java/com/jiayue/ssi/service/SysLogininforService.java

@@ -0,0 +1,35 @@
+package com.jiayue.ssi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.jiayue.ssi.entity.SysLogininfor;
+
+
+/**
+ * 系统访问日志服务类
+ * @author xsl
+ * @date 2023/2/16
+ */
+public interface SysLogininforService extends IService<SysLogininfor> {
+    /**
+     * 新增系统登录日志
+     *
+     * @param logininfor 访问日志对象
+     */
+    void insertLogininfor(SysLogininfor logininfor);
+    /**
+     * 删除登录日志
+     * @param infoId
+     * @return
+     */
+    boolean removeLoginInfoById(Long infoId);
+    /**
+     * 清空系统登录日志
+     */
+    boolean cleanLogininfor();
+    /**
+     * 批量真实删除登录日志
+     * @param infoIds 主键id集合
+     * @return
+     */
+    boolean deleteLogininforByIds(Long[] infoIds);
+}

+ 122 - 0
backend/src/main/java/com/jiayue/ssi/service/SysMenuService.java

@@ -0,0 +1,122 @@
+package com.jiayue.ssi.service;
+
+import java.util.List;
+import java.util.Set;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.jiayue.ssi.entity.SysMenu;
+import com.jiayue.ssi.entity.TreeSelect;
+import com.jiayue.ssi.entity.vo.RouterVo;
+
+/**
+ * 菜单服务类
+ * @author xsl
+ * @date 2023/2/16
+ */
+public interface SysMenuService  extends IService<SysMenu> {
+    /**
+     * 根据用户ID查询菜单树信息
+     *
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+    List<SysMenu> selectMenuTreeByUserId(Long userId);
+    /**
+     * 构建前端路由所需要的菜单
+     *
+     * @param menus 菜单列表
+     * @return 路由列表
+     */
+    List<RouterVo> buildMenus(List<SysMenu> menus);
+    /**
+     * 查询系统菜单列表
+     *
+     * @param menu 菜单信息
+     * @return 菜单列表
+     */
+    List<SysMenu> selectMenuList(SysMenu menu, Long userId);
+    /**
+     * 校验菜单名称是否唯一
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    String checkMenuNameUnique(SysMenu menu);
+    /**
+     * 新增保存菜单信息
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    int insertMenu(SysMenu menu);
+    /**
+     * 根据菜单ID查询信息
+     *
+     * @param menuId 菜单ID
+     * @return 菜单信息
+     */
+    SysMenu selectMenuById(Long menuId);
+    /**
+     * 修改保存菜单信息
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    int updateMenu(SysMenu menu);
+    /**
+     * 是否存在菜单子节点
+     *
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    boolean hasChildByMenuId(Long menuId);
+    /**
+     * 删除菜单管理信息
+     *
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    int deleteMenuById(Long menuId);
+    /**
+     * 构建前端所需要下拉树结构
+     *
+     * @param menus 菜单列表
+     * @return 下拉树结构列表
+     */
+    List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus,String pc);
+    /**
+     * 构建前端所需要树结构
+     *
+     * @param menus 菜单列表
+     * @return 树结构列表
+     */
+    List<SysMenu> buildMenuTree(List<SysMenu> menus,String pc);
+    /**
+     * 根据用户查询系统菜单列表
+     *
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+    List<SysMenu> selectMenuList(Long userId);
+    /**
+     * 根据角色ID查询菜单树信息
+     *
+     * @param roleId 角色ID
+     * @return 选中菜单列表
+     */
+    List<Long> selectMenuListByRoleId(Long roleId);
+    /**
+     * 根据用户ID查询权限
+     *
+     * @param userId 用户ID
+     * @return 权限列表
+     */
+    Set<String> selectMenuPermsByUserId(Long userId);
+    /**
+     * 查询菜单是否存在角色
+     *
+     * @param menuId 菜单ID
+     * @return 结果 true 存在 false 不存在
+     */
+    boolean checkMenuExistRole(Long menuId);
+}

+ 35 - 0
backend/src/main/java/com/jiayue/ssi/service/SysOperLogService.java

@@ -0,0 +1,35 @@
+package com.jiayue.ssi.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.jiayue.ssi.entity.SysOperLog;
+
+/**
+ * 操作日志 服务层
+ *
+ * @author ruoyi
+ */
+public interface SysOperLogService extends IService<SysOperLog> {
+    /**
+     * 新增操作日志
+     *
+     * @param operLog 操作日志对象
+     */
+    void insertOperlog(SysOperLog operLog);
+    /**
+     * 删除操作日志
+     * @param operId
+     * @return
+     */
+    boolean removeOperlogById(Long operId);
+    /**
+     * 清空操作日志
+     */
+    boolean cleanOperLog();
+    /**
+     * 批量真实删除操作日志
+     * @param operIds 主键id集合
+     * @return
+     */
+    boolean deleteOperLogByIds(Long[] operIds);
+}

+ 21 - 0
backend/src/main/java/com/jiayue/ssi/service/SysParameterService.java

@@ -0,0 +1,21 @@
+package com.jiayue.ssi.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.jiayue.ssi.entity.SysParameter;
+import com.jiayue.ssi.entity.SysUser;
+
+/**
+* 参数管理接口
+* @author xsl
+* @date 2023/2/16
+*/
+public interface SysParameterService extends IService<SysParameter> {
+    /**
+     * 根据key查找
+     * @param key
+     * @return SysUser
+     */
+    String queryByKey(String key,String defaultValue);
+}

+ 14 - 0
backend/src/main/java/com/jiayue/ssi/service/SysPolicyService.java

@@ -0,0 +1,14 @@
+package com.jiayue.ssi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.jiayue.ssi.entity.SysPolicy;
+
+
+/**
+* 策略配置接口
+* @author xsl
+* @date 2023/2/16
+*/
+public interface SysPolicyService extends IService<SysPolicy> {
+
+}

+ 179 - 0
backend/src/main/java/com/jiayue/ssi/service/SysRoleService.java

@@ -0,0 +1,179 @@
+package com.jiayue.ssi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.jiayue.ssi.entity.SysRole;
+
+/**
+ * 角色业务层
+ *
+ * @author ruoyi
+ */
+public interface SysRoleService extends IService<SysRole> {
+//    /**
+//     * 根据条件分页查询角色数据
+//     *
+//     * @param role 角色信息
+//     * @return 角色数据集合信息
+//     */
+//    public List<SysRole> selectRoleList(SysRole role);
+//
+//    /**
+//     * 根据用户ID查询角色列表
+//     *
+//     * @param userId 用户ID
+//     * @return 角色列表
+//     */
+//    public List<SysRole> selectRolesByUserId(Long userId);
+//
+//    /**
+//     * 根据用户ID查询角色权限
+//     *
+//     * @param userId 用户ID
+//     * @return 权限列表
+//     */
+//    public Set<String> selectRolePermissionByUserId(Long userId);
+//
+//    /**
+//     * 查询所有角色
+//     *
+//     * @return 角色列表
+//     */
+//    public List<SysRole> selectRoleAll();
+//
+//    /**
+//     * 根据用户ID获取角色选择框列表
+//     *
+//     * @param userId 用户ID
+//     * @return 选中角色ID列表
+//     */
+//    public List<Long> selectRoleListByUserId(Long userId);
+//
+    /**
+     * 通过角色ID查询角色
+     *
+     * @param roleId 角色ID
+     * @return 角色对象信息
+     */
+    public SysRole selectRoleById(Long roleId);
+//
+    /**
+     * 校验角色名称是否唯一
+     *
+     * @param role 角色信息
+     * @return 结果
+     */
+    String checkRoleNameUnique(SysRole role);
+//
+    /**
+     * 校验角色权限是否唯一
+     *
+     * @param role 角色信息
+     * @return 结果
+     */
+    String checkRoleKeyUnique(SysRole role);
+//
+//    /**
+//     * 校验角色是否允许操作
+//     *
+//     * @param role 角色信息
+//     */
+//    public void checkRoleAllowed(SysRole role);
+//
+//    /**
+//     * 校验角色是否有数据权限
+//     *
+//     * @param roleId 角色id
+//     */
+//    public void checkRoleDataScope(Long roleId);
+//
+    /**
+     * 通过角色ID查询角色使用数量
+     *
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    public int countUserRoleByRoleId(Long roleId);
+
+    /**
+     * 新增保存角色信息
+     *
+     * @param role 角色信息
+     * @return 结果
+     */
+    public int insertRole(SysRole role);
+
+    /**
+     * 修改保存角色信息
+     *
+     * @param role 角色信息
+     * @return 结果
+     */
+    public int updateRole(SysRole role);
+//
+//    /**
+//     * 修改角色状态
+//     *
+//     * @param role 角色信息
+//     * @return 结果
+//     */
+//    public int updateRoleStatus(SysRole role);
+//
+//    /**
+//     * 修改数据权限信息
+//     *
+//     * @param role 角色信息
+//     * @return 结果
+//     */
+//    public int authDataScope(SysRole role);
+//
+    /**
+     * 通过角色ID删除角色
+     *
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    int deleteRoleById(Long roleId);
+    /**
+     * 根据角色名称查找
+     * @param roleName
+     * @return SysRole
+     */
+    SysRole queryRoleName(String roleName);
+    /**
+     * 根据角色权限查找
+     * @param roleKey
+     * @return SysRole
+     */
+    SysRole queryRoleKey(String roleKey);
+    /**
+     * 根据用户id找对应的角色
+     * @return 结果
+     */
+    SysRole selectRoleListByUserId();
+//
+//    /**
+//     * 取消授权用户角色
+//     *
+//     * @param userRole 用户和角色关联信息
+//     * @return 结果
+//     */
+//    public int deleteAuthUser(SysUserRole userRole);
+//
+//    /**
+//     * 批量取消授权用户角色
+//     *
+//     * @param roleId 角色ID
+//     * @param userIds 需要取消授权的用户数据ID
+//     * @return 结果
+//     */
+//    public int deleteAuthUsers(Long roleId, Long[] userIds);
+//
+//    /**
+//     * 批量选择授权用户角色
+//     *
+//     * @param roleId 角色ID
+//     * @param userIds 需要删除的用户数据ID
+//     * @return 结果
+//     */
+//    public int insertAuthUsers(Long roleId, Long[] userIds);
+}

+ 13 - 0
backend/src/main/java/com/jiayue/ssi/service/SysUserRoleService.java

@@ -0,0 +1,13 @@
+package com.jiayue.ssi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.jiayue.ssi.entity.SysUserRole;
+
+/**
+* 用户角色联合接口
+* @author xsl
+* @date 2023/2/16
+*/
+public interface SysUserRoleService extends IService<SysUserRole> {
+
+}

+ 96 - 0
backend/src/main/java/com/jiayue/ssi/service/SysUserService.java

@@ -0,0 +1,96 @@
+package com.jiayue.ssi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.jiayue.ssi.entity.SysApprove;
+import com.jiayue.ssi.entity.SysUser;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+* 用户管理接口
+* @author xsl
+* @date 2023/2/16
+*/
+public interface SysUserService extends IService<SysUser> {
+    /**
+    * 获取所有用户
+    * @return  List<SysUser>
+    */
+    List<SysUser> queryAllUsers();
+    /**
+     * 保存用户
+     * @param sysUser
+     * @return boolean
+     */
+    boolean saveUser(SysUser sysUser);
+    /**
+     * 更新用户
+     * @param sysUser
+     * @return boolean
+     */
+    boolean updateUser(SysUser sysUser);
+    /**
+     * 删除用户
+     * @param id
+     * @return
+     */
+    boolean deleteUser(String id);
+    /**
+     * 根据账号查找
+     * @param username
+     * @return SysUser
+     */
+    SysUser queryUserName(String username);
+    /**
+     * 根据邮箱查找
+     * @param mailbox
+     * @return SysUser
+     */
+    SysUser queryMailBox(String mailbox);
+    /**
+     * 删除用户
+     * @param sysUser
+     * @return
+     */
+    boolean removeUserById(SysUser sysUser);
+    /**
+     * 初始/重置密码
+     * @param id            主键id
+     * @param initPassword  初始密码
+     * @return
+     */
+    boolean resetPassword(Integer id,String initPassword,String checkPassword,String bakPassword);
+    /**
+     * 解锁用户
+     * @param id
+     * @return
+     */
+    boolean relockUserById(Integer id);
+    /**
+     * 用户授权角色
+     *
+     * @param userId 用户ID
+     * @param roleId 角色组
+     */
+    int insertUserAuth(Long userId, Long roleId);
+    /**
+     * 通过角色ID查找用户
+     * @param roleId 角色Id
+     * @return
+     */
+    List<SysUser> selectUserByRole(String roleId);
+    /**
+     * 获取全部用户数据,并且字段解密后的
+     * @return
+     */
+    List<SysUser> queryAllUserByDecrypt();
+    /**
+     * 新增用户
+     * @param id
+     * @param sysApprove
+     * @return
+     */
+    boolean addUserByApprove(Long id, SysApprove sysApprove);
+}

+ 9 - 0
backend/src/main/java/com/jiayue/ssi/service/UmsAdminService.java

@@ -0,0 +1,9 @@
+package com.jiayue.ssi.service;
+
+import cn.dev33.satoken.stp.SaTokenInfo;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.jiayue.ssi.entity.SysUser;
+
+public interface UmsAdminService  extends IService<SysUser> {
+    SaTokenInfo login(String password, SysUser sysUsers);
+}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است