HuRongxin 5 mesiacov pred
commit
28f0f28a6f
100 zmenil súbory, kde vykonal 6741 pridanie a 0 odobranie
  1. 18 0
      .editorconfig
  2. 47 0
      .gitignore
  3. 21 0
      LICENSE
  4. 93 0
      README.md
  5. 534 0
      pom.xml
  6. 25 0
      ruoyi-api/pom.xml
  7. 46 0
      ruoyi-api/ruoyi-api-bom/pom.xml
  8. 28 0
      ruoyi-api/ruoyi-api-resource/pom.xml
  9. 15 0
      ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteBaiduMapService.java
  10. 38 0
      ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileService.java
  11. 53 0
      ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileServiceMock.java
  12. 21 0
      ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMailService.java
  13. 26 0
      ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageService.java
  14. 47 0
      ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageServiceStub.java
  15. 145 0
      ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteSmsService.java
  16. 88 0
      ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/LocationResponse.java
  17. 44 0
      ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteFile.java
  18. 36 0
      ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteSms.java
  19. 33 0
      ruoyi-api/ruoyi-api-system/pom.xml
  20. 20 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteClientService.java
  21. 17 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteConfigService.java
  22. 26 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDataScopeService.java
  23. 37 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDeptService.java
  24. 31 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDictService.java
  25. 27 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteLogService.java
  26. 28 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemotePermissionService.java
  27. 12 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteShopService.java
  28. 52 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteSocialService.java
  29. 45 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteTaskAssigneeService.java
  30. 28 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteTenantService.java
  31. 192 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java
  32. 71 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/SysUserOnline.java
  33. 89 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteLogininforBo.java
  34. 119 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteOperLogBo.java
  35. 129 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteSocialBo.java
  36. 56 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteTaskAssigneeBo.java
  37. 124 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteUserBo.java
  38. 70 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteClientVo.java
  39. 37 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteDeptVo.java
  40. 76 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteDictDataVo.java
  41. 46 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteDictTypeVo.java
  42. 257 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteShopVo.java
  43. 135 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteSocialVo.java
  44. 104 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteTaskAssigneeVo.java
  45. 108 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteTenantVo.java
  46. 73 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteUserVo.java
  47. 151 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/LoginUser.java
  48. 46 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/PostDTO.java
  49. 42 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/RoleDTO.java
  50. 27 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/XcxLoginUser.java
  51. 35 0
      ruoyi-api/ruoyi-api-workflow/pom.xml
  52. 88 0
      ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/RemoteWorkflowService.java
  53. 70 0
      ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/RemoteWorkflowServiceMock.java
  54. 71 0
      ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteCompleteTask.java
  55. 30 0
      ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteFlowCopy.java
  56. 45 0
      ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteStartProcess.java
  57. 30 0
      ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteStartProcessReturn.java
  58. 41 0
      ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/event/ProcessDeleteEvent.java
  59. 72 0
      ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/event/ProcessEvent.java
  60. 65 0
      ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/event/ProcessTaskEvent.java
  61. 27 0
      ruoyi-auth/Dockerfile
  62. 139 0
      ruoyi-auth/pom.xml
  63. 23 0
      ruoyi-auth/src/main/java/org/dromara/auth/RuoYiAuthApplication.java
  64. 88 0
      ruoyi-auth/src/main/java/org/dromara/auth/captcha/UnsignedMathGenerator.java
  65. 62 0
      ruoyi-auth/src/main/java/org/dromara/auth/config/CaptchaConfig.java
  66. 87 0
      ruoyi-auth/src/main/java/org/dromara/auth/controller/CaptchaController.java
  67. 231 0
      ruoyi-auth/src/main/java/org/dromara/auth/controller/TokenController.java
  68. 16 0
      ruoyi-auth/src/main/java/org/dromara/auth/domain/convert/TenantVoConvert.java
  69. 25 0
      ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/CaptchaVo.java
  70. 25 0
      ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/LoginTenantVo.java
  71. 54 0
      ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/LoginVo.java
  72. 28 0
      ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/TenantListVo.java
  73. 35 0
      ruoyi-auth/src/main/java/org/dromara/auth/enums/CaptchaCategory.java
  74. 29 0
      ruoyi-auth/src/main/java/org/dromara/auth/enums/CaptchaType.java
  75. 31 0
      ruoyi-auth/src/main/java/org/dromara/auth/form/EmailLoginBody.java
  76. 32 0
      ruoyi-auth/src/main/java/org/dromara/auth/form/PasswordLoginBody.java
  77. 37 0
      ruoyi-auth/src/main/java/org/dromara/auth/form/RegisterBody.java
  78. 29 0
      ruoyi-auth/src/main/java/org/dromara/auth/form/SmsLoginBody.java
  79. 35 0
      ruoyi-auth/src/main/java/org/dromara/auth/form/SocialLoginBody.java
  80. 28 0
      ruoyi-auth/src/main/java/org/dromara/auth/form/XcxLoginBody.java
  81. 168 0
      ruoyi-auth/src/main/java/org/dromara/auth/listener/UserActionListener.java
  82. 46 0
      ruoyi-auth/src/main/java/org/dromara/auth/properties/CaptchaProperties.java
  83. 29 0
      ruoyi-auth/src/main/java/org/dromara/auth/properties/UserPasswordProperties.java
  84. 44 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/IAuthStrategy.java
  85. 265 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/SysLoginService.java
  86. 86 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/EmailAuthStrategy.java
  87. 107 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/PasswordAuthStrategy.java
  88. 86 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SmsAuthStrategy.java
  89. 102 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SocialAuthStrategy.java
  90. 91 0
      ruoyi-auth/src/main/java/org/dromara/auth/service/impl/XcxAuthStrategy.java
  91. 34 0
      ruoyi-auth/src/main/resources/application.yml
  92. 10 0
      ruoyi-auth/src/main/resources/banner.txt
  93. 28 0
      ruoyi-auth/src/main/resources/logback-plus.xml
  94. 59 0
      ruoyi-common/pom.xml
  95. 181 0
      ruoyi-common/ruoyi-common-alibaba-bom/pom.xml
  96. 21 0
      ruoyi-common/ruoyi-common-annotation/pom.xml
  97. 17 0
      ruoyi-common/ruoyi-common-annotation/src/main/java/org/dromara/common/annotation/ano/IgnoreTenant.java
  98. 73 0
      ruoyi-common/ruoyi-common-annotation/src/main/java/org/dromara/common/annotation/aspect/TenantIgnoreAspect.java
  99. 1 0
      ruoyi-common/ruoyi-common-annotation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  100. 52 0
      ruoyi-common/ruoyi-common-baidumap/pom.xml

+ 18 - 0
.editorconfig

@@ -0,0 +1,18 @@
+# http://editorconfig.org
+root = true
+
+# 空格替代Tab缩进在各种编辑工具下效果一致
+[*]
+indent_style = space
+indent_size = 4
+charset = utf-8
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{json,yml,yaml}]
+indent_size = 2
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 47 - 0
.gitignore

@@ -0,0 +1,47 @@
+######################################################################
+# Build Tools
+
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+######################################################################
+# IDE
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### JRebel ###
+rebel.xml
+### NetBeans ###
+nbproject/private/
+build/*
+nbbuild/
+nbdist/
+.nb-gradle/
+
+######################################################################
+# Others
+*.log
+*.xml.versionsBackup
+*.swp
+
+!*/build/*.java
+!*/build/*.html
+!*/build/*.xml
+
+.flattened-pom.xml

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 RuoYi-Cloud-Plus
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 93 - 0
README.md

@@ -0,0 +1,93 @@
+# photo-studio
+
+
+
+## Getting started
+
+To make it easy for you to get started with GitLab, here's a list of recommended next steps.
+
+Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
+
+## Add your files
+
+- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
+- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
+
+```
+cd existing_repo
+git remote add origin http://192.168.1.121/yp/photo-studio.git
+git branch -M main
+git push -uf origin main
+```
+
+## Integrate with your tools
+
+- [ ] [Set up project integrations](http://192.168.1.121/yp/photo-studio/-/settings/integrations)
+
+## Collaborate with your team
+
+- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
+- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
+- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
+- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
+- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
+
+## Test and Deploy
+
+Use the built-in continuous integration in GitLab.
+
+- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
+- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
+- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
+- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
+- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
+
+***
+
+# Editing this README
+
+When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
+
+## Suggestions for a good README
+
+Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
+
+## Name
+Choose a self-explaining name for your project.
+
+## Description
+Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
+
+## Badges
+On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
+
+## Visuals
+Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
+
+## Installation
+Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
+
+## Usage
+Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
+
+## Support
+Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
+
+## Roadmap
+If you have ideas for releases in the future, it is a good idea to list them in the README.
+
+## Contributing
+State if you are open to contributions and what your requirements are for accepting them.
+
+For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
+
+You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
+
+## Authors and acknowledgment
+Show your appreciation to those who have contributed to the project.
+
+## License
+For open source projects, say how it is licensed.
+
+## Project status
+If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.

+ 534 - 0
pom.xml

@@ -0,0 +1,534 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.dromara</groupId>
+    <artifactId>ruoyi-cloud-plus</artifactId>
+    <version>${revision}</version>
+
+    <name>RuoYi-Cloud-Plus</name>
+    <url>https://gitee.com/dromara/RuoYi-Cloud-Plus</url>
+    <description>Dromara RuoYi-Cloud-Plus微服务系统</description>
+
+    <properties>
+        <revision>2.4.1</revision>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>17</java.version>
+        <spring-boot.version>3.4.7</spring-boot.version>
+        <spring-cloud.version>2024.0.0</spring-cloud.version>
+        <spring-boot-admin.version>3.4.7</spring-boot-admin.version>
+        <mybatis.version>3.5.16</mybatis.version>
+        <mybatis-plus.version>3.5.12</mybatis-plus.version>
+        <p6spy.version>3.9.1</p6spy.version>
+        <dynamic-ds.version>4.3.1</dynamic-ds.version>
+        <velocity.version>2.3</velocity.version>
+        <swagger.core.version>2.2.30</swagger.core.version>
+        <springdoc.version>2.8.8</springdoc.version>
+        <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
+        <fastexcel.version>1.2.0</fastexcel.version>
+        <hutool.version>5.8.38</hutool.version>
+        <redisson.version>3.50.0</redisson.version>
+        <lock4j.version>2.2.7</lock4j.version>
+        <snailjob.version>1.5.0</snailjob.version>
+        <satoken.version>1.44.0</satoken.version>
+        <lombok.version>1.18.36</lombok.version>
+        <logstash.version>7.4</logstash.version>
+        <easy-es.version>3.0.0</easy-es.version>
+        <skywalking-toolkit.version>9.3.0</skywalking-toolkit.version>
+        <bouncycastle.version>1.80</bouncycastle.version>
+        <mapstruct-plus.version>1.4.8</mapstruct-plus.version>
+        <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
+        <justauth.version>1.16.7</justauth.version>
+        <!-- 离线IP地址定位库 -->
+        <ip2region.version>2.7.0</ip2region.version>
+        <!-- 临时修复 fastjson 漏洞 -->
+        <fastjson.version>1.2.83</fastjson.version>
+        <!-- OSS 配置 -->
+        <aws.sdk.version>2.28.22</aws.sdk.version>
+        <!-- SMS 配置 -->
+        <sms4j.version>3.3.4</sms4j.version>
+        <!-- 面向运行时的D-ORM依赖 -->
+        <anyline.version>8.7.2-20250603</anyline.version>
+        <!-- 工作流配置 -->
+        <warm-flow.version>1.7.4</warm-flow.version>
+        <!-- mq配置 -->
+        <rocketmq.version>2.3.0</rocketmq.version>
+
+        <!-- 插件版本 -->
+        <maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
+        <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
+        <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
+        <!-- 打包默认跳过测试 -->
+        <skipTests>true</skipTests>
+    </properties>
+
+    <profiles>
+        <profile>
+            <id>dev</id>
+            <properties>
+                <!-- 环境标识,需要与配置文件的名称相对应 -->
+                <profiles.active>dev</profiles.active>
+                <nacos.server>192.168.1.144:8848</nacos.server>
+                <nacos.discovery.group>DEFAULT_GROUP_DEV</nacos.discovery.group>
+                <nacos.config.group>DEFAULT_GROUP_DEV</nacos.config.group>
+                <nacos.username>nacos</nacos.username>
+                <nacos.password>nacos</nacos.password>
+                <logstash.address>127.0.0.1:4560</logstash.address>
+            </properties>
+        </profile>
+        <profile>
+            <id>hrx</id>
+            <properties>
+                <!-- 环境标识,需要与配置文件的名称相对应 -->
+                <profiles.active>64159a31-3bc1-4ef6-b85a-eccd1e2dcc53</profiles.active>
+                <nacos.server>192.168.1.144:8848</nacos.server>
+                <nacos.discovery.group>DEFAULT_GROUP</nacos.discovery.group>
+                <nacos.config.group>DEFAULT_GROUP</nacos.config.group>
+                <nacos.username>nacos</nacos.username>
+                <nacos.password>nacos</nacos.password>
+                <logstash.address>127.0.0.1:4560</logstash.address>
+            </properties>
+            <activation>
+                <!-- 默认环境 -->
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+        <profile>
+            <id>prod</id>
+            <properties>
+                <profiles.active>prod</profiles.active>
+                <nacos.server>192.168.1.144:8848</nacos.server>
+                <nacos.discovery.group>DEFAULT_GROUP</nacos.discovery.group>
+                <nacos.config.group>DEFAULT_GROUP</nacos.config.group>
+                <nacos.username>nacos</nacos.username>
+                <nacos.password>nacos</nacos.password>
+                <logstash.address>127.0.0.1:4560</logstash.address>
+            </properties>
+        </profile>
+    </profiles>
+
+    <!-- 依赖声明 -->
+    <dependencyManagement>
+        <dependencies>
+
+            <!-- SpringCloud 微服务 -->
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring-cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
+            <!-- SpringCloud Alibaba 微服务 -->
+            <dependency>
+                <groupId>org.dromara</groupId>
+                <artifactId>ruoyi-common-alibaba-bom</artifactId>
+                <version>${revision}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
+            <!-- SpringBoot 依赖配置 -->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring-boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
+            <!-- hutool 的依赖配置-->
+            <dependency>
+                <groupId>cn.hutool</groupId>
+                <artifactId>hutool-bom</artifactId>
+                <version>${hutool.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
+            <!-- JustAuth 的依赖配置-->
+            <dependency>
+                <groupId>me.zhyd.oauth</groupId>
+                <artifactId>JustAuth</artifactId>
+                <version>${justauth.version}</version>
+            </dependency>
+
+            <!-- common 的依赖配置-->
+            <dependency>
+                <groupId>org.dromara</groupId>
+                <artifactId>ruoyi-common-bom</artifactId>
+                <version>${revision}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
+            <!-- api 的依赖配置-->
+            <dependency>
+                <groupId>org.dromara</groupId>
+                <artifactId>ruoyi-api-bom</artifactId>
+                <version>${revision}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-core</artifactId>
+                <version>${satoken.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-spring-boot3-starter</artifactId>
+                <version>${satoken.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.mybatis</groupId>
+                <artifactId>mybatis</artifactId>
+                <version>${mybatis.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
+                <version>${mybatis-plus.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-jsqlparser</artifactId>
+                <version>${mybatis-plus.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-annotation</artifactId>
+                <version>${mybatis-plus.version}</version>
+            </dependency>
+            <!-- sql性能分析插件 -->
+            <dependency>
+                <groupId>p6spy</groupId>
+                <artifactId>p6spy</artifactId>
+                <version>${p6spy.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>io.swagger.core.v3</groupId>
+                <artifactId>swagger-annotations</artifactId>
+                <version>${swagger.core.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.springdoc</groupId>
+                <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
+                <version>${springdoc.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.github.therapi</groupId>
+                <artifactId>therapi-runtime-javadoc</artifactId>
+                <version>${therapi-javadoc.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.projectlombok</groupId>
+                <artifactId>lombok</artifactId>
+                <version>${lombok.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.idev.excel</groupId>
+                <artifactId>fastexcel</artifactId>
+                <version>${fastexcel.version}</version>
+            </dependency>
+
+            <!-- 代码生成使用模板 -->
+            <dependency>
+                <groupId>org.apache.velocity</groupId>
+                <artifactId>velocity-engine-core</artifactId>
+                <version>${velocity.version}</version>
+            </dependency>
+
+            <!--redisson-->
+            <dependency>
+                <groupId>org.redisson</groupId>
+                <artifactId>redisson-spring-boot-starter</artifactId>
+                <version>${redisson.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
+                <version>${lock4j.version}</version>
+            </dependency>
+
+            <!--  SnailJob Client -->
+            <dependency>
+                <groupId>com.aizuda</groupId>
+                <artifactId>snail-job-client-starter</artifactId>
+                <version>${snailjob.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.aizuda</groupId>
+                <artifactId>snail-job-client-job-core</artifactId>
+                <version>${snailjob.version}</version>
+            </dependency>
+
+            <!-- 加密包引入 -->
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcprov-jdk15to18</artifactId>
+                <version>${bouncycastle.version}</version>
+            </dependency>
+
+            <!-- logstash -->
+            <dependency>
+                <groupId>net.logstash.logback</groupId>
+                <artifactId>logstash-logback-encoder</artifactId>
+                <version>${logstash.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.dromara.easy-es</groupId>
+                <artifactId>easy-es-boot-starter</artifactId>
+                <version>${easy-es.version}</version>
+            </dependency>
+
+            <!-- skywalking 整合 logback -->
+            <dependency>
+                <groupId>org.apache.skywalking</groupId>
+                <artifactId>apm-toolkit-logback-1.x</artifactId>
+                <version>${skywalking-toolkit.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.skywalking</groupId>
+                <artifactId>apm-toolkit-trace</artifactId>
+                <version>${skywalking-toolkit.version}</version>
+            </dependency>
+
+            <!--  AWS SDK for Java 2.x  -->
+            <dependency>
+                <groupId>software.amazon.awssdk</groupId>
+                <artifactId>s3</artifactId>
+                <version>${aws.sdk.version}</version>
+            </dependency>
+            <!-- 基于 AWS CRT 的 S3 客户端的性能增强的 S3 传输管理器 -->
+            <dependency>
+                <groupId>software.amazon.awssdk</groupId>
+                <artifactId>s3-transfer-manager</artifactId>
+                <version>${aws.sdk.version}</version>
+            </dependency>
+            <!-- 将基于 Netty 的 HTTP 客户端从类路径中移除 -->
+            <dependency>
+                <groupId>software.amazon.awssdk</groupId>
+                <artifactId>netty-nio-client</artifactId>
+                <version>${aws.sdk.version}</version>
+            </dependency>
+
+            <!--短信sms4j-->
+            <dependency>
+                <groupId>org.dromara.sms4j</groupId>
+                <artifactId>sms4j-spring-boot-starter</artifactId>
+                <version>${sms4j.version}</version>
+            </dependency>
+
+            <!-- 离线IP地址定位库 ip2region -->
+            <dependency>
+                <groupId>org.lionsoul</groupId>
+                <artifactId>ip2region</artifactId>
+                <version>${ip2region.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>fastjson</artifactId>
+                <version>${fastjson.version}</version>
+            </dependency>
+
+            <!-- dubbo 专用 -->
+            <dependency>
+                <groupId>redis.clients</groupId>
+                <artifactId>jedis</artifactId>
+                <version>5.1.0</version>
+            </dependency>
+
+            <dependency>
+                <groupId>io.github.linpeilie</groupId>
+                <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
+                <version>${mapstruct-plus.version}</version>
+            </dependency>
+
+            <!--消息队列-->
+            <dependency>
+                <groupId>org.apache.rocketmq</groupId>
+                <artifactId>rocketmq-spring-boot-starter</artifactId>
+                <version>${rocketmq.version}</version>
+            </dependency>
+
+            <!-- Warm-Flow国产工作流引擎, 在线文档:http://warm-flow.cn/ -->
+            <dependency>
+                <groupId>org.dromara.warm</groupId>
+                <artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
+                <version>${warm-flow.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.dromara.warm</groupId>
+                <artifactId>warm-flow-plugin-ui-sb-web</artifactId>
+                <version>${warm-flow.version}</version>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+
+    <modules>
+        <module>ruoyi-auth</module>
+        <module>ruoyi-gateway</module>
+        <module>ruoyi-visual</module>
+        <module>ruoyi-modules</module>
+        <module>ruoyi-api</module>
+        <module>ruoyi-common</module>
+        <module>ruoyi-example</module>
+    </modules>
+    <packaging>pom</packaging>
+
+    <dependencies>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${maven-compiler-plugin.version}</version>
+                <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                    <encoding>${project.build.sourceEncoding}</encoding>
+                    <annotationProcessorPaths>
+                        <path>
+                            <groupId>com.github.therapi</groupId>
+                            <artifactId>therapi-runtime-javadoc-scribe</artifactId>
+                            <version>0.15.0</version>
+                        </path>
+                        <path>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                            <version>${lombok.version}</version>
+                        </path>
+                        <path>
+                            <groupId>org.springframework.boot</groupId>
+                            <artifactId>spring-boot-configuration-processor</artifactId>
+                            <version>${spring-boot.version}</version>
+                        </path>
+                        <path>
+                            <groupId>io.github.linpeilie</groupId>
+                            <artifactId>mapstruct-plus-processor</artifactId>
+                            <version>${mapstruct-plus.version}</version>
+                        </path>
+                        <path>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok-mapstruct-binding</artifactId>
+                            <version>${mapstruct-plus.lombok.version}</version>
+                        </path>
+                    </annotationProcessorPaths>
+                    <compilerArgs>
+                        <arg>-parameters</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
+            <!-- 单元测试使用 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${maven-surefire-plugin.version}</version>
+                <configuration>
+                    <argLine>-Dfile.encoding=UTF-8</argLine>
+                    <!-- 根据打包环境执行对应的@Tag测试方法 -->
+                    <groups>${profiles.active}</groups>
+                    <!-- 排除标签 -->
+                    <excludedGroups>exclude</excludedGroups>
+                </configuration>
+            </plugin>
+            <!-- 统一版本号管理 -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>flatten-maven-plugin</artifactId>
+                <version>${flatten-maven-plugin.version}</version>
+                <configuration>
+                    <updatePomFile>true</updatePomFile>
+                    <flattenMode>resolveCiFriendliesOnly</flattenMode>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>flatten</id>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>flatten</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>flatten.clean</id>
+                        <phase>clean</phase>
+                        <goals>
+                            <goal>clean</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <!-- 关闭过滤 -->
+                <filtering>false</filtering>
+            </resource>
+            <resource>
+                <directory>src/main/webapp/</directory>
+            </resource>
+            <resource>
+                <directory>src/main/resources</directory>
+                <!-- 引入所有 匹配文件进行过滤 -->
+                <includes>
+                    <include>application*</include>
+                    <include>bootstrap*</include>
+                    <include>logback*</include>
+                </includes>
+                <!-- 启用过滤 即该资源中的变量将会被过滤器中的值替换 -->
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+    </build>
+
+    <repositories>
+        <repository>
+            <id>public</id>
+            <name>huawei nexus</name>
+            <url>https://mirrors.huaweicloud.com/repository/maven/</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+        </repository>
+    </repositories>
+
+    <pluginRepositories>
+        <pluginRepository>
+            <id>public</id>
+            <name>huawei nexus</name>
+            <url>https://mirrors.huaweicloud.com/repository/maven/</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </pluginRepository>
+    </pluginRepositories>
+
+</project>
+

+ 25 - 0
ruoyi-api/pom.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-cloud-plus</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <modules>
+        <module>ruoyi-api-bom</module>
+        <module>ruoyi-api-system</module>
+        <module>ruoyi-api-resource</module>
+        <module>ruoyi-api-workflow</module>
+    </modules>
+
+    <artifactId>ruoyi-api</artifactId>
+    <packaging>pom</packaging>
+
+    <description>
+        ruoyi-api系统接口
+    </description>
+
+</project>

+ 46 - 0
ruoyi-api/ruoyi-api-bom/pom.xml

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.dromara</groupId>
+    <artifactId>ruoyi-api-bom</artifactId>
+    <packaging>pom</packaging>
+    <version>${revision}</version>
+
+    <description>
+        ruoyi-api-bom api依赖项
+    </description>
+
+    <properties>
+        <revision>2.4.1</revision>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- 系统接口 -->
+            <dependency>
+                <groupId>org.dromara</groupId>
+                <artifactId>ruoyi-api-system</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 资源服务接口 -->
+            <dependency>
+                <groupId>org.dromara</groupId>
+                <artifactId>ruoyi-api-resource</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- workflow接口 -->
+            <dependency>
+                <groupId>org.dromara</groupId>
+                <artifactId>ruoyi-api-workflow</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+</project>

+ 28 - 0
ruoyi-api/ruoyi-api-resource/pom.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-api</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ruoyi-api-resource</artifactId>
+
+    <description>
+        ruoyi-api-resource 资源服务接口模块
+    </description>
+
+    <dependencies>
+
+        <!-- RuoYi Common Core-->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-core</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 15 - 0
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteBaiduMapService.java

@@ -0,0 +1,15 @@
+package org.dromara.resource.api;
+
+import org.dromara.resource.api.domain.LocationResponse;
+
+import java.util.List;
+
+public interface RemoteBaiduMapService {
+    LocationResponse geocoding(String address);
+
+    LocationResponse reverseGeocoding(double lat, double lng, String coordtype);
+
+    List<LocationResponse> suggestion(String keyword, String region, boolean cityLimit);
+
+    List<LocationResponse> batchGeocoding(List<String> addresses);
+}

+ 38 - 0
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileService.java

@@ -0,0 +1,38 @@
+package org.dromara.resource.api;
+
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.resource.api.domain.RemoteFile;
+
+import java.util.List;
+
+/**
+ * 文件服务
+ *
+ * @author Lion Li
+ */
+public interface RemoteFileService {
+
+    /**
+     * 上传文件
+     *
+     * @param file 文件信息
+     * @return 结果
+     */
+    RemoteFile upload(String name, String originalFilename, String contentType, byte[] file) throws ServiceException;
+
+    /**
+     * 通过ossId查询对应的url
+     *
+     * @param ossIds ossId串逗号分隔
+     * @return url串逗号分隔
+     */
+    String selectUrlByIds(String ossIds);
+
+    /**
+     * 通过ossId查询列表
+     *
+     * @param ossIds ossId串逗号分隔
+     * @return 列表
+     */
+    List<RemoteFile> selectByIds(String ossIds);
+}

+ 53 - 0
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileServiceMock.java

@@ -0,0 +1,53 @@
+package org.dromara.resource.api;
+
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.resource.api.domain.RemoteFile;
+
+import java.util.List;
+
+/**
+ * 文件服务(降级处理)
+ *
+ * @author Lion Li
+ */
+@Slf4j
+public class RemoteFileServiceMock implements RemoteFileService {
+
+    /**
+     * 上传文件
+     *
+     * @param file 文件信息
+     * @return 结果
+     */
+    @Override
+    public RemoteFile upload(String name, String originalFilename, String contentType, byte[] file) {
+        log.warn("服务调用异常 -> 降级处理");
+        return null;
+    }
+
+    /**
+     * 通过ossId查询对应的url
+     *
+     * @param ossIds ossId串逗号分隔
+     * @return url串逗号分隔
+     */
+    @Override
+    public String selectUrlByIds(String ossIds) {
+        log.warn("服务调用异常 -> 降级处理");
+        return StringUtils.EMPTY;
+    }
+
+    /**
+     * 通过ossId查询列表
+     *
+     * @param ossIds ossId串逗号分隔
+     * @return 列表
+     */
+    @Override
+    public List<RemoteFile> selectByIds(String ossIds) {
+        log.warn("服务调用异常 -> 降级处理");
+        return List.of();
+    }
+
+}

+ 21 - 0
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMailService.java

@@ -0,0 +1,21 @@
+package org.dromara.resource.api;
+
+import org.dromara.common.core.exception.ServiceException;
+
+/**
+ * 邮件服务
+ *
+ * @author Lion Li
+ */
+public interface RemoteMailService {
+
+    /**
+     * 发送邮件
+     *
+     * @param to      接收人
+     * @param subject 标题
+     * @param text    内容
+     */
+    void send(String to, String subject, String text) throws ServiceException;
+
+}

+ 26 - 0
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageService.java

@@ -0,0 +1,26 @@
+package org.dromara.resource.api;
+
+import java.util.List;
+
+/**
+ * 消息服务
+ *
+ * @author Lion Li
+ */
+public interface RemoteMessageService {
+
+    /**
+     * 发送消息
+     *
+     * @param sessionKey session主键 一般为用户id
+     * @param message    消息文本
+     */
+    void publishMessage(List<Long> sessionKey, String message);
+
+    /**
+     * 发布订阅的消息(群发)
+     *
+     * @param message 消息内容
+     */
+    void publishAll(String message);
+}

+ 47 - 0
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageServiceStub.java

@@ -0,0 +1,47 @@
+package org.dromara.resource.api;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+
+/**
+ * 消息服务
+ *
+ * @author Lion Li
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class RemoteMessageServiceStub implements RemoteMessageService {
+
+    private final RemoteMessageService remoteMessageService;
+
+    /**
+     * 发送消息
+     *
+     * @param sessionKey session主键 一般为用户id
+     * @param message    消息文本
+     */
+    @Override
+    public void publishMessage(List<Long> sessionKey, String message) {
+        try {
+            remoteMessageService.publishMessage(sessionKey, message);
+        } catch (Exception e) {
+            log.warn("推送功能未开启或服务未找到");
+        }
+    }
+
+    /**
+     * 发布订阅的消息(群发)
+     *
+     * @param message 消息内容
+     */
+    @Override
+    public void publishAll(String message) {
+        try {
+            remoteMessageService.publishAll(message);
+        } catch (Exception e) {
+            log.warn("推送功能未开启或服务未找到");
+        }
+    }
+}

+ 145 - 0
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteSmsService.java

@@ -0,0 +1,145 @@
+package org.dromara.resource.api;
+
+import org.dromara.resource.api.domain.RemoteSms;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+
+/**
+ * 短信服务
+ *
+ * @author Feng
+ */
+public interface RemoteSmsService {
+
+    /**
+     * 同步方法:发送固定消息模板短信
+     *
+     * @param phone   目标手机号
+     * @param message 短信内容
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    RemoteSms sendMessage(String phone, String message);
+
+    /**
+     * 同步方法:发送固定消息模板多模板参数短信
+     *
+     * @param phone    目标手机号
+     * @param messages 短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    RemoteSms sendMessage(String phone, LinkedHashMap<String, String> messages);
+
+    /**
+     * 同步方法:使用自定义模板发送短信
+     *
+     * @param phone      目标手机号
+     * @param templateId 短信模板ID
+     * @param messages   短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    RemoteSms sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages);
+
+    /**
+     * 同步方法:群发固定模板短信
+     *
+     * @param phones  目标手机号列表(1~1000)
+     * @param message 短信内容
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    RemoteSms messageTexting(List<String> phones, String message);
+
+    /**
+     * 同步方法:使用自定义模板群发短信
+     *
+     * @param phones     目标手机号列表(1~1000)(1~1000)
+     * @param templateId 短信模板ID
+     * @param messages   短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    RemoteSms messageTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages);
+
+    /**
+     * 异步方法:发送固定消息模板短信
+     *
+     * @param phone   目标手机号
+     * @param message 短信内容
+     */
+    void sendMessageAsync(String phone, String message);
+
+    /**
+     * 异步方法:使用自定义模板发送短信
+     *
+     * @param phone      目标手机号
+     * @param templateId 短信模板ID
+     * @param messages   短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     */
+    void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages);
+
+    /**
+     * 延迟发送:发送固定消息模板短信
+     *
+     * @param phone       目标手机号
+     * @param message     短信内容
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    void delayMessage(String phone, String message, Long delayedTime);
+
+    /**
+     * 延迟发送:使用自定义模板发送定时短信
+     *
+     * @param phone       目标手机号
+     * @param templateId  短信模板ID
+     * @param messages    短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    void delayMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime);
+
+    /**
+     * 延迟群发:群发延迟短信
+     *
+     * @param phones      目标手机号列表(1~1000)
+     * @param message     短信内容
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    void delayMessageTexting(List<String> phones, String message, Long delayedTime);
+
+    /**
+     * 延迟群发:使用自定义模板发送群体延迟短信
+     *
+     * @param phones      目标手机号列表(1~1000)
+     * @param templateId  短信模板ID
+     * @param messages    短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    void delayMessageTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime);
+
+    /**
+     * 加入黑名单
+     *
+     * @param phone 手机号
+     */
+    void addBlacklist(String phone);
+
+    /**
+     * 加入黑名单
+     *
+     * @param phones 手机号列表
+     */
+    void addBlacklist(List<String> phones);
+
+    /**
+     * 移除黑名单
+     *
+     * @param phone 手机号
+     */
+    void removeBlacklist(String phone);
+
+    /**
+     * 移除黑名单
+     *
+     * @param phones 手机号
+     */
+    void removeBlacklist(List<String> phones);
+
+}

+ 88 - 0
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/LocationResponse.java

@@ -0,0 +1,88 @@
+package org.dromara.resource.api.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 位置信息响应
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class LocationResponse implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 地点名称
+     */
+    private String name;
+
+    /**
+     * 纬度
+     */
+    private Double lat;
+
+    /**
+     * 经度
+     */
+    private Double lng;
+
+    /**
+     * 完整地址
+     */
+    private String address;
+
+    /**
+     * 省份
+     */
+    private String province;
+
+    /**
+     * 城市
+     */
+    private String city;
+
+    /**
+     * 区县
+     */
+    private String district;
+
+    /**
+     * 街道
+     */
+    private String street;
+
+    /**
+     * 门牌号
+     */
+    private String streetNumber;
+
+    /**
+     * 城市编码
+     */
+    private String cityCode;
+
+    /**
+     * 地点UID
+     */
+    private String uid;
+
+    /**
+     * 精确度
+     */
+    private Integer precise;
+
+    /**
+     * 可信度
+     */
+    private Integer confidence;
+
+    /**
+     * 地址级别
+     */
+    private String level;
+}

+ 44 - 0
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteFile.java

@@ -0,0 +1,44 @@
+package org.dromara.resource.api.domain;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 文件信息
+ *
+ * @author ruoyi
+ */
+@Data
+public class RemoteFile implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * oss主键
+     */
+    private Long ossId;
+
+    /**
+     * 文件名称
+     */
+    private String name;
+
+    /**
+     * 文件地址
+     */
+    private String url;
+
+    /**
+     * 原名
+     */
+    private String originalName;
+
+    /**
+     * 文件后缀名
+     */
+    private String fileSuffix;
+
+}

+ 36 - 0
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteSms.java

@@ -0,0 +1,36 @@
+package org.dromara.resource.api.domain;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 文件信息
+ *
+ * @author ruoyi
+ */
+@Data
+public class RemoteSms implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 是否成功
+     */
+    private Boolean success;
+
+    /**
+     * 配置标识名 如未配置取对应渠道名例如 Alibaba
+     */
+    private String configId;
+
+    /**
+     * 厂商原返回体
+     * <p>
+     * 可自行转换为 SDK 对应的 SendSmsResponse
+     */
+    private String response;
+
+}

+ 33 - 0
ruoyi-api/ruoyi-api-system/pom.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-api</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ruoyi-api-system</artifactId>
+
+    <description>
+        ruoyi-api-system系统接口模块
+    </description>
+
+    <dependencies>
+
+        <!-- RuoYi Common Core-->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-excel</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 20 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteClientService.java

@@ -0,0 +1,20 @@
+package org.dromara.system.api;
+
+import org.dromara.system.api.domain.vo.RemoteClientVo;
+
+/**
+ * 客户端服务
+ *
+ * @author Michelle.Chung
+ */
+public interface RemoteClientService {
+
+    /**
+     * 根据客户端id获取客户端详情
+     *
+     * @param clientId 客户端id
+     * @return 客户端对象
+     */
+    RemoteClientVo queryByClientId(String clientId);
+
+}

+ 17 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteConfigService.java

@@ -0,0 +1,17 @@
+package org.dromara.system.api;
+
+/**
+ * 配置服务
+ *
+ * @author Michelle.Chung
+ */
+public interface RemoteConfigService {
+
+    /**
+     * 获取注册开关
+     * @param tenantId 租户id
+     * @return true开启,false关闭
+     */
+    boolean selectRegisterEnabled(String tenantId);
+
+}

+ 26 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDataScopeService.java

@@ -0,0 +1,26 @@
+package org.dromara.system.api;
+
+/**
+ * 数据权限服务
+ *
+ * @author Lion Li
+ */
+public interface RemoteDataScopeService {
+
+    /**
+     * 获取角色自定义权限语句
+     *
+     * @param roleId 角色ID
+     * @return 返回角色的自定义权限语句,如果没有找到则返回 null
+     */
+    String getRoleCustom(Long roleId);
+
+    /**
+     * 获取部门和下级权限语句
+     *
+     * @param deptId 部门ID
+     * @return 返回部门及其下级的权限语句,如果没有找到则返回 null
+     */
+    String getDeptAndChild(Long deptId);
+
+}

+ 37 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDeptService.java

@@ -0,0 +1,37 @@
+package org.dromara.system.api;
+
+import org.dromara.system.api.domain.vo.RemoteDeptVo;
+
+import java.util.List;
+
+/**
+ * 部门服务
+ *
+ * @author Lion Li
+ */
+public interface RemoteDeptService {
+
+    /**
+     * 通过部门ID查询部门名称
+     *
+     * @param deptIds 部门ID串逗号分隔
+     * @return 部门名称串逗号分隔
+     */
+    String selectDeptNameByIds(String deptIds);
+
+    /**
+     * 根据部门ID查询部门负责人
+     *
+     * @param deptId 部门ID,用于指定需要查询的部门
+     * @return 返回该部门的负责人ID
+     */
+    Long selectDeptLeaderById(Long deptId);
+
+    /**
+     * 查询部门
+     *
+     * @return 部门列表
+     */
+    List<RemoteDeptVo> selectDeptsByList();
+
+}

+ 31 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDictService.java

@@ -0,0 +1,31 @@
+package org.dromara.system.api;
+
+import org.dromara.system.api.domain.vo.RemoteDictDataVo;
+import org.dromara.system.api.domain.vo.RemoteDictTypeVo;
+
+import java.util.List;
+
+/**
+ * 字典服务
+ *
+ * @author Lion Li
+ */
+public interface RemoteDictService {
+
+    /**
+     * 根据字典类型查询信息
+     *
+     * @param dictType 字典类型
+     * @return 字典类型
+     */
+    RemoteDictTypeVo selectDictTypeByType(String dictType);
+
+    /**
+     * 根据字典类型查询字典数据
+     *
+     * @param dictType 字典类型
+     * @return 字典数据集合信息
+     */
+    List<RemoteDictDataVo> selectDictDataByType(String dictType);
+
+}

+ 27 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteLogService.java

@@ -0,0 +1,27 @@
+package org.dromara.system.api;
+
+import org.dromara.system.api.domain.bo.RemoteLogininforBo;
+import org.dromara.system.api.domain.bo.RemoteOperLogBo;
+
+/**
+ * 日志服务
+ *
+ * @author Lion Li
+ */
+public interface RemoteLogService {
+
+    /**
+     * 保存系统日志
+     *
+     * @param sysOperLog 日志实体
+     */
+    void saveLog(RemoteOperLogBo sysOperLog);
+
+    /**
+     * 保存访问记录
+     *
+     * @param sysLogininfor 访问实体
+     */
+    void saveLogininfor(RemoteLogininforBo sysLogininfor);
+
+}

+ 28 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemotePermissionService.java

@@ -0,0 +1,28 @@
+package org.dromara.system.api;
+
+import java.util.Set;
+
+/**
+ * 用户权限处理
+ *
+ * @author Lion Li
+ */
+public interface RemotePermissionService {
+
+    /**
+     * 获取角色数据权限
+     *
+     * @param userId  用户id
+     * @return 角色权限信息
+     */
+    Set<String> getRolePermission(Long userId);
+
+    /**
+     * 获取菜单数据权限
+     *
+     * @param userId  用户id
+     * @return 菜单权限信息
+     */
+    Set<String> getMenuPermission(Long userId);
+
+}

+ 12 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteShopService.java

@@ -0,0 +1,12 @@
+package org.dromara.system.api;
+
+import org.dromara.system.api.domain.vo.RemoteShopVo;
+
+import java.util.List;
+
+public interface RemoteShopService {
+
+    List<RemoteShopVo> selectStoreByIds(List<Long> ids);
+
+    RemoteShopVo selectStoreById(String id);
+}

+ 52 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteSocialService.java

@@ -0,0 +1,52 @@
+package org.dromara.system.api;
+
+import org.dromara.system.api.domain.bo.RemoteSocialBo;
+import org.dromara.system.api.domain.vo.RemoteSocialVo;
+
+import java.util.List;
+
+/**
+ * 社会化关系服务
+ *
+ * @author Michelle.Chung
+ */
+public interface RemoteSocialService {
+
+    /**
+     * 根据 authId 查询用户授权信息
+     *
+     * @param authId 认证id
+     * @return 授权信息
+     */
+    List<RemoteSocialVo> selectByAuthId(String authId);
+
+    /**
+     * 查询列表
+     *
+     * @param bo 社会化关系业务对象
+     */
+    List<RemoteSocialVo> queryList(RemoteSocialBo bo);
+
+    /**
+     * 保存社会化关系
+     *
+     * @param bo 社会化关系业务对象
+     */
+    void insertByBo(RemoteSocialBo bo);
+
+    /**
+     * 更新社会化关系
+     *
+     * @param bo 社会化关系业务对象
+     */
+    void updateByBo(RemoteSocialBo bo);
+
+    /**
+     * 删除社会化关系
+     *
+     * @param socialId 社会化关系ID
+     * @return 结果
+     */
+    Boolean deleteWithValidById(Long socialId);
+
+}

+ 45 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteTaskAssigneeService.java

@@ -0,0 +1,45 @@
+package org.dromara.system.api;
+
+import org.dromara.system.api.domain.bo.RemoteTaskAssigneeBo;
+import org.dromara.system.api.domain.vo.RemoteTaskAssigneeVo;
+
+/**
+ * 工作流设计器获取任务执行人
+ *
+ * @author Lion Li
+ */
+public interface RemoteTaskAssigneeService {
+
+    /**
+     * 查询角色并返回任务指派的列表,支持分页
+     *
+     * @param taskQuery 查询条件
+     * @return 办理人
+     */
+    RemoteTaskAssigneeVo selectRolesByTaskAssigneeList(RemoteTaskAssigneeBo taskQuery);
+
+    /**
+     * 查询岗位并返回任务指派的列表,支持分页
+     *
+     * @param taskQuery 查询条件
+     * @return 办理人
+     */
+    RemoteTaskAssigneeVo selectPostsByTaskAssigneeList(RemoteTaskAssigneeBo taskQuery);
+
+    /**
+     * 查询部门并返回任务指派的列表,支持分页
+     *
+     * @param taskQuery 查询条件
+     * @return 办理人
+     */
+    RemoteTaskAssigneeVo selectDeptsByTaskAssigneeList(RemoteTaskAssigneeBo taskQuery);
+
+    /**
+     * 查询用户并返回任务指派的列表,支持分页
+     *
+     * @param taskQuery 查询条件
+     * @return 办理人
+     */
+    RemoteTaskAssigneeVo selectUsersByTaskAssigneeList(RemoteTaskAssigneeBo taskQuery);
+
+}

+ 28 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteTenantService.java

@@ -0,0 +1,28 @@
+package org.dromara.system.api;
+
+
+import org.dromara.system.api.domain.vo.RemoteTenantVo;
+
+import java.util.List;
+
+/**
+ * 租户服务
+ *
+ * @author zhujie
+ */
+public interface RemoteTenantService {
+
+    /**
+     * 根据租户id获取租户详情
+     * @param tenantId 租户id
+     * @return 结果
+     */
+    RemoteTenantVo queryByTenantId(String tenantId);
+
+    /**
+     * 获取租户列表
+     * @return 结果
+     */
+    List<RemoteTenantVo> queryList();
+
+}

+ 192 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java

@@ -0,0 +1,192 @@
+package org.dromara.system.api;
+
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.exception.user.UserException;
+import org.dromara.system.api.domain.bo.RemoteUserBo;
+import org.dromara.system.api.domain.vo.RemoteUserVo;
+import org.dromara.system.api.model.LoginUser;
+import org.dromara.system.api.model.XcxLoginUser;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 用户服务
+ *
+ * @author Lion Li
+ */
+public interface RemoteUserService {
+
+    /**
+     * 通过用户名查询用户信息
+     *
+     * @param username 用户名
+     * @param tenantId 租户id
+     * @return 结果
+     */
+    LoginUser getUserInfo(String username, String tenantId) throws UserException;
+
+    /**
+     * 通过用户id查询用户信息
+     *
+     * @param userId   用户id
+     * @param tenantId 租户id
+     * @return 结果
+     */
+    LoginUser getUserInfo(Long userId, String tenantId) throws UserException;
+
+    /**
+     * 通过手机号查询用户信息
+     *
+     * @param phonenumber 手机号
+     * @param tenantId    租户id
+     * @return 结果
+     */
+    LoginUser getUserInfoByPhonenumber(String phonenumber, String tenantId) throws UserException;
+
+    /**
+     * 通过邮箱查询用户信息
+     *
+     * @param email    邮箱
+     * @param tenantId 租户id
+     * @return 结果
+     */
+    LoginUser getUserInfoByEmail(String email, String tenantId) throws UserException;
+
+    /**
+     * 通过openid查询用户信息
+     *
+     * @param openid openid
+     * @return 结果
+     */
+    XcxLoginUser getUserInfoByOpenid(String openid) throws UserException;
+
+    /**
+     * 注册用户信息
+     *
+     * @param remoteUserBo 用户信息
+     * @return 结果
+     */
+    Boolean registerUserInfo(RemoteUserBo remoteUserBo) throws UserException, ServiceException;
+
+    /**
+     * 通过userId查询用户账户
+     *
+     * @param userId 用户id
+     * @return 结果
+     */
+    String selectUserNameById(Long userId);
+
+    /**
+     * 通过用户ID查询用户昵称
+     *
+     * @param userId 用户id
+     * @return 结果
+     */
+    String selectNicknameById(Long userId);
+
+    /**
+     * 通过用户ID查询用户账户
+     *
+     * @param userIds 用户ID 多个用逗号隔开
+     * @return 用户名称
+     */
+    String selectNicknameByIds(String userIds);
+
+    /**
+     * 通过用户ID查询用户手机号
+     *
+     * @param userId 用户id
+     * @return 用户手机号
+     */
+    String selectPhonenumberById(Long userId);
+
+    /**
+     * 通过用户ID查询用户邮箱
+     *
+     * @param userId 用户id
+     * @return 用户邮箱
+     */
+    String selectEmailById(Long userId);
+
+    /**
+     * 更新用户信息
+     *
+     * @param userId 用户ID
+     * @param ip     IP地址
+     */
+    void recordLoginInfo(Long userId, String ip);
+
+    /**
+     * 通过用户ID查询用户列表
+     *
+     * @param userIds 用户ids
+     * @return 用户列表
+     */
+    List<RemoteUserVo> selectListByIds(List<Long> userIds);
+
+    /**
+     * 通过角色ID查询用户ID
+     *
+     * @param roleIds 角色ids
+     * @return 用户ids
+     */
+    List<Long> selectUserIdsByRoleIds(List<Long> roleIds);
+
+    /**
+     * 通过角色ID查询用户
+     *
+     * @param roleIds 角色ids
+     * @return 用户
+     */
+    List<RemoteUserVo> selectUsersByRoleIds(List<Long> roleIds);
+
+    /**
+     * 通过部门ID查询用户
+     *
+     * @param deptIds 部门ids
+     * @return 用户
+     */
+    List<RemoteUserVo> selectUsersByDeptIds(List<Long> deptIds);
+
+    /**
+     * 通过岗位ID查询用户
+     *
+     * @param postIds 岗位ids
+     * @return 用户
+     */
+    List<RemoteUserVo> selectUsersByPostIds(List<Long> postIds);
+
+    /**
+     * 根据用户 ID 列表查询用户名称映射关系
+     *
+     * @param userIds 用户 ID 列表
+     * @return Map,其中 key 为用户 ID,value 为对应的用户名称
+     */
+    Map<Long, String> selectUserNamesByIds(List<Long> userIds);
+
+    /**
+     * 根据角色 ID 列表查询角色名称映射关系
+     *
+     * @param roleIds 角色 ID 列表
+     * @return Map,其中 key 为角色 ID,value 为对应的角色名称
+     */
+    Map<Long, String> selectRoleNamesByIds(List<Long> roleIds);
+
+    /**
+     * 根据部门 ID 列表查询部门名称映射关系
+     *
+     * @param deptIds 部门 ID 列表
+     * @return Map,其中 key 为部门 ID,value 为对应的部门名称
+     */
+    Map<Long, String> selectDeptNamesByIds(List<Long> deptIds);
+
+    /**
+     * 根据岗位 ID 列表查询岗位名称映射关系
+     *
+     * @param postIds 岗位 ID 列表
+     * @return Map,其中 key 为岗位 ID,value 为对应的岗位名称
+     */
+    Map<Long, String> selectPostNamesByIds(List<Long> postIds);
+
+}

+ 71 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/SysUserOnline.java

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

+ 89 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteLogininforBo.java

@@ -0,0 +1,89 @@
+package org.dromara.system.api.domain.bo;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 系统访问记录表 sys_logininfor
+ *
+ * @author Lion Li
+ */
+@Data
+@NoArgsConstructor
+public class RemoteLogininforBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 访问ID
+     */
+    private Long infoId;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 用户账号
+     */
+    private String userName;
+
+    /**
+     * 客户端
+     */
+    private String clientKey;
+
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+
+    /**
+     * 登录IP地址
+     */
+    private String ipaddr;
+
+    /**
+     * 登录地点
+     */
+    private String loginLocation;
+
+    /**
+     * 浏览器类型
+     */
+    private String browser;
+
+    /**
+     * 操作系统
+     */
+    private String os;
+
+    /**
+     * 登录状态(0成功 1失败)
+     */
+    private String status;
+
+    /**
+     * 提示消息
+     */
+    private String msg;
+
+    /**
+     * 访问时间
+     */
+    private Date loginTime;
+
+    /**
+     * 请求参数
+     */
+    private Map<String, Object> params = new HashMap<>();
+
+}

+ 119 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteOperLogBo.java

@@ -0,0 +1,119 @@
+package org.dromara.system.api.domain.bo;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 操作日志记录表 oper_log
+ *
+ * @author Lion Li
+ */
+@Data
+@NoArgsConstructor
+public class RemoteOperLogBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 日志主键
+     */
+    private Long operId;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 模块标题
+     */
+    private String title;
+
+    /**
+     * 业务类型(0其它 1新增 2修改 3删除)
+     */
+    private Integer businessType;
+
+    /**
+     * 方法名称
+     */
+    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;
+
+    /**
+     * 操作时间
+     */
+    private Date operTime;
+
+    /**
+     * 消耗时间
+     */
+    private Long costTime;
+
+    /**
+     * 请求参数
+     */
+    private Map<String, Object> params = new HashMap<>();
+
+}

+ 129 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteSocialBo.java

@@ -0,0 +1,129 @@
+package org.dromara.system.api.domain.bo;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 社会化关系业务对象 sys_social
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@NoArgsConstructor
+public class RemoteSocialBo implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1L;
+    /**
+     * 主键
+     */
+    private Long id;
+
+    /**
+     * 的唯一ID
+     */
+    private String authId;
+
+    /**
+     * 用户来源
+     */
+    private String source;
+
+    /**
+     * 用户的授权令牌
+     */
+    private String accessToken;
+
+    /**
+     * 用户的授权令牌的有效期,部分平台可能没有
+     */
+    private int expireIn;
+
+    /**
+     * 刷新令牌,部分平台可能没有
+     */
+    private String refreshToken;
+
+    /**
+     * 平台唯一id
+     */
+    private String openId;
+
+    /**
+     * 用户的 ID
+     */
+    private Long userId;
+
+    /**
+     * 平台的授权信息,部分平台可能没有
+     */
+    private String accessCode;
+
+    /**
+     * 用户的 unionid
+     */
+    private String unionId;
+
+    /**
+     * 授予的权限,部分平台可能没有
+     */
+    private String scope;
+
+    /**
+     * 授权的第三方账号
+     */
+    private String userName;
+
+    /**
+     * 授权的第三方昵称
+     */
+    private String nickName;
+
+    /**
+     * 授权的第三方邮箱
+     */
+    private String email;
+
+    /**
+     * 授权的第三方头像地址
+     */
+    private String avatar;
+
+    /**
+     * 个别平台的授权信息,部分平台可能没有
+     */
+    private String tokenType;
+
+    /**
+     * id token,部分平台可能没有
+     */
+    private String idToken;
+
+    /**
+     * 小米平台用户的附带属性,部分平台可能没有
+     */
+    private String macAlgorithm;
+
+    /**
+     * 小米平台用户的附带属性,部分平台可能没有
+     */
+    private String macKey;
+
+    /**
+     * 用户的授权code,部分平台可能没有
+     */
+    private String code;
+
+    /**
+     * Twitter平台用户的附带属性,部分平台可能没有
+     */
+    private String oauthToken;
+
+    /**
+     * Twitter平台用户的附带属性,部分平台可能没有
+     */
+    private String oauthTokenSecret;
+
+}

+ 56 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteTaskAssigneeBo.java

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

+ 124 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteUserBo.java

@@ -0,0 +1,124 @@
+package org.dromara.system.api.domain.bo;
+
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.dromara.common.core.constant.SystemConstants;
+import org.dromara.common.core.xss.Xss;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 用户信息业务对象 sys_user
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@NoArgsConstructor
+public class RemoteUserBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 用户账号
+     */
+    @Xss(message = "用户账号不能包含脚本字符")
+    @NotBlank(message = "用户账号不能为空")
+    @Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符")
+    private String userName;
+
+    /**
+     * 用户昵称
+     */
+    @Xss(message = "用户昵称不能包含脚本字符")
+    @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符")
+    private String nickName;
+
+    /**
+     * 用户类型(sys_user系统用户)
+     */
+    private String userType;
+
+    /**
+     * 用户邮箱
+     */
+    @Email(message = "邮箱格式不正确")
+    @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
+    private String email;
+
+    /**
+     * 手机号码
+     */
+    private String phonenumber;
+
+    /**
+     * 用户性别(0男 1女 2未知)
+     */
+    private String sex;
+
+    /**
+     * 头像地址
+     */
+    private Long avatar;
+
+    /**
+     * 密码
+     */
+    private String password;
+
+    /**
+     * 帐号状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 最后登录IP
+     */
+    private String loginIp;
+
+    /**
+     * 最后登录时间
+     */
+    private Date loginDate;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 数据权限 当前角色ID
+     */
+    private Long roleId;
+
+    public RemoteUserBo(Long userId) {
+        this.userId = userId;
+    }
+
+    public boolean isSuperAdmin() {
+        return SystemConstants.SUPER_ADMIN_ID.equals(this.userId);
+    }
+
+}

+ 70 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteClientVo.java

@@ -0,0 +1,70 @@
+package org.dromara.system.api.domain.vo;
+
+import lombok.Data;
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * 授权管理视图对象 sys_client
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class RemoteClientVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    private Long id;
+
+    /**
+     * 客户端id
+     */
+    private String clientId;
+
+    /**
+     * 客户端key
+     */
+    private String clientKey;
+
+    /**
+     * 客户端秘钥
+     */
+    private String clientSecret;
+
+    /**
+     * 授权类型
+     */
+    private List<String> grantTypeList;
+
+    /**
+     * 授权类型
+     */
+    private String grantType;
+
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+
+    /**
+     * token活跃超时时间
+     */
+    private Long activeTimeout;
+
+    /**
+     * token固定超时时间
+     */
+    private Long timeout;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+}

+ 37 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteDeptVo.java

@@ -0,0 +1,37 @@
+package org.dromara.system.api.domain.vo;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 部门
+ *
+ * @author AprilWind
+ */
+
+@Data
+@NoArgsConstructor
+public class RemoteDeptVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 父部门ID
+     */
+    private Long parentId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+}

+ 76 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteDictDataVo.java

@@ -0,0 +1,76 @@
+package org.dromara.system.api.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 字典数据视图对象 sys_dict_data
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class RemoteDictDataVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 字典编码
+     */
+    private Long dictCode;
+
+    /**
+     * 字典排序
+     */
+    private Integer dictSort;
+
+    /**
+     * 字典标签
+     */
+    private String dictLabel;
+
+    /**
+     * 字典键值
+     */
+    private String dictValue;
+
+    /**
+     * 字典类型
+     */
+    private String dictType;
+
+    /**
+     * 样式属性(其他样式扩展)
+     */
+    private String cssClass;
+
+    /**
+     * 表格回显样式
+     */
+    private String listClass;
+
+    /**
+     * 是否默认(Y是 N否)
+     */
+    private String isDefault;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+}

+ 46 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteDictTypeVo.java

@@ -0,0 +1,46 @@
+package org.dromara.system.api.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 字典类型视图对象 sys_dict_type
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class RemoteDictTypeVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 字典主键
+     */
+    private Long dictId;
+
+    /**
+     * 字典名称
+     */
+    private String dictName;
+
+    /**
+     * 字典类型
+     */
+    private String dictType;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+}

+ 257 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteShopVo.java

@@ -0,0 +1,257 @@
+package org.dromara.system.api.domain.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+
+/**
+ * 门店视图对象 sys_shop
+ *
+ * @author LionLi
+ * @date 2025-10-16
+ */
+@Data
+@ExcelIgnoreUnannotated
+public class RemoteShopVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @ExcelProperty(value = "ID")
+    private Long id;
+
+    /**
+     * 店铺logo
+     */
+    @ExcelProperty(value = "店铺logo")
+    private String storeLogo;
+
+//    @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "storeLogo")
+//    private String storeLogoUrl;
+
+    /**
+     * 店铺名称
+     */
+    @ExcelProperty(value = "店铺名称")
+    private String storeName;
+
+    /**
+     * 店铺描述
+     */
+    @ExcelProperty(value = "店铺描述")
+    private String storeDescribe;
+
+    /**
+     * 区域代码
+     */
+    @ExcelProperty(value = "区域代码")
+    private String areaCode;
+
+    /**
+     * 省份
+     */
+    @ExcelProperty(value = "省份")
+    private String province;
+
+    /**
+     * 市
+     */
+    @ExcelProperty(value = "市")
+    private String city;
+
+    /**
+     * 区
+     */
+    @ExcelProperty(value = "区")
+    private String district;
+
+    /**
+     * 所属用户id
+     */
+    @ExcelProperty(value = "所属用户id")
+    private Long vestInUserId;
+
+    /**
+     * 所属用户
+     */
+    @ExcelProperty(value = "所属用户")
+    private String vestInUser;
+
+    /*客服id*/
+    private Long serviceId;
+
+    /**
+     * 所属品牌id
+     */
+    private Long brandId;
+
+    /**
+     * 品牌名称
+     */
+    @ExcelProperty(value = "品牌名称")
+    private String brandName;
+
+    /**
+     * 店铺排序
+     */
+    @ExcelProperty(value = "店铺排序")
+    private Long storeSort;
+
+    /**
+     * 店铺电话
+     */
+    @ExcelProperty(value = "店铺电话")
+    private String storePhonenumber;
+
+    /**
+     * 小程序分享图
+     */
+    @ExcelProperty(value = "小程序分享图")
+    private String miniSharePic;
+
+    /**
+     * 店铺地址
+     */
+    @ExcelProperty(value = "店铺地址")
+    private String storeAddress;
+
+    /**
+     * 商户套餐
+     */
+    @ExcelProperty(value = "商户套餐")
+    private Long packageId;
+
+    /**
+     * 套餐名称
+     */
+    @ExcelProperty(value = "套餐名称")
+    private String packageName;
+
+    /**
+     * 到期时间
+     */
+    @ExcelProperty(value = "到期时间")
+    private Date expireDate;
+
+    /**
+     * 商户状态
+     */
+    @ExcelProperty(value = "商户状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "store_status")
+    private String storeStatus;
+
+    /**
+     * 帐号状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    private String serviceQrCode;
+
+//    @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "serviceQrCode")
+//    private String serviceQrCodeUrl;
+
+    private String serviceName;
+
+    private String servicePhone;
+
+    /**
+     * 店铺人气
+     */
+    private Long population;
+
+    /**
+     * 人均消费
+     */
+    private Long consume;
+
+    /**
+     * 虚拟销量
+     */
+    private Long virtualSalesVolume;
+
+    /**
+     * 评分等级
+     */
+    private Long ranking;
+
+    /**
+     * 商户幻灯片
+     */
+    private String lanternSlide;
+
+    /**
+     * 幻灯片大小比例-宽
+     */
+    private BigDecimal lanternSlideSizeWide;
+
+    /**
+     * 幻灯片大小比例-高
+     */
+    private BigDecimal lanternSlideSizeHigh;
+
+    /**
+     * 商户相册
+     */
+    private String photoAlbum;
+
+    /**
+     * 店铺详情
+     */
+    private String shopDetails;
+
+    /**
+     * 所属区域
+     */
+    @NotBlank(message = "所属区域不能为空", groups = {AddGroup.class})
+    private String belongArea;
+
+    /**
+     * 地理位置
+     */
+    @NotBlank(message = "地理位置不能为空", groups = {AddGroup.class})
+    private String geographicPosition;
+
+    /**
+     * 创建人
+     */
+    private Long createBy;
+
+    /**
+     * 代理人名称
+     */
+    private String agentName;
+
+    /**
+     * 代理人手机号
+     */
+    private String agentPhone;
+
+    private Date createTime;
+
+//    /**
+//     * 营业时间
+//     */
+//    private List<SysShopBusinessHoursVo> businessHoursVoList;
+
+}

+ 135 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteSocialVo.java

@@ -0,0 +1,135 @@
+package org.dromara.system.api.domain.vo;
+
+import lombok.Data;
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 社会化关系视图对象 sys_social
+ *
+ * @author thiszhc
+ */
+@Data
+public class RemoteSocialVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    private Long id;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 认证唯一ID
+     */
+    private String authId;
+
+    /**
+     * 用户来源
+     */
+    private String source;
+
+    /**
+     * 用户的授权令牌
+     */
+    private String accessToken;
+
+    /**
+     * 用户的授权令牌的有效期,部分平台可能没有
+     */
+    private int expireIn;
+
+    /**
+     * 刷新令牌,部分平台可能没有
+     */
+    private String refreshToken;
+
+    /**
+     * 用户的 open id
+     */
+    private String openId;
+
+    /**
+     * 授权的第三方账号
+     */
+    private String userName;
+
+    /**
+     * 授权的第三方昵称
+     */
+    private String nickName;
+
+    /**
+     * 授权的第三方邮箱
+     */
+    private String email;
+
+    /**
+     * 授权的第三方头像地址
+     */
+    private String avatar;
+
+
+    /**
+     * 平台的授权信息,部分平台可能没有
+     */
+    private String accessCode;
+
+    /**
+     * 用户的 unionid
+     */
+    private String unionId;
+
+    /**
+     * 授予的权限,部分平台可能没有
+     */
+    private String scope;
+
+    /**
+     * 个别平台的授权信息,部分平台可能没有
+     */
+    private String tokenType;
+
+    /**
+     * id token,部分平台可能没有
+     */
+    private String idToken;
+
+    /**
+     * 小米平台用户的附带属性,部分平台可能没有
+     */
+    private String macAlgorithm;
+
+    /**
+     * 小米平台用户的附带属性,部分平台可能没有
+     */
+    private String macKey;
+
+    /**
+     * 用户的授权code,部分平台可能没有
+     */
+    private String code;
+
+    /**
+     * Twitter平台用户的附带属性,部分平台可能没有
+     */
+    private String oauthToken;
+
+    /**
+     * Twitter平台用户的附带属性,部分平台可能没有
+     */
+    private String oauthTokenSecret;
+
+}

+ 104 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteTaskAssigneeVo.java

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

+ 108 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteTenantVo.java

@@ -0,0 +1,108 @@
+package org.dromara.system.api.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 租户视图对象
+ *
+ * @author zhujie
+ */
+@Data
+public class RemoteTenantVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    private Long id;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 联系人
+     */
+    private String contactUserName;
+
+    /**
+     * 联系电话
+     */
+    private String contactPhone;
+
+    /**
+     * 企业名称
+     */
+    private String companyName;
+
+    /**
+     * 统一社会信用代码
+     */
+    private String licenseNumber;
+
+    /**
+     * 地址
+     */
+    private String address;
+
+    /**
+     * 域名
+     */
+    private String domain;
+
+    /**
+     * 企业简介
+     */
+    private String intro;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 租户套餐编号
+     */
+    private Long packageId;
+
+    /**
+     * 过期时间
+     */
+    private Date expireTime;
+
+    /**
+     * 用户数量(-1不限制)
+     */
+    private Long accountCount;
+
+    /**
+     * 租户状态(0正常 1停用)
+     */
+    private String status;
+
+    //扩展字段,暂用于测试
+
+    /**
+     * 品牌名称
+     */
+    private String brandName;
+
+    /**
+     * appId
+     */
+    private String appId;
+
+    /**
+     * appSecret
+     */
+    private String appSecret;
+
+}

+ 73 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteUserVo.java

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

+ 151 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/LoginUser.java

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

+ 46 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/PostDTO.java

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

+ 42 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/RoleDTO.java

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

+ 27 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/XcxLoginUser.java

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

+ 35 - 0
ruoyi-api/ruoyi-api-workflow/pom.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-api</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ruoyi-api-workflow</artifactId>
+
+    <description>
+        ruoyi-api-workflow 工作流接口模块
+    </description>
+
+    <dependencies>
+
+        <!-- RuoYi Common Core-->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-core</artifactId>
+        </dependency>
+
+        <!-- 非必需模块 如果需要跟工作流同步数据 则需要在对应服务内引入bus模块 如果只是调用工作流api则不需要 -->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-bus</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 88 - 0
ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/RemoteWorkflowService.java

@@ -0,0 +1,88 @@
+package org.dromara.workflow.api;
+
+import org.dromara.workflow.api.domain.RemoteCompleteTask;
+import org.dromara.workflow.api.domain.RemoteStartProcess;
+import org.dromara.workflow.api.domain.RemoteStartProcessReturn;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 通用 工作流服务
+ *
+ * @Author ZETA
+ * @Date 2024/6/3
+ */
+public interface RemoteWorkflowService {
+
+    /**
+     * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
+     *
+     * @param businessIds 业务id
+     * @return 结果
+     */
+    boolean deleteInstance(List<Long> businessIds);
+
+    /**
+     * 获取当前流程状态
+     *
+     * @param taskId 任务id
+     * @return 状态
+     */
+    String getBusinessStatusByTaskId(Long taskId);
+
+    /**
+     * 获取当前流程状态
+     *
+     * @param businessId 业务id
+     * @return 状态
+     */
+    String getBusinessStatus(String businessId);
+
+    /**
+     * 设置流程变量
+     *
+     * @param instanceId 流程实例id
+     * @param variable   流程变量
+     */
+    void setVariable(Long instanceId, Map<String, Object> variable);
+
+    /**
+     * 获取流程变量
+     *
+     * @param instanceId 流程实例id
+     */
+    Map<String, Object> instanceVariable(Long instanceId);
+
+    /**
+     * 按照业务id查询流程实例id
+     *
+     * @param businessId 业务id
+     * @return 结果
+     */
+    Long getInstanceIdByBusinessId(String businessId);
+
+    /**
+     * 新增租户流程定义
+     *
+     * @param tenantId 租户id
+     */
+    void syncDef(String tenantId);
+
+    /**
+     * 启动流程
+     *
+     * @param startProcess 参数
+     * @return 结果
+     */
+    RemoteStartProcessReturn startWorkFlow(RemoteStartProcess startProcess);
+
+    /**
+     * 办理任务
+     *
+     * @param completeTask 参数
+     * @return 结果
+     */
+    boolean completeTask(RemoteCompleteTask completeTask);
+
+}

+ 70 - 0
ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/RemoteWorkflowServiceMock.java

@@ -0,0 +1,70 @@
+package org.dromara.workflow.api;
+
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.workflow.api.domain.RemoteCompleteTask;
+import org.dromara.workflow.api.domain.RemoteStartProcess;
+import org.dromara.workflow.api.domain.RemoteStartProcessReturn;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 工作流服务(降级处理)
+ *
+ * @author Lion Li
+ */
+@Slf4j
+public class RemoteWorkflowServiceMock implements RemoteWorkflowService {
+
+    @Override
+    public boolean deleteInstance(List<Long> businessIds) {
+        log.warn("服务调用异常 -> 降级处理");
+        return false;
+    }
+
+    @Override
+    public String getBusinessStatusByTaskId(Long taskId) {
+        log.warn("服务调用异常 -> 降级处理");
+        return null;
+    }
+
+    @Override
+    public String getBusinessStatus(String businessId) {
+        log.warn("服务调用异常 -> 降级处理");
+        return null;
+    }
+
+    @Override
+    public void setVariable(Long instanceId, Map<String, Object> variable) {
+        log.warn("服务调用异常 -> 降级处理");
+    }
+
+    @Override
+    public Map<String, Object> instanceVariable(Long instanceId) {
+        log.warn("服务调用异常 -> 降级处理");
+        return null;
+    }
+
+    @Override
+    public Long getInstanceIdByBusinessId(String businessId) {
+        log.warn("服务调用异常 -> 降级处理");
+        return null;
+    }
+
+    @Override
+    public void syncDef(String tenantId) {
+        log.warn("服务调用异常 -> 降级处理");
+    }
+
+    @Override
+    public RemoteStartProcessReturn startWorkFlow(RemoteStartProcess startProcess) {
+        log.warn("服务调用异常 -> 降级处理");
+        return null;
+    }
+
+    @Override
+    public boolean completeTask(RemoteCompleteTask completeTask) {
+        log.warn("服务调用异常 -> 降级处理");
+        return false;
+    }
+}

+ 71 - 0
ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteCompleteTask.java

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

+ 30 - 0
ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteFlowCopy.java

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

+ 45 - 0
ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteStartProcess.java

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

+ 30 - 0
ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteStartProcessReturn.java

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

+ 41 - 0
ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/event/ProcessDeleteEvent.java

@@ -0,0 +1,41 @@
+package org.dromara.workflow.api.event;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.utils.SpringUtils;
+import org.springframework.cloud.bus.event.RemoteApplicationEvent;
+
+import java.io.Serial;
+
+/**
+ * 删除流程监听
+ *
+ * @author AprilWind
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ProcessDeleteEvent extends RemoteApplicationEvent {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 流程定义编码
+     */
+    private String flowCode;
+
+    /**
+     * 业务id
+     */
+    private String businessId;
+
+    public ProcessDeleteEvent() {
+        super(new Object(), SpringUtils.getApplicationName(), DEFAULT_DESTINATION_FACTORY.getDestination(null));
+    }
+
+}

+ 72 - 0
ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/event/ProcessEvent.java

@@ -0,0 +1,72 @@
+package org.dromara.workflow.api.event;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.utils.SpringUtils;
+import org.springframework.cloud.bus.event.RemoteApplicationEvent;
+
+import java.io.Serial;
+import java.util.Map;
+
+/**
+ * 总体流程监听
+ *
+ * @author may
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ProcessEvent extends RemoteApplicationEvent {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 流程定义编码
+     */
+    private String flowCode;
+
+    /**
+     * 业务id
+     */
+    private String businessId;
+
+    /**
+     * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
+     */
+    private Integer nodeType;
+
+    /**
+     * 流程节点编码
+     */
+    private String nodeCode;
+
+    /**
+     * 流程节点名称
+     */
+    private String nodeName;
+
+    /**
+     * 流程状态
+     */
+    private String status;
+
+    /**
+     * 办理参数
+     */
+    private Map<String, Object> params;
+
+    /**
+     * 当为true时为申请人节点办理
+     */
+    private Boolean submit;
+
+    public ProcessEvent() {
+        super(new Object(), SpringUtils.getApplicationName(), DEFAULT_DESTINATION_FACTORY.getDestination(null));
+    }
+
+}

+ 65 - 0
ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/event/ProcessTaskEvent.java

@@ -0,0 +1,65 @@
+package org.dromara.workflow.api.event;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.utils.SpringUtils;
+import org.springframework.cloud.bus.event.RemoteApplicationEvent;
+
+import java.io.Serial;
+
+/**
+ * 流程任务监听
+ *
+ * @author may
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ProcessTaskEvent extends RemoteApplicationEvent {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 流程定义编码
+     */
+    private String flowCode;
+
+    /**
+     * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
+     */
+    private Integer nodeType;
+
+    /**
+     * 流程节点编码
+     */
+    private String nodeCode;
+
+    /**
+     * 流程节点名称
+     */
+    private String nodeName;
+
+    /**
+     * 任务id
+     */
+    private Long taskId;
+
+    /**
+     * 业务id
+     */
+    private String businessId;
+
+    /**
+     * 流程状态
+     */
+    private String status;
+
+    public ProcessTaskEvent() {
+        super(new Object(), SpringUtils.getApplicationName(), DEFAULT_DESTINATION_FACTORY.getDestination(null));
+    }
+}

+ 27 - 0
ruoyi-auth/Dockerfile

@@ -0,0 +1,27 @@
+# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
+FROM bellsoft/liberica-openjdk-rocky:17.0.15-cds
+#FROM bellsoft/liberica-openjdk-rocky:21.0.7-cds
+#FROM findepi/graalvm:java17-native
+
+LABEL maintainer="Lion Li"
+
+RUN mkdir -p /ruoyi/auth/logs  \
+    /ruoyi/auth/temp  \
+    /ruoyi/skywalking/agent
+
+WORKDIR /ruoyi/auth
+
+ENV SERVER_PORT=9210 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
+
+EXPOSE ${SERVER_PORT}
+
+ADD ./target/ruoyi-auth.jar ./app.jar
+
+SHELL ["/bin/bash", "-c"]
+
+ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
+           #-Dskywalking.agent.service_name=ruoyi-auth \
+           #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
+           -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
+           -jar app.jar
+

+ 139 - 0
ruoyi-auth/pom.xml

@@ -0,0 +1,139 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-cloud-plus</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ruoyi-auth</artifactId>
+
+    <description>
+        ruoyi-auth 认证授权中心
+    </description>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-nacos</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-captcha</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-sentinel</artifactId>
+        </dependency>
+
+        <!-- RuoYi Common Security-->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-security</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-social</artifactId>
+        </dependency>
+
+        <!-- RuoYi Common Log -->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-log</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-doc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-ratelimiter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-encrypt</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-dubbo</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-seata</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-tenant</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-service-impl</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-api-resource</artifactId>
+        </dependency>
+
+        <!-- 自定义负载均衡(多团队开发使用) -->
+<!--        <dependency>-->
+<!--            <groupId>org.dromara</groupId>-->
+<!--            <artifactId>ruoyi-common-loadbalancer</artifactId>-->
+<!--        </dependency>-->
+
+        <!-- ELK 日志收集 -->
+<!--        <dependency>-->
+<!--            <groupId>org.dromara</groupId>-->
+<!--            <artifactId>ruoyi-common-logstash</artifactId>-->
+<!--        </dependency>-->
+
+        <!-- skywalking 日志收集 -->
+<!--        <dependency>-->
+<!--            <groupId>org.dromara</groupId>-->
+<!--            <artifactId>ruoyi-common-skylog</artifactId>-->
+<!--        </dependency>-->
+
+        <!-- prometheus 监控 -->
+<!--        <dependency>-->
+<!--            <groupId>org.dromara</groupId>-->
+<!--            <artifactId>ruoyi-common-prometheus</artifactId>-->
+<!--        </dependency>-->
+
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 23 - 0
ruoyi-auth/src/main/java/org/dromara/auth/RuoYiAuthApplication.java

@@ -0,0 +1,23 @@
+package org.dromara.auth;
+
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
+
+/**
+ * 认证授权中心
+ *
+ * @author ruoyi
+ */
+@EnableDubbo
+@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
+public class RuoYiAuthApplication {
+    public static void main(String[] args) {
+        SpringApplication application = new SpringApplication(RuoYiAuthApplication.class);
+        application.setApplicationStartup(new BufferingApplicationStartup(2048));
+        application.run(args);
+        System.out.println("(♥◠‿◠)ノ゙  认证授权中心启动成功   ლ(´ڡ`ლ)゙  ");
+    }
+}

+ 88 - 0
ruoyi-auth/src/main/java/org/dromara/auth/captcha/UnsignedMathGenerator.java

@@ -0,0 +1,88 @@
+package org.dromara.auth.captcha;
+
+import cn.hutool.captcha.generator.CodeGenerator;
+import cn.hutool.core.math.Calculator;
+import cn.hutool.core.util.CharUtil;
+import cn.hutool.core.util.RandomUtil;
+import org.dromara.common.core.utils.StringUtils;
+
+import java.io.Serial;
+
+/**
+ * 无符号计算生成器
+ *
+ * @author Lion Li
+ */
+public class UnsignedMathGenerator implements CodeGenerator {
+
+    @Serial
+    private static final long serialVersionUID = -5514819971774091076L;
+
+    private static final String OPERATORS = "+-*";
+
+    /**
+     * 参与计算数字最大长度
+     */
+    private final int numberLength;
+
+    /**
+     * 构造
+     */
+    public UnsignedMathGenerator() {
+        this(2);
+    }
+
+    /**
+     * 构造
+     *
+     * @param numberLength 参与计算最大数字位数
+     */
+    public UnsignedMathGenerator(int numberLength) {
+        this.numberLength = numberLength;
+    }
+
+    @Override
+    public String generate() {
+        final int limit = getLimit();
+        int a = RandomUtil.randomInt(limit);
+        int b = RandomUtil.randomInt(limit);
+        String max = Integer.toString(Math.max(a,b));
+        String min = Integer.toString(Math.min(a,b));
+        max = StringUtils.rightPad(max, this.numberLength, CharUtil.SPACE);
+        min = StringUtils.rightPad(min, this.numberLength, CharUtil.SPACE);
+
+        return max + RandomUtil.randomChar(OPERATORS) + min + '=';
+    }
+
+    @Override
+    public boolean verify(String code, String userInputCode) {
+        int result;
+        try {
+            result = Integer.parseInt(userInputCode);
+        } catch (NumberFormatException e) {
+            // 用户输入非数字
+            return false;
+        }
+
+        final int calculateResult = (int) Calculator.conversion(code);
+        return result == calculateResult;
+    }
+
+    /**
+     * 获取验证码长度
+     *
+     * @return 验证码长度
+     */
+    public int getLength() {
+        return this.numberLength * 2 + 2;
+    }
+
+    /**
+     * 根据长度获取参与计算数字最大值
+     *
+     * @return 最大值
+     */
+    private int getLimit() {
+        return Integer.parseInt("1" + StringUtils.repeat('0', this.numberLength));
+    }
+}

+ 62 - 0
ruoyi-auth/src/main/java/org/dromara/auth/config/CaptchaConfig.java

@@ -0,0 +1,62 @@
+package org.dromara.auth.config;
+
+import cn.hutool.captcha.CaptchaUtil;
+import cn.hutool.captcha.CircleCaptcha;
+import cn.hutool.captcha.LineCaptcha;
+import cn.hutool.captcha.ShearCaptcha;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Lazy;
+
+import java.awt.*;
+
+/**
+ * 验证码配置
+ *
+ * @author Lion Li
+ */
+@Configuration
+public class CaptchaConfig {
+
+    private static final int WIDTH = 160;
+    private static final int HEIGHT = 60;
+    private static final Color BACKGROUND = Color.LIGHT_GRAY;
+    private static final Font FONT = new Font("Arial", Font.BOLD, 48);
+
+    /**
+     * 圆圈干扰验证码
+     */
+    @Lazy
+    @Bean
+    public CircleCaptcha circleCaptcha() {
+        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(WIDTH, HEIGHT);
+        captcha.setBackground(BACKGROUND);
+        captcha.setFont(FONT);
+        return captcha;
+    }
+
+    /**
+     * 线段干扰的验证码
+     */
+    @Lazy
+    @Bean
+    public LineCaptcha lineCaptcha() {
+        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(WIDTH, HEIGHT);
+        captcha.setBackground(BACKGROUND);
+        captcha.setFont(FONT);
+        return captcha;
+    }
+
+    /**
+     * 扭曲干扰验证码
+     */
+    @Lazy
+    @Bean
+    public ShearCaptcha shearCaptcha() {
+        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(WIDTH, HEIGHT);
+        captcha.setBackground(BACKGROUND);
+        captcha.setFont(FONT);
+        return captcha;
+    }
+
+}

+ 87 - 0
ruoyi-auth/src/main/java/org/dromara/auth/controller/CaptchaController.java

@@ -0,0 +1,87 @@
+package org.dromara.auth.controller;
+
+import cn.hutool.captcha.AbstractCaptcha;
+import cn.hutool.captcha.generator.CodeGenerator;
+import cn.hutool.core.util.IdUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.auth.domain.vo.CaptchaVo;
+import org.dromara.auth.enums.CaptchaType;
+import org.dromara.auth.properties.CaptchaProperties;
+import org.dromara.common.core.constant.Constants;
+import org.dromara.common.core.constant.GlobalConstants;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.utils.reflect.ReflectUtils;
+import org.dromara.common.ratelimiter.annotation.RateLimiter;
+import org.dromara.common.ratelimiter.enums.LimitType;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.Duration;
+
+/**
+ * 验证码操作处理
+ *
+ * @author Lion Li
+ */
+@Slf4j
+@Validated
+@RequiredArgsConstructor
+@RestController
+public class CaptchaController {
+
+    private final CaptchaProperties captchaProperties;
+
+    /**
+     * 生成验证码
+     */
+    @GetMapping("/code")
+    public R<CaptchaVo> getCode() {
+        CaptchaVo captchaVo = new CaptchaVo();
+        boolean captchaEnabled = captchaProperties.getEnabled();
+        if (!captchaEnabled) {
+            captchaVo.setCaptchaEnabled(false);
+            return R.ok(captchaVo);
+        }
+        return R.ok(SpringUtils.getAopProxy(this).getCodeImpl());
+    }
+
+    /**
+     * 生成验证码
+     * 独立方法避免验证码关闭之后仍然走限流
+     */
+    @RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
+    public CaptchaVo getCodeImpl() {
+        // 保存验证码信息
+        String uuid = IdUtil.simpleUUID();
+        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
+        // 生成验证码
+        CaptchaType captchaType = captchaProperties.getType();
+        boolean isMath = CaptchaType.MATH == captchaType;
+        Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
+        CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
+        AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
+        captcha.setGenerator(codeGenerator);
+        captcha.createCode();
+        // 如果是数学验证码,使用SpEL表达式处理验证码结果
+        String code = captcha.getCode();
+        if (isMath) {
+            ExpressionParser parser = new SpelExpressionParser();
+            Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
+            code = exp.getValue(String.class);
+        }
+        RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
+        CaptchaVo captchaVo = new CaptchaVo();
+        captchaVo.setUuid(uuid);
+        captchaVo.setImg(captcha.getImageBase64());
+        return captchaVo;
+    }
+
+}

+ 231 - 0
ruoyi-auth/src/main/java/org/dromara/auth/controller/TokenController.java

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

+ 16 - 0
ruoyi-auth/src/main/java/org/dromara/auth/domain/convert/TenantVoConvert.java

@@ -0,0 +1,16 @@
+package org.dromara.auth.domain.convert;
+
+import io.github.linpeilie.BaseMapper;
+import org.dromara.auth.domain.vo.TenantListVo;
+import org.dromara.system.api.domain.vo.RemoteTenantVo;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingConstants;
+
+/**
+ * 租户vo转换器
+ * @author zhujie
+ */
+@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
+public interface TenantVoConvert extends BaseMapper<RemoteTenantVo, TenantListVo> {
+
+}

+ 25 - 0
ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/CaptchaVo.java

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

+ 25 - 0
ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/LoginTenantVo.java

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

+ 54 - 0
ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/LoginVo.java

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

+ 28 - 0
ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/TenantListVo.java

@@ -0,0 +1,28 @@
+package org.dromara.auth.domain.vo;
+
+import lombok.Data;
+
+/**
+ * 租户列表
+ *
+ * @author zhujie
+ */
+@Data
+public class TenantListVo {
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 企业名称
+     */
+    private String companyName;
+
+    /**
+     * 域名
+     */
+    private String domain;
+
+}

+ 35 - 0
ruoyi-auth/src/main/java/org/dromara/auth/enums/CaptchaCategory.java

@@ -0,0 +1,35 @@
+package org.dromara.auth.enums;
+
+import cn.hutool.captcha.AbstractCaptcha;
+import cn.hutool.captcha.CircleCaptcha;
+import cn.hutool.captcha.LineCaptcha;
+import cn.hutool.captcha.ShearCaptcha;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 验证码类别
+ *
+ * @author Lion Li
+ */
+@Getter
+@AllArgsConstructor
+public enum CaptchaCategory {
+
+    /**
+     * 线段干扰
+     */
+    LINE(LineCaptcha.class),
+
+    /**
+     * 圆圈干扰
+     */
+    CIRCLE(CircleCaptcha.class),
+
+    /**
+     * 扭曲干扰
+     */
+    SHEAR(ShearCaptcha.class);
+
+    private final Class<? extends AbstractCaptcha> clazz;
+}

+ 29 - 0
ruoyi-auth/src/main/java/org/dromara/auth/enums/CaptchaType.java

@@ -0,0 +1,29 @@
+package org.dromara.auth.enums;
+
+import cn.hutool.captcha.generator.CodeGenerator;
+import cn.hutool.captcha.generator.RandomGenerator;
+import org.dromara.auth.captcha.UnsignedMathGenerator;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 验证码类型
+ *
+ * @author Lion Li
+ */
+@Getter
+@AllArgsConstructor
+public enum CaptchaType {
+
+    /**
+     * 数字
+     */
+    MATH(UnsignedMathGenerator.class),
+
+    /**
+     * 字符
+     */
+    CHAR(RandomGenerator.class);
+
+    private final Class<? extends CodeGenerator> clazz;
+}

+ 31 - 0
ruoyi-auth/src/main/java/org/dromara/auth/form/EmailLoginBody.java

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

+ 32 - 0
ruoyi-auth/src/main/java/org/dromara/auth/form/PasswordLoginBody.java

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

+ 37 - 0
ruoyi-auth/src/main/java/org/dromara/auth/form/RegisterBody.java

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

+ 29 - 0
ruoyi-auth/src/main/java/org/dromara/auth/form/SmsLoginBody.java

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

+ 35 - 0
ruoyi-auth/src/main/java/org/dromara/auth/form/SocialLoginBody.java

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

+ 28 - 0
ruoyi-auth/src/main/java/org/dromara/auth/form/XcxLoginBody.java

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

+ 168 - 0
ruoyi-auth/src/main/java/org/dromara/auth/listener/UserActionListener.java

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

+ 46 - 0
ruoyi-auth/src/main/java/org/dromara/auth/properties/CaptchaProperties.java

@@ -0,0 +1,46 @@
+package org.dromara.auth.properties;
+
+import org.dromara.auth.enums.CaptchaCategory;
+import org.dromara.auth.enums.CaptchaType;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 验证码配置
+ *
+ * @author ruoyi
+ */
+@Data
+@Configuration
+@RefreshScope
+@ConfigurationProperties(prefix = "security.captcha")
+public class CaptchaProperties {
+
+    /**
+     * 验证码类型
+     */
+    private CaptchaType type;
+
+    /**
+     * 验证码类别
+     */
+    private CaptchaCategory category;
+
+    /**
+     * 数字验证码位数
+     */
+    private Integer numberLength;
+
+    /**
+     * 字符验证码长度
+     */
+    private Integer charLength;
+
+    /**
+     * 验证码开关
+     */
+    private Boolean enabled;
+
+}

+ 29 - 0
ruoyi-auth/src/main/java/org/dromara/auth/properties/UserPasswordProperties.java

@@ -0,0 +1,29 @@
+package org.dromara.auth.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 用户密码配置
+ *
+ * @author Lion Li
+ */
+@Data
+@Configuration
+@RefreshScope
+@ConfigurationProperties(prefix = "user.password")
+public class UserPasswordProperties {
+
+    /**
+     * 密码最大错误次数
+     */
+    private Integer maxRetryCount;
+
+    /**
+     * 密码锁定时间(默认10分钟)
+     */
+    private Integer lockTime;
+
+}

+ 44 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/IAuthStrategy.java

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

+ 265 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/SysLoginService.java

@@ -0,0 +1,265 @@
+package org.dromara.auth.service;
+
+import cn.dev33.satoken.exception.NotLoginException;
+import cn.hutool.crypto.digest.BCrypt;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.lock.annotation.Lock4j;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.model.AuthUser;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.auth.form.RegisterBody;
+import org.dromara.auth.properties.CaptchaProperties;
+import org.dromara.auth.properties.UserPasswordProperties;
+import org.dromara.common.core.constant.*;
+import org.dromara.common.core.enums.LoginType;
+import org.dromara.common.core.enums.UserType;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.exception.user.CaptchaException;
+import org.dromara.common.core.exception.user.CaptchaExpireException;
+import org.dromara.common.core.exception.user.UserException;
+import org.dromara.common.core.utils.MessageUtils;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.log.event.LogininforEvent;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.tenant.exception.TenantException;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.system.api.RemoteSocialService;
+import org.dromara.system.api.RemoteTenantService;
+import org.dromara.system.api.RemoteUserService;
+import org.dromara.system.api.domain.bo.RemoteSocialBo;
+import org.dromara.system.api.domain.bo.RemoteUserBo;
+import org.dromara.system.api.domain.vo.RemoteSocialVo;
+import org.dromara.system.api.domain.vo.RemoteTenantVo;
+import org.dromara.system.api.model.LoginUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.Duration;
+import java.util.Date;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * 登录校验方法
+ *
+ * @author ruoyi
+ */
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class SysLoginService {
+
+    @DubboReference
+    private RemoteUserService remoteUserService;
+    @DubboReference
+    private RemoteTenantService remoteTenantService;
+    @DubboReference
+    private RemoteSocialService remoteSocialService;
+
+    @Autowired
+    private UserPasswordProperties userPasswordProperties;
+    @Autowired
+    private final CaptchaProperties captchaProperties;
+
+    /**
+     * 绑定第三方用户
+     *
+     * @param authUserData 授权响应实体
+     */
+    @Lock4j
+    public void socialRegister(AuthUser authUserData) {
+        String authId = authUserData.getSource() + authUserData.getUuid();
+        // 第三方用户信息
+        RemoteSocialBo bo = BeanUtil.toBean(authUserData, RemoteSocialBo.class);
+        BeanUtil.copyProperties(authUserData.getToken(), bo);
+        Long userId = LoginHelper.getUserId();
+        bo.setUserId(userId);
+        bo.setAuthId(authId);
+        bo.setOpenId(authUserData.getUuid());
+        bo.setUserName(authUserData.getUsername());
+        bo.setNickName(authUserData.getNickname());
+        List<RemoteSocialVo> checkList = remoteSocialService.selectByAuthId(authId);
+        if (CollUtil.isNotEmpty(checkList)) {
+            throw new ServiceException("此三方账号已经被绑定!");
+        }
+        // 查询是否已经绑定用户
+        RemoteSocialBo params = new RemoteSocialBo();
+        params.setUserId(userId);
+        params.setSource(bo.getSource());
+        List<RemoteSocialVo> list = remoteSocialService.queryList(params);
+        if (CollUtil.isEmpty(list)) {
+            // 没有绑定用户, 新增用户信息
+            remoteSocialService.insertByBo(bo);
+        } else {
+            // 更新用户信息
+            bo.setId(list.get(0).getId());
+            remoteSocialService.updateByBo(bo);
+            // 如果要绑定的平台账号已经被绑定过了 是否抛异常自行决断
+            // throw new ServiceException("此平台账号已经被绑定!");
+        }
+    }
+
+    /**
+     * 退出登录
+     */
+    public void logout() {
+        try {
+            LoginUser loginUser = LoginHelper.getLoginUser();
+            if (ObjectUtil.isNull(loginUser)) {
+                return;
+            }
+            if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
+                // 超级管理员 登出清除动态租户
+                TenantHelper.clearDynamic();
+            }
+            recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
+        } catch (NotLoginException ignored) {
+        } finally {
+            try {
+                StpUtil.logout();
+            } catch (NotLoginException ignored) {
+            }
+        }
+    }
+
+    /**
+     * 注册
+     */
+    public void register(RegisterBody registerBody) {
+        String tenantId = registerBody.getTenantId();
+        String username = registerBody.getUsername();
+        String password = registerBody.getPassword();
+        // 校验用户类型是否存在
+        String userType = UserType.getUserType(registerBody.getUserType()).getUserType();
+
+        boolean captchaEnabled = captchaProperties.getEnabled();
+        // 验证码开关
+        if (captchaEnabled) {
+            validateCaptcha(tenantId, username, registerBody.getCode(), registerBody.getUuid());
+        }
+
+        // 注册用户信息
+        RemoteUserBo remoteUserBo = new RemoteUserBo();
+        remoteUserBo.setTenantId(tenantId);
+        remoteUserBo.setUserName(username);
+        remoteUserBo.setNickName(username);
+        remoteUserBo.setPassword(BCrypt.hashpw(password));
+        remoteUserBo.setUserType(userType);
+
+        boolean regFlag = remoteUserService.registerUserInfo(remoteUserBo);
+        if (!regFlag) {
+            throw new UserException("user.register.error");
+        }
+        recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success"));
+    }
+
+    /**
+     * 校验验证码
+     *
+     * @param username 用户名
+     * @param code     验证码
+     * @param uuid     唯一标识
+     */
+    public void validateCaptcha(String tenantId, String username, String code, String uuid) {
+        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
+        String captcha = RedisUtils.getCacheObject(verifyKey);
+        RedisUtils.deleteObject(verifyKey);
+        if (captcha == null) {
+            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
+            throw new CaptchaExpireException();
+        }
+        if (!code.equalsIgnoreCase(captcha)) {
+            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
+            throw new CaptchaException();
+        }
+    }
+
+    /**
+     * 记录登录信息
+     *
+     * @param username 用户名
+     * @param status   状态
+     * @param message  消息内容
+     * @return
+     */
+    public void recordLogininfor(String tenantId, String username, String status, String message) {
+        // 封装对象
+        LogininforEvent logininforEvent = new LogininforEvent();
+        logininforEvent.setTenantId(tenantId);
+        logininforEvent.setUsername(username);
+        logininforEvent.setStatus(status);
+        logininforEvent.setMessage(message);
+        SpringUtils.context().publishEvent(logininforEvent);
+    }
+
+    /**
+     * 登录校验
+     */
+    public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
+        String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;
+        String loginFail = Constants.LOGIN_FAIL;
+        Integer maxRetryCount = userPasswordProperties.getMaxRetryCount();
+        Integer lockTime = userPasswordProperties.getLockTime();
+
+        // 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip)
+        int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0);
+        // 锁定时间内登录 则踢出
+        if (errorNumber >= maxRetryCount) {
+            recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
+            throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
+        }
+
+        if (supplier.get()) {
+            // 错误次数递增
+            errorNumber++;
+            RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
+            // 达到规定错误次数 则锁定登录
+            if (errorNumber >= maxRetryCount) {
+                recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
+                throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
+            } else {
+                // 未达到规定错误次数
+                recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
+                throw new UserException(loginType.getRetryLimitCount(), errorNumber);
+            }
+        }
+
+        // 登录成功 清空错误次数
+        RedisUtils.deleteObject(errorKey);
+    }
+
+    /**
+     * 校验租户
+     *
+     * @param tenantId 租户ID
+     */
+    public void checkTenant(String tenantId) {
+        if (!TenantHelper.isEnable()) {
+            return;
+        }
+        if (StringUtils.isBlank(tenantId)) {
+            throw new TenantException("tenant.number.not.blank");
+        }
+        if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
+            return;
+        }
+        RemoteTenantVo tenant = remoteTenantService.queryByTenantId(tenantId);
+        if (ObjectUtil.isNull(tenant)) {
+            log.info("登录租户:{} 不存在.", tenantId);
+            throw new TenantException("tenant.not.exists");
+        } else if (SystemConstants.DISABLE.equals(tenant.getStatus())) {
+            log.info("登录租户:{} 已被停用.", tenantId);
+            throw new TenantException("tenant.blocked");
+        } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
+            && new Date().after(tenant.getExpireTime())) {
+            log.info("登录租户:{} 已超过有效期.", tenantId);
+            throw new TenantException("tenant.expired");
+        }
+    }
+}

+ 86 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/EmailAuthStrategy.java

@@ -0,0 +1,86 @@
+package org.dromara.auth.service.impl;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.stp.parameter.SaLoginParameter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.auth.domain.vo.LoginVo;
+import org.dromara.auth.form.EmailLoginBody;
+import org.dromara.auth.service.IAuthStrategy;
+import org.dromara.auth.service.SysLoginService;
+import org.dromara.common.core.constant.Constants;
+import org.dromara.common.core.constant.GlobalConstants;
+import org.dromara.common.core.enums.LoginType;
+import org.dromara.common.core.exception.user.CaptchaExpireException;
+import org.dromara.common.core.utils.MessageUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.utils.ValidatorUtils;
+import org.dromara.common.json.utils.JsonUtils;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.system.api.RemoteUserService;
+import org.dromara.system.api.domain.vo.RemoteClientVo;
+import org.dromara.system.api.model.LoginUser;
+import org.springframework.stereotype.Service;
+
+/**
+ * 邮件认证策略
+ *
+ * @author Michelle.Chung
+ */
+@Slf4j
+@Service("email" + IAuthStrategy.BASE_NAME)
+@RequiredArgsConstructor
+public class EmailAuthStrategy implements IAuthStrategy {
+
+    private final SysLoginService loginService;
+
+    @DubboReference
+    private RemoteUserService remoteUserService;
+
+    @Override
+    public LoginVo login(String body, RemoteClientVo client) {
+        EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        String tenantId = loginBody.getTenantId();
+        String email = loginBody.getEmail();
+        String emailCode = loginBody.getEmailCode();
+        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
+            LoginUser user = remoteUserService.getUserInfoByEmail(email, tenantId);
+            loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUsername(), () -> !validateEmailCode(tenantId, email, emailCode));
+            return user;
+        });
+        loginUser.setClientKey(client.getClientKey());
+        loginUser.setDeviceType(client.getDeviceType());
+        SaLoginParameter model = new SaLoginParameter();
+        model.setDeviceType(client.getDeviceType());
+        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
+        // 例如: 后台用户30分钟过期 app用户1天过期
+        model.setTimeout(client.getTimeout());
+        model.setActiveTimeout(client.getActiveTimeout());
+        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
+        // 生成token
+        LoginHelper.login(loginUser, model);
+
+        LoginVo loginVo = new LoginVo();
+        loginVo.setAccessToken(StpUtil.getTokenValue());
+        loginVo.setExpireIn(StpUtil.getTokenTimeout());
+        loginVo.setClientId(client.getClientId());
+        return loginVo;
+    }
+
+    /**
+     * 校验邮箱验证码
+     */
+    private boolean validateEmailCode(String tenantId, String email, String emailCode) {
+        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email);
+        if (StringUtils.isBlank(code)) {
+            loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
+            throw new CaptchaExpireException();
+        }
+        return code.equals(emailCode);
+    }
+
+}

+ 107 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/PasswordAuthStrategy.java

@@ -0,0 +1,107 @@
+package org.dromara.auth.service.impl;
+
+import cn.hutool.crypto.digest.BCrypt;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.stp.parameter.SaLoginParameter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.auth.domain.vo.LoginVo;
+import org.dromara.auth.form.PasswordLoginBody;
+import org.dromara.auth.properties.CaptchaProperties;
+import org.dromara.auth.service.IAuthStrategy;
+import org.dromara.auth.service.SysLoginService;
+import org.dromara.common.core.constant.Constants;
+import org.dromara.common.core.constant.GlobalConstants;
+import org.dromara.common.core.enums.LoginType;
+import org.dromara.common.core.exception.user.CaptchaException;
+import org.dromara.common.core.exception.user.CaptchaExpireException;
+import org.dromara.common.core.utils.MessageUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.utils.ValidatorUtils;
+import org.dromara.common.json.utils.JsonUtils;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.system.api.RemoteUserService;
+import org.dromara.system.api.domain.vo.RemoteClientVo;
+import org.dromara.system.api.model.LoginUser;
+import org.springframework.stereotype.Service;
+
+/**
+ * 密码认证策略
+ *
+ * @author Michelle.Chung
+ */
+@Slf4j
+@Service("password" + IAuthStrategy.BASE_NAME)
+@RequiredArgsConstructor
+public class PasswordAuthStrategy implements IAuthStrategy {
+
+    private final CaptchaProperties captchaProperties;
+
+    private final SysLoginService loginService;
+
+    @DubboReference
+    private RemoteUserService remoteUserService;
+
+    @Override
+    public LoginVo login(String body, RemoteClientVo client) {
+        PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        String tenantId = loginBody.getTenantId();
+        String username = loginBody.getUsername();
+        String password = loginBody.getPassword();
+        String code = loginBody.getCode();
+        String uuid = loginBody.getUuid();
+
+        // 验证码开关
+        if (captchaProperties.getEnabled()) {
+            validateCaptcha(tenantId, username, code, uuid);
+        }
+        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
+            LoginUser user = remoteUserService.getUserInfo(username, tenantId);
+            loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
+            return user;
+        });
+        loginUser.setClientKey(client.getClientKey());
+        loginUser.setDeviceType(client.getDeviceType());
+        SaLoginParameter model = new SaLoginParameter();
+        model.setDeviceType(client.getDeviceType());
+        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
+        // 例如: 后台用户30分钟过期 app用户1天过期
+        model.setTimeout(client.getTimeout());
+        model.setActiveTimeout(client.getActiveTimeout());
+        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
+        // 生成token
+        LoginHelper.login(loginUser, model);
+
+        LoginVo loginVo = new LoginVo();
+        loginVo.setAccessToken(StpUtil.getTokenValue());
+        loginVo.setExpireIn(StpUtil.getTokenTimeout());
+        loginVo.setClientId(client.getClientId());
+        return loginVo;
+    }
+
+    /**
+     * 校验验证码
+     *
+     * @param username 用户名
+     * @param code     验证码
+     * @param uuid     唯一标识
+     */
+    private void validateCaptcha(String tenantId, String username, String code, String uuid) {
+        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
+        String captcha = RedisUtils.getCacheObject(verifyKey);
+        RedisUtils.deleteObject(verifyKey);
+        if (captcha == null) {
+            loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
+            throw new CaptchaExpireException();
+        }
+        if (!code.equalsIgnoreCase(captcha)) {
+            loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
+            throw new CaptchaException();
+        }
+    }
+
+}

+ 86 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SmsAuthStrategy.java

@@ -0,0 +1,86 @@
+package org.dromara.auth.service.impl;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.stp.parameter.SaLoginParameter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.auth.domain.vo.LoginVo;
+import org.dromara.auth.form.SmsLoginBody;
+import org.dromara.auth.service.IAuthStrategy;
+import org.dromara.auth.service.SysLoginService;
+import org.dromara.common.core.constant.Constants;
+import org.dromara.common.core.constant.GlobalConstants;
+import org.dromara.common.core.enums.LoginType;
+import org.dromara.common.core.exception.user.CaptchaExpireException;
+import org.dromara.common.core.utils.MessageUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.utils.ValidatorUtils;
+import org.dromara.common.json.utils.JsonUtils;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.system.api.RemoteUserService;
+import org.dromara.system.api.domain.vo.RemoteClientVo;
+import org.dromara.system.api.model.LoginUser;
+import org.springframework.stereotype.Service;
+
+/**
+ * 短信认证策略
+ *
+ * @author Michelle.Chung
+ */
+@Slf4j
+@Service("sms" + IAuthStrategy.BASE_NAME)
+@RequiredArgsConstructor
+public class SmsAuthStrategy implements IAuthStrategy {
+
+    private final SysLoginService loginService;
+
+    @DubboReference
+    private RemoteUserService remoteUserService;
+
+    @Override
+    public LoginVo login(String body, RemoteClientVo client) {
+        SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        String tenantId = loginBody.getTenantId();
+        String phonenumber = loginBody.getPhonenumber();
+        String smsCode = loginBody.getSmsCode();
+        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
+            LoginUser user = remoteUserService.getUserInfoByPhonenumber(phonenumber, tenantId);
+            loginService.checkLogin(LoginType.SMS, tenantId, user.getUsername(), () -> !validateSmsCode(tenantId, phonenumber, smsCode));
+            return user;
+        });
+        loginUser.setClientKey(client.getClientKey());
+        loginUser.setDeviceType(client.getDeviceType());
+        SaLoginParameter model = new SaLoginParameter();
+        model.setDeviceType(client.getDeviceType());
+        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
+        // 例如: 后台用户30分钟过期 app用户1天过期
+        model.setTimeout(client.getTimeout());
+        model.setActiveTimeout(client.getActiveTimeout());
+        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
+        // 生成token
+        LoginHelper.login(loginUser, model);
+
+        LoginVo loginVo = new LoginVo();
+        loginVo.setAccessToken(StpUtil.getTokenValue());
+        loginVo.setExpireIn(StpUtil.getTokenTimeout());
+        loginVo.setClientId(client.getClientId());
+        return loginVo;
+    }
+
+    /**
+     * 校验短信验证码
+     */
+    private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) {
+        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber);
+        if (StringUtils.isBlank(code)) {
+            loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
+            throw new CaptchaExpireException();
+        }
+        return code.equals(smsCode);
+    }
+
+}

+ 102 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SocialAuthStrategy.java

@@ -0,0 +1,102 @@
+package org.dromara.auth.service.impl;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.stp.parameter.SaLoginParameter;
+import cn.hutool.core.collection.CollUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.model.AuthResponse;
+import me.zhyd.oauth.model.AuthUser;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.auth.domain.vo.LoginVo;
+import org.dromara.auth.form.SocialLoginBody;
+import org.dromara.auth.service.IAuthStrategy;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.ValidatorUtils;
+import org.dromara.common.json.utils.JsonUtils;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.social.config.properties.SocialProperties;
+import org.dromara.common.social.utils.SocialUtils;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.system.api.RemoteSocialService;
+import org.dromara.system.api.RemoteUserService;
+import org.dromara.system.api.domain.vo.RemoteClientVo;
+import org.dromara.system.api.domain.vo.RemoteSocialVo;
+import org.dromara.system.api.model.LoginUser;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 第三方授权策略
+ *
+ * @author thiszhc is 三三
+ */
+@Slf4j
+@Service("social" + IAuthStrategy.BASE_NAME)
+@RequiredArgsConstructor
+public class SocialAuthStrategy implements IAuthStrategy {
+
+    private final SocialProperties socialProperties;
+
+    @DubboReference
+    private RemoteSocialService remoteSocialService;
+    @DubboReference
+    private RemoteUserService remoteUserService;
+
+    /**
+     * 登录-第三方授权登录
+     *
+     * @param body     登录信息
+     * @param client   客户端信息
+     */
+    @Override
+    public LoginVo login(String body, RemoteClientVo client) {
+        SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
+            loginBody.getSource(), loginBody.getSocialCode(),
+            loginBody.getSocialState(), socialProperties);
+        if (!response.ok()) {
+            throw new ServiceException(response.getMsg());
+        }
+        AuthUser authUserData = response.getData();
+
+        List<RemoteSocialVo> list = remoteSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
+        if (CollUtil.isEmpty(list)) {
+            throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
+        }
+        RemoteSocialVo socialVo;
+        if (TenantHelper.isEnable()) {
+            Optional<RemoteSocialVo> opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId()));
+            if (opt.isEmpty()) {
+                throw new ServiceException("对不起,你没有权限登录当前租户!");
+            }
+            socialVo = opt.get();
+        } else {
+            socialVo = list.get(0);
+        }
+
+        LoginUser loginUser = remoteUserService.getUserInfo(socialVo.getUserId(), socialVo.getTenantId());
+        loginUser.setClientKey(client.getClientKey());
+        loginUser.setDeviceType(client.getDeviceType());
+        SaLoginParameter model = new SaLoginParameter();
+        model.setDeviceType(client.getDeviceType());
+        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
+        // 例如: 后台用户30分钟过期 app用户1天过期
+        model.setTimeout(client.getTimeout());
+        model.setActiveTimeout(client.getActiveTimeout());
+        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
+        // 生成token
+        LoginHelper.login(loginUser, model);
+
+        LoginVo loginVo = new LoginVo();
+        loginVo.setAccessToken(StpUtil.getTokenValue());
+        loginVo.setExpireIn(StpUtil.getTokenTimeout());
+        loginVo.setClientId(client.getClientId());
+        return loginVo;
+    }
+
+}

+ 91 - 0
ruoyi-auth/src/main/java/org/dromara/auth/service/impl/XcxAuthStrategy.java

@@ -0,0 +1,91 @@
+package org.dromara.auth.service.impl;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.stp.parameter.SaLoginParameter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.config.AuthConfig;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthResponse;
+import me.zhyd.oauth.model.AuthToken;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthRequest;
+import me.zhyd.oauth.request.AuthWechatMiniProgramRequest;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.auth.domain.vo.LoginVo;
+import org.dromara.auth.form.XcxLoginBody;
+import org.dromara.auth.service.IAuthStrategy;
+import org.dromara.auth.service.SysLoginService;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.ValidatorUtils;
+import org.dromara.common.json.utils.JsonUtils;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.system.api.RemoteUserService;
+import org.dromara.system.api.domain.vo.RemoteClientVo;
+import org.dromara.system.api.model.XcxLoginUser;
+import org.springframework.stereotype.Service;
+
+/**
+ * 邮件认证策略
+ *
+ * @author Michelle.Chung
+ */
+@Slf4j
+@Service("xcx" + IAuthStrategy.BASE_NAME)
+@RequiredArgsConstructor
+public class XcxAuthStrategy implements IAuthStrategy {
+
+    private final SysLoginService loginService;
+
+    @DubboReference
+    private RemoteUserService remoteUserService;
+
+    @Override
+    public LoginVo login(String body, RemoteClientVo client) {
+        XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class);
+        ValidatorUtils.validate(loginBody);
+        // xcxCode 为 小程序调用 wx.login 授权后获取
+        String xcxCode = loginBody.getXcxCode();
+        // 多个小程序识别使用
+        String appid = loginBody.getAppid();
+
+        // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
+        AuthRequest authRequest = new AuthWechatMiniProgramRequest(AuthConfig.builder()
+            .clientId(appid).clientSecret("自行填写密钥 可根据不同appid填入不同密钥")
+            .ignoreCheckRedirectUri(true).ignoreCheckState(true).build());
+        AuthCallback authCallback = new AuthCallback();
+        authCallback.setCode(xcxCode);
+        AuthResponse<AuthUser> resp = authRequest.login(authCallback);
+        String openid, unionId;
+        if (resp.ok()) {
+            AuthToken token = resp.getData().getToken();
+            openid = token.getOpenId();
+            // 微信小程序只有关联到微信开放平台下之后才能获取到 unionId,因此unionId不一定能返回。
+            unionId = token.getUnionId();
+        } else {
+            throw new ServiceException(resp.getMsg());
+        }
+        // todo getUserInfoByOpenid 方法内部查询逻辑需要自行根据业务实现
+        XcxLoginUser loginUser = remoteUserService.getUserInfoByOpenid(openid);
+        loginUser.setClientKey(client.getClientKey());
+        loginUser.setDeviceType(client.getDeviceType());
+
+        SaLoginParameter model = new SaLoginParameter();
+        model.setDeviceType(client.getDeviceType());
+        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
+        // 例如: 后台用户30分钟过期 app用户1天过期
+        model.setTimeout(client.getTimeout());
+        model.setActiveTimeout(client.getActiveTimeout());
+        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
+        // 生成token
+        LoginHelper.login(loginUser, model);
+
+        LoginVo loginVo = new LoginVo();
+        loginVo.setAccessToken(StpUtil.getTokenValue());
+        loginVo.setExpireIn(StpUtil.getTokenTimeout());
+        loginVo.setClientId(client.getClientId());
+        loginVo.setOpenid(openid);
+        return loginVo;
+    }
+
+}

+ 34 - 0
ruoyi-auth/src/main/resources/application.yml

@@ -0,0 +1,34 @@
+# Tomcat
+server:
+  port: 9211
+
+# Spring
+spring:
+  application:
+    # 应用名称
+    name: ruoyi-auth
+  profiles:
+    # 环境配置
+    active: @profiles.active@
+
+--- # nacos 配置
+spring:
+  cloud:
+    nacos:
+      # nacos 服务地址
+      server-addr: @nacos.server@
+      username: @nacos.username@
+      password: @nacos.password@
+      discovery:
+        ip: 192.168.1.237
+        # 注册组
+        group: @nacos.discovery.group@
+        namespace: ${spring.profiles.active}
+      config:
+        # 配置组
+        group: @nacos.config.group@
+        namespace: ${spring.profiles.active}
+  config:
+    import:
+      - optional:nacos:application-common.yml
+      - optional:nacos:${spring.application.name}.yml

+ 10 - 0
ruoyi-auth/src/main/resources/banner.txt

@@ -0,0 +1,10 @@
+Spring Boot Version: ${spring-boot.version}
+Spring Application Name: ${spring.application.name}
+                            _                        _    _     
+                           (_)                      | |  | |    
+ _ __  _   _   ___   _   _  _  ______   __ _  _   _ | |_ | |__  
+| '__|| | | | / _ \ | | | || ||______| / _` || | | || __|| '_ \ 
+| |   | |_| || (_) || |_| || |        | (_| || |_| || |_ | | | |
+|_|    \__,_| \___/  \__, ||_|         \__,_| \__,_| \__||_| |_|
+                      __/ |                                     
+                     |___/                                      

+ 28 - 0
ruoyi-auth/src/main/resources/logback-plus.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="60 seconds" debug="false">
+    <!-- 日志存放路径 -->
+    <property name="log.path" value="logs/${project.artifactId}"/>
+    <!-- 日志输出格式 -->
+    <property name="console.log.pattern"
+              value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
+
+    <!-- 控制台输出 -->
+    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${console.log.pattern}</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+    </appender>
+
+    <include resource="logback-common.xml" />
+
+    <include resource="logback-logstash.xml" />
+
+    <!-- 开启 skywalking 日志收集 -->
+    <include resource="logback-skylog.xml" />
+
+    <!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="console"/>
+    </root>
+</configuration>

+ 59 - 0
ruoyi-common/pom.xml

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-cloud-plus</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <modules>
+        <module>ruoyi-common-bom</module>
+        <module>ruoyi-common-alibaba-bom</module>
+        <module>ruoyi-common-log</module>
+        <module>ruoyi-common-service-impl</module>
+        <module>ruoyi-common-excel</module>
+        <module>ruoyi-common-core</module>
+        <module>ruoyi-common-redis</module>
+        <module>ruoyi-common-doc</module>
+        <module>ruoyi-common-security</module>
+        <module>ruoyi-common-satoken</module>
+        <module>ruoyi-common-web</module>
+        <module>ruoyi-common-mybatis</module>
+        <module>ruoyi-common-job</module>
+        <module>ruoyi-common-dubbo</module>
+        <module>ruoyi-common-seata</module>
+        <module>ruoyi-common-loadbalancer</module>
+        <module>ruoyi-common-oss</module>
+        <module>ruoyi-common-ratelimiter</module>
+        <module>ruoyi-common-idempotent</module>
+        <module>ruoyi-common-mail</module>
+        <module>ruoyi-common-sms</module>
+        <module>ruoyi-common-logstash</module>
+        <module>ruoyi-common-elasticsearch</module>
+        <module>ruoyi-common-sentinel</module>
+        <module>ruoyi-common-skylog</module>
+        <module>ruoyi-common-prometheus</module>
+        <module>ruoyi-common-translation</module>
+        <module>ruoyi-common-sensitive</module>
+        <module>ruoyi-common-json</module>
+        <module>ruoyi-common-encrypt</module>
+        <module>ruoyi-common-tenant</module>
+        <module>ruoyi-common-websocket</module>
+        <module>ruoyi-common-social</module>
+        <module>ruoyi-common-nacos</module>
+        <module>ruoyi-common-bus</module>
+        <module>ruoyi-common-sse</module>
+        <module>ruoyi-common-annotation</module>
+        <module>ruoyi-common-baidumap</module>
+    </modules>
+
+    <artifactId>ruoyi-common</artifactId>
+    <packaging>pom</packaging>
+
+    <description>
+        ruoyi-common通用模块
+    </description>
+
+</project>

+ 181 - 0
ruoyi-common/ruoyi-common-alibaba-bom/pom.xml

@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.dromara</groupId>
+    <artifactId>ruoyi-common-alibaba-bom</artifactId>
+    <version>${revision}</version>
+    <packaging>pom</packaging>
+
+    <description>
+        ruoyi-common-alibaba-bom alibaba依赖项
+    </description>
+
+    <properties>
+        <revision>2.4.1</revision>
+        <spring-cloud-alibaba.version>2023.0.3.3</spring-cloud-alibaba.version>
+        <sentinel.version>1.8.8</sentinel.version>
+        <seata.version>2.4.0</seata.version>
+        <nacos.client.version>2.5.1</nacos.client.version>
+        <dubbo.version>3.3.5</dubbo.version>
+        <dubbo-extensions.version>3.3.1</dubbo-extensions.version>
+    </properties>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>com.alibaba.cloud</groupId>
+                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
+                <version>${spring-cloud-alibaba.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.nacos</groupId>
+                <artifactId>nacos-client</artifactId>
+                <version>${nacos.client.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-core</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-parameter-flow-control</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-datasource-extension</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-datasource-apollo</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-datasource-zookeeper</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-datasource-nacos</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-datasource-redis</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-datasource-consul</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-web-servlet</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-transport-simple-http</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-annotation-aspectj</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-reactor-adapter</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-cluster-server-default</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-cluster-client-default</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-spring-webflux-adapter</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-api-gateway-adapter-common</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-spring-webmvc-v6x-adapter</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-dubbo-adapter</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-apache-dubbo-adapter</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-apache-dubbo3-adapter</artifactId>
+                <version>${sentinel.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.seata</groupId>
+                <artifactId>seata-spring-boot-starter</artifactId>
+                <version>${seata.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.seata</groupId>
+                <artifactId>seata-all</artifactId>
+                <version>${seata.version}</version>
+            </dependency>
+
+            <!-- Apache Dubbo 配置 -->
+            <dependency>
+                <groupId>org.apache.dubbo</groupId>
+                <artifactId>dubbo-spring-boot-starter</artifactId>
+                <version>${dubbo.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.dubbo</groupId>
+                <artifactId>dubbo-spring-boot-actuator</artifactId>
+                <version>${dubbo.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.dubbo</groupId>
+                <artifactId>dubbo</artifactId>
+                <version>${dubbo.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.dubbo.extensions</groupId>
+                <artifactId>dubbo-metadata-report-redis</artifactId>
+                <version>${dubbo-extensions.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>

+ 21 - 0
ruoyi-common/ruoyi-common-annotation/pom.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-common</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>ruoyi-common-annotation</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-tenant</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 17 - 0
ruoyi-common/ruoyi-common-annotation/src/main/java/org/dromara/common/annotation/ano/IgnoreTenant.java

@@ -0,0 +1,17 @@
+package org.dromara.common.annotation.ano;
+
+import java.lang.annotation.*;
+
+/**
+ * 忽略多租户过滤注解
+ * 标注在方法上时,该方法执行期间将忽略租户ID过滤条件
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface IgnoreTenant {
+    /**
+     * 是否忽略租户过滤(默认true)
+     */
+    boolean value() default true;
+}

+ 73 - 0
ruoyi-common/ruoyi-common-annotation/src/main/java/org/dromara/common/annotation/aspect/TenantIgnoreAspect.java

@@ -0,0 +1,73 @@
+package org.dromara.common.annotation.aspect;
+
+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.aspectj.lang.reflect.MethodSignature;
+import org.dromara.common.annotation.ano.IgnoreTenant;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+
+@Aspect
+@Component
+public class TenantIgnoreAspect {
+
+    /**
+     * 切入点:所有被@IgnoreTenant注解的方法
+     */
+    @Pointcut("@annotation(org.dromara.common.annotation.ano.IgnoreTenant)")
+    public void ignoreTenantPointcut() {}
+
+    /**
+     * 环绕通知处理租户忽略逻辑
+     */
+    @Around("ignoreTenantPointcut()")
+    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+        // 获取方法签名
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        // 获取目标方法
+        Method method = signature.getMethod();
+
+        // 获取方法上的@IgnoreTenant注解
+        IgnoreTenant ignoreTenant = method.getAnnotation(IgnoreTenant.class);
+        if (ignoreTenant.value()) {
+            // 有返回值的方法处理
+            if (hasReturnValue(joinPoint)) {
+                return TenantHelper.ignore(() -> {
+                    try {
+                        return joinPoint.proceed();
+                    } catch (Throwable e) {
+                        throw new RuntimeException(e);
+                    }
+                });
+            }
+            // 无返回值的方法处理
+            else {
+                TenantHelper.ignore(() -> {
+                    try {
+                        joinPoint.proceed();
+                    } catch (Throwable e) {
+                        throw new RuntimeException(e);
+                    }
+                });
+                return null;
+            }
+        } else {
+            // 注解设置为false时不忽略租户
+            return joinPoint.proceed();
+        }
+    }
+
+    /**
+     * 判断方法是否有返回值
+     */
+    private boolean hasReturnValue(ProceedingJoinPoint joinPoint) {
+        return !void.class.equals(
+            ((MethodSignature) joinPoint.getSignature()).getReturnType()
+        );
+    }
+}

+ 1 - 0
ruoyi-common/ruoyi-common-annotation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -0,0 +1 @@
+org.dromara.common.annotation.aspect.TenantIgnoreAspect

+ 52 - 0
ruoyi-common/ruoyi-common-baidumap/pom.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-common</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <groupId>org.dromara</groupId>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ruoyi-common-baidumap</artifactId>
+
+    <description>
+        ruoyi-common-baidumap 百度地图模块
+    </description>
+
+    <dependencies>
+        <!-- Spring Boot 基础依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <!-- Web依赖(用于RestTemplate) -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- 配置处理 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <!-- Jackson -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+</project>

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov