ageer 1 سال پیش
والد
کامیت
1f7f97e86a
100فایلهای تغییر یافته به همراه6052 افزوده شده و 2 حذف شده
  1. 18 0
      .editorconfig
  2. 48 0
      .gitignore
  3. 20 0
      LICENSE
  4. 36 0
      README.en.md
  5. 81 2
      README.md
  6. BIN
      image/01.png
  7. BIN
      image/02.png
  8. BIN
      image/03.png
  9. BIN
      image/04.png
  10. BIN
      image/05.png
  11. BIN
      image/06.png
  12. BIN
      image/07.png
  13. BIN
      image/08.png
  14. BIN
      image/09.png
  15. BIN
      image/10.png
  16. BIN
      image/11.png
  17. BIN
      image/12.png
  18. BIN
      image/私有知识库业务架构图.drawio.png
  19. 471 0
      pom.xml
  20. 23 0
      ruoyi-admin/Dockerfile
  21. 138 0
      ruoyi-admin/pom.xml
  22. 23 0
      ruoyi-admin/src/main/java/com/xmzs/PandaApplication.java
  23. 18 0
      ruoyi-admin/src/main/java/com/xmzs/PandaServletInitializer.java
  24. 159 0
      ruoyi-admin/src/main/java/com/xmzs/controller/AuthController.java
  25. 139 0
      ruoyi-admin/src/main/java/com/xmzs/controller/CaptchaController.java
  26. 91 0
      ruoyi-admin/src/main/java/com/xmzs/controller/ChatController.java
  27. 26 0
      ruoyi-admin/src/main/java/com/xmzs/controller/IndexController.java
  28. 151 0
      ruoyi-admin/src/main/java/com/xmzs/controller/PayController.java
  29. 150 0
      ruoyi-admin/src/main/resources/application-dev.yml
  30. 174 0
      ruoyi-admin/src/main/resources/application-prod.yml
  31. 345 0
      ruoyi-admin/src/main/resources/application.yml
  32. 9 0
      ruoyi-admin/src/main/resources/banner.txt
  33. 54 0
      ruoyi-admin/src/main/resources/i18n/messages.properties
  34. 54 0
      ruoyi-admin/src/main/resources/i18n/messages_en_US.properties
  35. 54 0
      ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
  36. BIN
      ruoyi-admin/src/main/resources/ip2region.xdb
  37. 129 0
      ruoyi-admin/src/main/resources/logback-plus.xml
  38. 1 0
      ruoyi-admin/src/main/resources/spy.properties
  39. BIN
      ruoyi-admin/src/main/resources/static/1.jpg
  40. 755 0
      ruoyi-admin/src/main/resources/static/index.html
  41. 45 0
      ruoyi-admin/src/test/java/com/xmzs/test/AssertUnitTest.java
  42. 70 0
      ruoyi-admin/src/test/java/com/xmzs/test/DemoUnitTest.java
  43. 72 0
      ruoyi-admin/src/test/java/com/xmzs/test/ParamUnitTest.java
  44. 54 0
      ruoyi-admin/src/test/java/com/xmzs/test/TagUnitTest.java
  45. 46 0
      ruoyi-common/pom.xml
  46. 178 0
      ruoyi-common/ruoyi-common-bom/pom.xml
  47. 86 0
      ruoyi-common/ruoyi-common-chat/pom.xml
  48. 49 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/ChatConfig.java
  49. 36 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/LocalCache.java
  50. 61 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/WebSocketConfig.java
  51. 26 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/properties/WebSocketProperties.java
  52. 31 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/constant/OpenAIConst.java
  53. 28 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/constant/WebSocketConstants.java
  54. 31 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/ChatProcessRequest.java
  55. 55 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/ChatRequest.java
  56. 33 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/Dall3Request.java
  57. 21 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/SaveMsgRequest.java
  58. 19 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/response/ChatResponse.java
  59. 33 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/BillingUsage.java
  60. 39 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/CreditGrantsResponse.java
  61. 28 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/DailyCost.java
  62. 40 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Datum.java
  63. 21 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Grants.java
  64. 56 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/KeyInfo.java
  65. 25 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/LineItem.java
  66. 17 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Plan.java
  67. 73 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Subscription.java
  68. 238 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/BaseChatCompletion.java
  69. 84 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/BaseMessage.java
  70. 31 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatChoice.java
  71. 34 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletion.java
  72. 26 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletionResponse.java
  73. 30 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletionWithPicture.java
  74. 43 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Content.java
  75. 27 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/FunctionCall.java
  76. 46 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Functions.java
  77. 28 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ImageUrl.java
  78. 117 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Message.java
  79. 114 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/MessagePicture.java
  80. 42 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Parameters.java
  81. 28 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ResponseFormat.java
  82. 31 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolCallFunction.java
  83. 38 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolCalls.java
  84. 25 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoice.java
  85. 33 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoiceObj.java
  86. 21 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoiceObjFunction.java
  87. 35 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/Tools.java
  88. 36 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolsFunction.java
  89. 23 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/Choice.java
  90. 20 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/DeleteResponse.java
  91. 30 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/OpenAiResponse.java
  92. 24 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/Usage.java
  93. 126 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/completions/Completion.java
  94. 27 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/completions/CompletionResponse.java
  95. 29 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/dto/WebSocketMessageDto.java
  96. 104 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/edits/Edit.java
  97. 27 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/edits/EditResponse.java
  98. 54 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/Embedding.java
  99. 25 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/EmbeddingResponse.java
  100. 16 0
      ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/Item.java

+ 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

+ 48 - 0
.gitignore

@@ -0,0 +1,48 @@
+######################################################################
+# 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

+ 20 - 0
LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2023 ruoyi-ai
+
+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.

+ 36 - 0
README.en.md

@@ -0,0 +1,36 @@
+# ruoyi-ai-pro
+
+#### Description
+ruoyi-ai-pro
+
+#### Software Architecture
+Software architecture description
+
+#### Installation
+
+1.  xxxx
+2.  xxxx
+3.  xxxx
+
+#### Instructions
+
+1.  xxxx
+2.  xxxx
+3.  xxxx
+
+#### Contribution
+
+1.  Fork the repository
+2.  Create Feat_xxx branch
+3.  Commit your code
+4.  Create Pull Request
+
+
+#### Gitee Feature
+
+1.  You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
+2.  Gitee blog [blog.gitee.com](https://blog.gitee.com)
+3.  Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
+4.  The most valuable open source project [GVP](https://gitee.com/gvp)
+5.  The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
+6.  The most popular members  [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

+ 81 - 2
README.md

@@ -1,2 +1,81 @@
-# ruoyi-ai
-基于ruoyi-plus实现AI聊天和绘画功能-后端  本项目完全开源免费! 后台管理界面使用elementUI服务端使用Java17+SpringBoot3.X
+## 平台简介
+[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/ageerle/ruoyi-ai/blob/master/LICENSE)
+[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=ruoyi-chatgpt)
+<br>
+[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.0.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
+[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.0-blue.svg)]()
+[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
+
+> 基于ruoyi-plus实现AI聊天和绘画功能-后端
+
+> 本项目完全开源免费!
+后台管理界面使用elementUI服务端使用Java17+SpringBoot3.X
+
+实现功能
+1. 接入ChatGPT-4-1106-preview,gpt-4-vision-preview,dall-e-3模型
+2. 支持ChatGPT-4-All alltools版本,集成识图、画图、联网和code interpreter
+3. 支持GPTS 可以使用openai的所有的GPTs
+4. 接入AzureOpenAI
+5. 接入文生图模型Midjourney( 史上最强AI画图)
+6. 接入微信支付
+7. 支持微信小程序
+
+>测试功能: 私有知识库
+
+>项目地址
+<ul>
+<li>后端: https://gitee.com/ageerle/ruoyi-ai</li>
+<li>小程序端: https://gitee.com/ageerle/ruoyi-uniapp</li>
+<li>前端-后台管理: https://gitee.com/ageerle/ruoyi-admin</li>
+<li>前端-用户端: https://gitee.com/ageerle/ruoyi-web</li>
+<li>演示地址: web.pandarobot.chat</li>
+</ul>
+
+
+## 小程序演示
+<div>
+  <img style="margin:10px" src="./image/03.png" alt="drawing" width="300px" height="400px"/>
+  <img style="margin:10px" src="./image/04.png" alt="drawing" width="300px" height="400px"/>
+</div>
+
+## H5演示
+<div>
+  <img style="margin:10px" src="./image/05.png" alt="drawing" width="300px" height="400px"/>
+  <img style="margin:10px" src="./image/06.png" alt="drawing" width="300px" height="400px"/>
+</div>
+
+## PC端演示
+<div>
+  <img style="margin-top:10px" src="./image/07.png" alt="drawing" width="550px" height="300px"/>
+  <img style="margin-top:10px" src="./image/08.png" alt="drawing" width="550px" height="300px"/>
+</div>
+
+## MJ绘图
+<div>
+  <img style="margin-top:10px" src="./image/10.png" alt="drawing" width="550px" height="300px"/>
+  <img style="margin-top:10px" src="./image/11.png" alt="drawing" width="550px" height="300px"/>
+</div>
+
+## 微信智能助手
+<div>
+  <img style="margin-top:10px" src="./image/09.png" alt="drawing" width="550px" height="300px"/>
+</div>
+
+## 私有知识库管理(开发中)
+<div>
+  <img style="margin-top:10px" src="./image/12.png" alt="drawing" width="550px" height="300px"/>
+  <img style="margin-top:10px" src="./image/私有知识库业务架构图.drawio.png" alt="drawing" width="550px" height="300px"/>
+</div>
+
+## 进群学习
+<div>
+  <img src="./image/01.png" alt="drawing" width="300px" height="300px"/>
+  <img src="./image/02.png" alt="drawing" width="300px" height="300px"/>
+</div>
+
+
+## 参考项目
+<ol>
+<li>https://github.com/Grt1228/chatgpt-java</li>
+<li>https://gitee.com/dromara/RuoYi-Vue-Plus</li>
+</ol>

BIN
image/01.png


BIN
image/02.png


BIN
image/03.png


BIN
image/04.png


BIN
image/05.png


BIN
image/06.png


BIN
image/07.png


BIN
image/08.png


BIN
image/09.png


BIN
image/10.png


BIN
image/11.png


BIN
image/12.png


BIN
image/私有知识库业务架构图.drawio.png


+ 471 - 0
pom.xml

@@ -0,0 +1,471 @@
+<?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>com.xmzs</groupId>
+    <artifactId>ruoyi-ai</artifactId>
+    <version>${revision}</version>
+
+    <name>ruoyi-ai</name>
+    <url>https://gitee.com/ageerle/ruoyi-ai</url>
+    <description>AI助手后台管理系统</description>
+
+    <properties>
+        <revision>1.0.0</revision>
+        <spring-boot.version>3.0.6</spring-boot.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>17</java.version>
+        <spring-boot.mybatis>3.0.1</spring-boot.mybatis>
+        <springdoc.version>2.1.0</springdoc.version>
+        <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
+        <poi.version>5.2.3</poi.version>
+        <easyexcel.version>3.2.1</easyexcel.version>
+        <velocity.version>2.3</velocity.version>
+        <satoken.version>1.34.0</satoken.version>
+        <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
+        <p6spy.version>3.9.1</p6spy.version>
+        <hutool.version>5.8.18</hutool.version>
+        <okhttp.version>4.10.0</okhttp.version>
+        <spring-boot-admin.version>3.0.3</spring-boot-admin.version>
+        <redisson.version>3.20.1</redisson.version>
+        <lock4j.version>2.2.4</lock4j.version>
+        <dynamic-ds.version>3.6.1</dynamic-ds.version>
+        <alibaba-ttl.version>2.14.2</alibaba-ttl.version>
+        <xxl-job.version>2.4.0</xxl-job.version>
+        <mapstruct-plus.version>1.2.1</mapstruct-plus.version>
+        <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
+        <lombok.version>1.18.26</lombok.version>
+        <bouncycastle.version>1.72</bouncycastle.version>
+        <!-- 离线IP地址定位库 -->
+        <ip2region.version>2.7.0</ip2region.version>
+
+        <!-- 临时修复 snakeyaml 漏洞 -->
+        <snakeyaml.version>1.33</snakeyaml.version>
+
+        <!-- OSS 配置 -->
+        <aws-java-sdk-s3.version>1.12.400</aws-java-sdk-s3.version>
+        <!-- SMS 配置 -->
+        <aliyun.sms.version>2.0.23</aliyun.sms.version>
+        <tencent.sms.version>3.1.687</tencent.sms.version>
+
+        <!-- 插件版本 -->
+        <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
+        <maven-war-plugin.version>3.2.2</maven-war-plugin.version>
+        <maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
+        <maven-surefire-plugin.version>3.0.0</maven-surefire-plugin.version>
+        <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
+        <weixin-java-miniapp.version>4.5.0</weixin-java-miniapp.version>
+    </properties>
+
+    <profiles>
+        <profile>
+            <id>local</id>
+            <properties>
+                <!-- 环境标识,需要与配置文件的名称相对应 -->
+                <profiles.active>local</profiles.active>
+                <logging.level>debug</logging.level>
+            </properties>
+        </profile>
+        <profile>
+            <id>dev</id>
+            <properties>
+                <!-- 环境标识,需要与配置文件的名称相对应 -->
+                <profiles.active>dev</profiles.active>
+                <logging.level>debug</logging.level>
+            </properties>
+            <activation>
+                <!-- 默认环境 -->
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+        <profile>
+            <id>prod</id>
+            <properties>
+                <profiles.active>prod</profiles.active>
+                <logging.level>warn</logging.level>
+            </properties>
+        </profile>
+    </profiles>
+
+    <!-- 依赖声明 -->
+    <dependencyManagement>
+        <dependencies>
+
+            <!-- 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>
+
+            <!-- common 的依赖配置-->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-bom</artifactId>
+                <version>${revision}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </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>org.apache.poi</groupId>
+                <artifactId>poi</artifactId>
+                <version>${poi.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.poi</groupId>
+                <artifactId>poi-ooxml</artifactId>
+                <version>${poi.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>easyexcel</artifactId>
+                <version>${easyexcel.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.apache.poi</groupId>
+                        <artifactId>poi-ooxml-schemas</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+
+            <!-- velocity代码生成使用模板 -->
+            <dependency>
+                <groupId>org.apache.velocity</groupId>
+                <artifactId>velocity-engine-core</artifactId>
+                <version>${velocity.version}</version>
+            </dependency>
+
+            <!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-spring-boot3-starter</artifactId>
+                <version>${satoken.version}</version>
+            </dependency>
+            <!-- Sa-Token 整合 jwt -->
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-jwt</artifactId>
+                <version>${satoken.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>cn.hutool</groupId>
+                        <artifactId>hutool-all</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-core</artifactId>
+                <version>${satoken.version}</version>
+            </dependency>
+
+            <!-- dynamic-datasource 多数据源-->
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
+                <version>${dynamic-ds.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.mybatis.spring.boot</groupId>
+                <artifactId>mybatis-spring-boot-starter</artifactId>
+                <version>${spring-boot.mybatis}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-boot-starter</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>com.amazonaws</groupId>
+                <artifactId>aws-java-sdk-s3</artifactId>
+                <version>${aws-java-sdk-s3.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.aliyun</groupId>
+                <artifactId>dysmsapi20170525</artifactId>
+                <version>${aliyun.sms.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.tencentcloudapi</groupId>
+                <artifactId>tencentcloud-sdk-java-sms</artifactId>
+                <version>${tencent.sms.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>de.codecentric</groupId>
+                <artifactId>spring-boot-admin-starter-server</artifactId>
+                <version>${spring-boot-admin.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>de.codecentric</groupId>
+                <artifactId>spring-boot-admin-starter-client</artifactId>
+                <version>${spring-boot-admin.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>
+
+            <!-- xxl-job-core -->
+            <dependency>
+                <groupId>com.xuxueli</groupId>
+                <artifactId>xxl-job-core</artifactId>
+                <version>${xxl-job.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>transmittable-thread-local</artifactId>
+                <version>${alibaba-ttl.version}</version>
+            </dependency>
+
+            <!-- 临时修复 snakeyaml 漏洞 -->
+            <dependency>
+                <groupId>org.yaml</groupId>
+                <artifactId>snakeyaml</artifactId>
+                <version>${snakeyaml.version}</version>
+            </dependency>
+
+            <!-- 加密包引入 -->
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcprov-jdk15to18</artifactId>
+                <version>${bouncycastle.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>io.github.linpeilie</groupId>
+                <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
+                <version>${mapstruct-plus.version}</version>
+            </dependency>
+
+            <!-- 离线IP地址定位库 ip2region -->
+            <dependency>
+                <groupId>org.lionsoul</groupId>
+                <artifactId>ip2region</artifactId>
+                <version>${ip2region.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-system</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-job</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-generator</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-demo</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+
+    <modules>
+        <module>ruoyi-admin</module>
+        <module>ruoyi-common</module>
+        <module>ruoyi-modules</module>
+    </modules>
+    <packaging>pom</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${maven-compiler-plugin.verison}</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>${therapi-javadoc.version}</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>
+                    <!-- 根据打包环境执行对应的@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/resources</directory>
+                <!-- 引入所有 匹配文件进行过滤 -->
+                <includes>
+                    <include>application*</include>
+                    <include>bootstrap*</include>
+                    <include>banner*</include>
+                </includes>
+                <!-- 启用过滤 即该资源中的变量将会被过滤器中的值替换 -->
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+    </build>
+
+    <repositories>
+        <repository>
+            <id>public</id>
+            <name>aliyun nexus</name>
+            <url>https://maven.aliyun.com/repository/public/</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+        </repository>
+    </repositories>
+
+    <pluginRepositories>
+        <pluginRepository>
+            <id>public</id>
+            <name>aliyun nexus</name>
+            <url>https://maven.aliyun.com/repository/public/</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </pluginRepository>
+    </pluginRepositories>
+
+</project>
+
+

+ 23 - 0
ruoyi-admin/Dockerfile

@@ -0,0 +1,23 @@
+FROM findepi/graalvm:java17-native
+
+MAINTAINER Lion Li
+
+RUN mkdir -p /ruoyi/server/logs \
+    /ruoyi/server/temp \
+    /ruoyi/skywalking/agent
+
+WORKDIR /ruoyi/server
+
+ENV SERVER_PORT=8080
+
+EXPOSE ${SERVER_PORT}
+
+ADD ./target/ruoyi-admin.jar ./app.jar
+
+ENTRYPOINT ["java", \
+            "-Djava.security.egd=file:/dev/./urandom", \
+            "-Dserver.port=${SERVER_PORT}", \
+            # 应用名称 如果想区分集群节点监控 改成不同的名称即可
+#            "-Dskywalking.agent.service_name=ruoyi-server", \
+#            "-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar", \
+            "-jar", "app.jar"]

+ 138 - 0
ruoyi-admin/pom.xml

@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>ruoyi-ai</artifactId>
+        <groupId>com.xmzs</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+    <artifactId>ruoyi-admin</artifactId>
+
+    <description>
+        web服务入口
+    </description>
+
+    <dependencies>
+
+        <!-- Mysql驱动包 -->
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+        </dependency>
+        <!-- Oracle -->
+        <dependency>
+            <groupId>com.oracle.database.jdbc</groupId>
+            <artifactId>ojdbc8</artifactId>
+        </dependency>
+        <!-- PostgreSql -->
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+        </dependency>
+        <!-- SqlServer -->
+        <dependency>
+            <groupId>com.microsoft.sqlserver</groupId>
+            <artifactId>mssql-jdbc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.xmzs</groupId>
+            <artifactId>ruoyi-common-doc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.xmzs</groupId>
+            <artifactId>ruoyi-system</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.xmzs</groupId>
+            <artifactId>ruoyi-common-chat</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.xmzs</groupId>
+            <artifactId>ruoyi-job</artifactId>
+        </dependency>
+
+        <!-- 代码生成-->
+        <dependency>
+            <groupId>com.xmzs</groupId>
+            <artifactId>ruoyi-generator</artifactId>
+        </dependency>
+
+        <!--  demo模块  -->
+        <dependency>
+            <groupId>com.xmzs</groupId>
+            <artifactId>ruoyi-demo</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>de.codecentric</groupId>
+            <artifactId>spring-boot-admin-starter-client</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- skywalking 整合 logback -->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.skywalking</groupId>-->
+<!--            <artifactId>apm-toolkit-logback-1.x</artifactId>-->
+<!--            <version>${与你的agent探针版本保持一致}</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.skywalking</groupId>-->
+<!--            <artifactId>apm-toolkit-trace</artifactId>-->
+<!--            <version>${与你的agent探针版本保持一致}</version>-->
+<!--        </dependency>-->
+
+        <!-- 添加thumbnailator依赖 -->
+        <dependency>
+            <groupId>net.coobird</groupId>
+            <artifactId>thumbnailator</artifactId>
+            <version>0.4.11</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>${maven-jar-plugin.version}</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <version>${maven-war-plugin.version}</version>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                    <warName>${project.artifactId}</warName>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 23 - 0
ruoyi-admin/src/main/java/com/xmzs/PandaApplication.java

@@ -0,0 +1,23 @@
+package com.xmzs;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
+
+/**
+ * 启动程序
+ *
+ * @author Lion Li
+ */
+
+@SpringBootApplication
+public class PandaApplication {
+
+
+    public static void main(String[] args) {
+        SpringApplication application = new SpringApplication(PandaApplication.class);
+        application.setApplicationStartup(new BufferingApplicationStartup(2048));
+        application.run(args);
+        System.out.println("(♥◠‿◠)ノ゙  panda智能助手启动成功   ლ(´ڡ`ლ)゙");
+    }
+}

+ 18 - 0
ruoyi-admin/src/main/java/com/xmzs/PandaServletInitializer.java

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

+ 159 - 0
ruoyi-admin/src/main/java/com/xmzs/controller/AuthController.java

@@ -0,0 +1,159 @@
+package com.xmzs.controller;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.hutool.core.collection.CollUtil;
+import com.xmzs.common.core.constant.Constants;
+import com.xmzs.common.core.domain.R;
+import com.xmzs.common.core.domain.model.*;
+import com.xmzs.common.core.utils.MapstructUtils;
+import com.xmzs.common.core.utils.StreamUtils;
+import com.xmzs.common.core.utils.StringUtils;
+import com.xmzs.common.satoken.utils.LoginHelper;
+import com.xmzs.common.tenant.helper.TenantHelper;
+import com.xmzs.system.domain.bo.SysTenantBo;
+import com.xmzs.system.domain.vo.LoginTenantVo;
+import com.xmzs.system.domain.vo.SysTenantVo;
+import com.xmzs.system.domain.vo.TenantListVo;
+import com.xmzs.system.service.ISysTenantService;
+
+
+import com.xmzs.system.service.SysLoginService;
+import com.xmzs.system.service.SysRegisterService;
+import com.xmzs.web.domain.vo.LoginVo;
+
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.net.URL;
+import java.util.List;
+
+/**
+ * 认证
+ *
+ * @author Lion Li
+ */
+@SaIgnore
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/auth")
+public class AuthController {
+
+    private final SysLoginService loginService;
+    private final SysRegisterService registerService;
+    private final ISysTenantService tenantService;
+
+
+
+    /**
+     * 登录方法
+     *
+     * @param body 登录信息
+     * @return 结果
+     */
+    @PostMapping("/login")
+    public R<LoginVo> login(@Validated @RequestBody LoginBody body) {
+        body.setTenantId(Constants.TENANT_ID);
+        LoginVo loginVo = new LoginVo();
+        // 生成令牌
+        String token = loginService.login(
+                body.getTenantId(),
+                body.getUsername(), body.getPassword(),
+                body.getCode(), body.getUuid());
+        loginVo.setToken(token);
+        loginVo.setUserInfo(LoginHelper.getLoginUser());
+        return R.ok(loginVo);
+    }
+
+    /**
+     * 短信登录
+     *
+     * @param body 登录信息
+     * @return 结果
+     */
+    @PostMapping("/smsLogin")
+    public R<LoginVo> smsLogin(@Validated @RequestBody SmsLoginBody body) {
+        LoginVo loginVo = new LoginVo();
+        // 生成令牌
+        String token = loginService.smsLogin(body.getTenantId(), body.getPhonenumber(), body.getSmsCode());
+        loginVo.setToken(token);
+        return R.ok(loginVo);
+    }
+
+    /**
+     * 邮件登录
+     *
+     * @param body 登录信息
+     * @return 结果
+     */
+    @PostMapping("/emailLogin")
+    public R<LoginVo> emailLogin(@Validated @RequestBody EmailLoginBody body) {
+        LoginVo loginVo = new LoginVo();
+        // 生成令牌
+        String token = loginService.emailLogin(body.getTenantId(), body.getEmail(), body.getEmailCode());
+        loginVo.setToken(token);
+        return R.ok(loginVo);
+    }
+
+    /**
+     * 游客登录
+     *
+     * @param loginBody
+     * @return 结果
+     */
+    @PostMapping("/visitorLogin")
+    public R<LoginVo> xcxLogin(@RequestBody VisitorLoginBody loginBody) {
+        return R.ok(loginService.visitorLogin(loginBody));
+    }
+
+    /**
+     * 退出登录
+     */
+    @PostMapping("/logout")
+    public R<Void> logout() {
+        loginService.logout();
+        return R.ok("退出成功");
+    }
+
+    /**
+     * 用户注册
+     */
+    @PostMapping("/register")
+    public R<Void> register(@Validated @RequestBody RegisterBody user) {
+        registerService.register(user);
+        return R.ok();
+    }
+
+    /**
+     * 重置密码
+     */
+    @PostMapping("/reset/password")
+    @SaIgnore
+    public R<Void> resetPassWord(@Validated @RequestBody RegisterBody user) {
+        registerService.resetPassWord(user);
+        return R.ok();
+    }
+
+    /**
+     * 登录页面租户下拉框
+     *
+     * @return 租户列表
+     */
+    @GetMapping("/tenant/list")
+    public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
+        List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
+        List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class);
+        // 获取域名
+        String host = new URL(request.getRequestURL().toString()).getHost();
+        // 根据域名进行筛选
+        List<TenantListVo> list = StreamUtils.filter(voList, vo -> StringUtils.equals(vo.getDomain(), host));
+        // 返回对象
+        LoginTenantVo vo = new LoginTenantVo();
+        vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
+        vo.setTenantEnabled(TenantHelper.isEnable());
+        return R.ok(vo);
+    }
+
+}

+ 139 - 0
ruoyi-admin/src/main/java/com/xmzs/controller/CaptchaController.java

@@ -0,0 +1,139 @@
+package com.xmzs.controller;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.hutool.captcha.AbstractCaptcha;
+import cn.hutool.captcha.generator.CodeGenerator;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.xmzs.common.core.constant.Constants;
+import com.xmzs.common.core.constant.GlobalConstants;
+import com.xmzs.common.core.domain.R;
+import com.xmzs.common.core.utils.SpringUtils;
+import com.xmzs.common.core.utils.StringUtils;
+import com.xmzs.common.core.utils.reflect.ReflectUtils;
+import com.xmzs.common.mail.config.properties.MailProperties;
+import com.xmzs.common.mail.utils.MailUtils;
+import com.xmzs.common.redis.utils.RedisUtils;
+import com.xmzs.common.sms.config.properties.SmsProperties;
+import com.xmzs.common.sms.core.SmsTemplate;
+import com.xmzs.common.sms.entity.SmsResult;
+import com.xmzs.common.web.config.properties.CaptchaProperties;
+import com.xmzs.common.web.enums.CaptchaType;
+import com.xmzs.web.domain.request.EmailRequest;
+import com.xmzs.web.domain.vo.CaptchaVo;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+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.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 验证码操作处理
+ *
+ * @author Lion Li
+ */
+@SaIgnore
+@Slf4j
+@Validated
+@RequiredArgsConstructor
+@RestController
+public class CaptchaController {
+
+    private final CaptchaProperties captchaProperties;
+    private final SmsProperties smsProperties;
+    private final MailProperties mailProperties;
+
+    /**
+     * 短信验证码
+     *
+     * @param phonenumber 用户手机号
+     */
+    @GetMapping("/resource/sms/code")
+    public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
+        if (!smsProperties.getEnabled()) {
+            return R.fail("当前系统没有开启短信功能!");
+        }
+        String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
+        String code = RandomUtil.randomNumbers(4);
+        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
+        // 验证码模板id 自行处理 (查数据库或写死均可)
+        String templateId = "";
+        Map<String, String> map = new HashMap<>(1);
+        map.put("code", code);
+        SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
+        SmsResult result = smsTemplate.send(phonenumber, templateId, map);
+        if (!result.isSuccess()) {
+            log.error("验证码短信发送异常 => {}", result);
+            return R.fail(result.getMessage());
+        }
+        return R.ok();
+    }
+
+    /**
+     * 邮箱验证码
+     *
+     * @param emailRequest 用户邮箱
+     */
+    @PostMapping("/resource/email/code")
+    public R<Void> emailCode(@RequestBody @Valid EmailRequest emailRequest) {
+        if (!mailProperties.getEnabled()) {
+            return R.fail("当前系统没有开启邮箱功能!");
+        }
+        String key = GlobalConstants.CAPTCHA_CODE_KEY + emailRequest.getUsername();
+        String code = RandomUtil.randomNumbers(4);
+        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
+        try {
+            MailUtils.sendText(emailRequest.getUsername(), "【GPT助手】登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
+        } catch (Exception e) {
+            log.error("验证码短信发送异常 => {}", e.getMessage());
+            return R.fail(e.getMessage());
+        }
+        return R.ok();
+    }
+
+    /**
+     * 生成验证码
+     */
+    @GetMapping("/code")
+    public R<CaptchaVo> getCode() {
+        CaptchaVo captchaVo = new CaptchaVo();
+        boolean captchaEnabled = captchaProperties.getEnable();
+        if (!captchaEnabled) {
+            captchaVo.setCaptchaEnabled(false);
+            return R.ok(captchaVo);
+        }
+        // 保存验证码信息
+        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();
+        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.setUuid(uuid);
+        captchaVo.setImg(captcha.getImageBase64());
+        return R.ok(captchaVo);
+    }
+
+}

+ 91 - 0
ruoyi-admin/src/main/java/com/xmzs/controller/ChatController.java

@@ -0,0 +1,91 @@
+package com.xmzs.controller;
+
+
+import com.xmzs.common.chat.domain.request.ChatRequest;
+import com.xmzs.common.chat.domain.request.Dall3Request;
+import com.xmzs.common.chat.entity.images.Item;
+import com.xmzs.common.core.domain.R;
+import com.xmzs.common.core.domain.model.LoginUser;
+import com.xmzs.common.core.exception.base.BaseException;
+import com.xmzs.common.mybatis.core.page.PageQuery;
+import com.xmzs.common.mybatis.core.page.TableDataInfo;
+import com.xmzs.common.satoken.utils.LoginHelper;
+import com.xmzs.system.domain.bo.ChatMessageBo;
+import com.xmzs.system.domain.vo.ChatMessageVo;
+import com.xmzs.system.service.IChatMessageService;
+import com.xmzs.system.service.SseService;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @date 2023-03-01
+ */
+@Controller
+@Slf4j
+@RequiredArgsConstructor
+public class ChatController {
+
+    private final SseService sseService;
+
+    private final  IChatMessageService chatMessageService;
+
+    /**
+     * 聊天接口
+     */
+    @PostMapping("/chat")
+    @ResponseBody
+    public SseEmitter sseChat(@RequestBody @Valid ChatRequest chatRequest) {
+        if("gpt-4-all".equals(chatRequest.getModel())
+            || chatRequest.getModel().startsWith("gpt-4-gizmo")
+            || chatRequest.getModel().startsWith("net-")
+        ){
+            return sseService.transitChat(chatRequest);
+        }
+        if("azure-gpt-3.5".equals(chatRequest.getModel())){
+            return sseService.azureChat(chatRequest);
+        }
+        return sseService.sseChat(chatRequest);
+    }
+
+    @PostMapping("/dall3")
+    @ResponseBody
+    public R<List<Item>> dall3(@RequestBody @Valid Dall3Request request) {
+       return R.ok(sseService.dall3(request));
+    }
+
+    @PostMapping("/mjTask")
+    @ResponseBody
+    public R<String> mjTask() {
+        sseService.mjTask();
+        return R.ok();
+    }
+
+    /**
+     * 聊天记录
+     */
+    @PostMapping("/chatList")
+    @ResponseBody
+    public R<TableDataInfo<ChatMessageVo>> list(@RequestBody @Valid ChatMessageBo chatRequest,@RequestBody PageQuery pageQuery) {
+        // 默认查询当前登录用户消息记录
+        LoginUser loginUser = LoginHelper.getLoginUser();
+        if (loginUser == null) {
+            throw new BaseException("用户未登录!");
+        }
+        chatRequest.setUserId(loginUser.getUserId());
+        TableDataInfo<ChatMessageVo> chatMessageVoTableDataInfo = chatMessageService.queryPageList(chatRequest, pageQuery);
+        return R.ok(chatMessageVoTableDataInfo);
+    }
+
+}

+ 26 - 0
ruoyi-admin/src/main/java/com/xmzs/controller/IndexController.java

@@ -0,0 +1,26 @@
+package com.xmzs.controller;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+
+/**
+ * 首页
+ *
+ * @author Lion Li
+ */
+@SaIgnore
+@RequiredArgsConstructor
+@Controller
+public class IndexController {
+
+
+    /**
+     * 访问首页,提示语
+     */
+    @GetMapping("/")
+    public String index() {
+        return "index.html";
+    }
+}

+ 151 - 0
ruoyi-admin/src/main/java/com/xmzs/controller/PayController.java

@@ -0,0 +1,151 @@
+package com.xmzs.controller;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.extra.qrcode.QrCodeUtil;
+import com.xmzs.common.config.PayConfig;
+import com.xmzs.common.core.domain.R;
+import com.xmzs.common.core.domain.model.LoginUser;
+import com.xmzs.common.core.exception.base.BaseException;
+import com.xmzs.common.core.utils.StringUtils;
+import com.xmzs.common.oss.core.OssClient;
+import com.xmzs.common.oss.entity.UploadResult;
+import com.xmzs.common.oss.factory.OssFactory;
+import com.xmzs.common.response.PayResponse;
+import com.xmzs.common.satoken.utils.LoginHelper;
+import com.xmzs.common.service.PayService;
+import com.xmzs.common.utils.MD5Util;
+import com.xmzs.system.domain.bo.PaymentOrdersBo;
+import com.xmzs.system.domain.bo.SysUserBo;
+import com.xmzs.system.domain.request.OrderRequest;
+import com.xmzs.system.domain.vo.PaymentOrdersVo;
+import com.xmzs.system.domain.vo.SysUserVo;
+import com.xmzs.system.service.IPaymentOrdersService;
+import com.xmzs.system.service.ISysUserService;
+import com.xmzs.system.util.OrderNumberGenerator;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/pay")
+@Slf4j
+public class PayController {
+
+    private final PayService payService;
+
+    private final ISysUserService userService;
+
+    private final IPaymentOrdersService paymentOrdersService;
+
+    /**
+     * 获取支付二维码
+     *
+     * @Date 2023/7/3
+     * @param response
+     * @return void
+     **/
+    @PostMapping("/payUrl")
+    public R<PaymentOrdersVo> payUrl(HttpServletResponse response, @RequestBody OrderRequest orderRequest) {
+        LoginUser loginUser = LoginHelper.getLoginUser();
+        // 创建订单
+        PaymentOrdersBo paymentOrders = new PaymentOrdersBo();
+        paymentOrders.setOrderName(orderRequest.getName());
+        paymentOrders.setAmount(new BigDecimal(orderRequest.getMoney()));
+        String orderNo = OrderNumberGenerator.generate();
+        paymentOrders.setOrderNo(orderNo);
+        paymentOrders.setUserId(loginUser.getUserId());
+        // TODO 支付状态默认待支付 - 添加枚举
+        paymentOrders.setPaymentStatus("1");
+        paymentOrdersService.insertByBo(paymentOrders);
+        String payUrl = payService.getPayUrl(orderNo, orderRequest.getName(), Double.parseDouble(orderRequest.getMoney()), "192.168.1.6");
+        byte[] bytes = QrCodeUtil.generatePng(payUrl, 300, 300);
+        OssClient storage = OssFactory.instance();
+        UploadResult upload=storage.upload(bytes, storage.getPath("qrCode",".png"), "image/png");
+        PaymentOrdersVo paymentOrdersVo = new PaymentOrdersVo();
+        BeanUtil.copyProperties(paymentOrders,paymentOrdersVo);
+        paymentOrdersVo.setUrl(upload.getUrl());
+        return R.ok(paymentOrdersVo);
+    }
+
+
+    /**
+     * 跳转通知地址
+     *
+     * @Date 2023/7/3
+     * @param
+     * @return void
+     **/
+    @PostMapping("/notifyUrl")
+    public void notifyUrl() {
+        log.info("notifyUrl===========");
+    }
+
+    /**
+     * 获取订单信息
+     *
+     */
+    @PostMapping("/orderInfo")
+    public R<PaymentOrdersVo> orderInfo(@RequestBody  OrderRequest orderRequest) {
+        if(StringUtils.isEmpty(orderRequest.getOrderNo())){
+            throw new BaseException("订单号不能为空!");
+        }
+        PaymentOrdersBo paymentOrdersBo = new PaymentOrdersBo();
+        paymentOrdersBo.setOrderNo(orderRequest.getOrderNo());
+        List<PaymentOrdersVo> paymentOrdersList = paymentOrdersService.queryList(paymentOrdersBo);
+        if (CollectionUtil.isEmpty(paymentOrdersList)){
+            throw new BaseException("订单不存在!");
+        }
+        PaymentOrdersVo paymentOrdersVo = paymentOrdersList.get(0);
+        return R.ok(paymentOrdersVo);
+    }
+
+    /**
+     * 跳转通知地址
+     *
+     * @Date 2023/7/3
+     * @param payResponse
+     * @return void
+     **/
+    @GetMapping("/returnUrl")
+    public String returnUrl(PayResponse payResponse) {
+        // 校验签名
+        String mdString = "money=" + payResponse.getMoney() + "&name=" + payResponse.getName() +
+            "&out_trade_no=" + payResponse.getOut_trade_no() + "&pid=" + PayConfig.pid +
+            "&trade_no=" + payResponse.getTrade_no() + "&trade_status=" + payResponse.getTrade_status() +
+            "&type=" + payResponse.getType() +  PayConfig.key;
+        String sign = MD5Util.GetMD5Code(mdString);
+        if(!sign.equals(payResponse.getSign())){
+            throw new BaseException("校验签名失败!");
+        }
+        double money = Double.parseDouble(payResponse.getMoney());
+        log.info("支付订单号{}",payResponse);
+        PaymentOrdersBo paymentOrdersBo = new PaymentOrdersBo();
+        paymentOrdersBo.setOrderNo(payResponse.getOut_trade_no());
+        List<PaymentOrdersVo> paymentOrdersList = paymentOrdersService.queryList(paymentOrdersBo);
+        if (CollectionUtil.isEmpty(paymentOrdersList)){
+            throw new BaseException("订单不存在!");
+        }
+        // 订单状态修改为已支付
+        PaymentOrdersVo paymentOrdersVo = paymentOrdersList.get(0);
+        paymentOrdersVo.setPaymentStatus("2");
+        paymentOrdersVo.setPaymentMethod(payResponse.getType());
+        BeanUtil.copyProperties(paymentOrdersVo,paymentOrdersBo);
+        paymentOrdersService.updateByBo(paymentOrdersBo);
+        SysUserVo sysUserVo = userService.selectUserById(paymentOrdersVo.getUserId());
+        sysUserVo.setUserBalance(sysUserVo.getUserBalance()+money);
+        SysUserBo sysUserBo = new SysUserBo();
+        BeanUtil.copyProperties(sysUserVo,sysUserBo);
+        // 设置为付费用户
+        sysUserBo.setUserGrade("1");
+        userService.updateUser(sysUserBo);
+        return "success";
+    }
+
+}
+

+ 150 - 0
ruoyi-admin/src/main/resources/application-dev.yml

@@ -0,0 +1,150 @@
+--- # 监控中心配置
+spring.boot.admin.client:
+  # 增加客户端开关
+  enabled: true
+  url: http://localhost:9090/admin
+  instance:
+    service-host-type: IP
+  username: ruoyi
+  password: 123456
+
+--- # xxl-job 配置
+xxl.job:
+  # 执行器开关
+  enabled: false
+  # 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。
+  admin-addresses: http://localhost:9100/xxl-job-admin
+  # 执行器通讯TOKEN:非空时启用
+  access-token: xxl-job
+  executor:
+    # 执行器AppName:执行器心跳注册分组依据;为空则关闭自动注册
+    appname: xxl-job-executor
+    # 执行器端口号 执行器从9101开始往后写
+    port: 9101
+    # 执行器注册:默认IP:PORT
+    address:
+    # 执行器IP:默认自动获取IP
+    ip:
+    # 执行器运行日志文件存储磁盘路径
+    logpath: ./logs/xxl-job
+    # 执行器日志文件保存天数:大于3生效
+    logretentiondays: 30
+
+--- # 数据源配置
+spring:
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+    # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
+    dynamic:
+      # 性能分析插件(有性能损耗 不建议生产环境使用)
+      p6spy: true
+      # 设置默认的数据源或者数据源组,默认值即为 master
+      primary: master
+      # 严格模式 匹配不到数据源则报错
+      strict: true
+      datasource:
+        # 主库数据源
+        master:
+          type: ${spring.datasource.type}
+          driverClassName: com.mysql.cj.jdbc.Driver
+          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
+          # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
+          url: jdbc:mysql://127.0.0.1:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
+          username: ry-vue
+          password: ry-vue
+
+        # 从库数据源
+#        slave:
+#          lazy: true
+#          type: ${spring.datasource.type}
+#          driverClassName: com.mysql.cj.jdbc.Driver
+#          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
+#          username:
+#          password:
+#        oracle:
+#          type: ${spring.datasource.type}
+#          driverClassName: oracle.jdbc.OracleDriver
+#          url: jdbc:oracle:thin:@//localhost:1521/XE
+#          username: ROOT
+#          password: root
+#          hikari:
+#            connectionTestQuery: SELECT 1 FROM DUAL
+#        postgres:
+#          type: ${spring.datasource.type}
+#          driverClassName: org.postgresql.Driver
+#          url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
+#          username: root
+#          password: root
+#        sqlserver:
+#          type: ${spring.datasource.type}
+#          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
+#          url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
+#          username: SA
+#          password: root
+      hikari:
+        # 最大连接池数量
+        maxPoolSize: 20
+        # 最小空闲线程数量
+        minIdle: 10
+        # 配置获取连接等待超时的时间
+        connectionTimeout: 30000
+        # 校验超时时间
+        validationTimeout: 5000
+        # 空闲连接存活最大时间,默认10分钟
+        idleTimeout: 600000
+        # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
+        maxLifetime: 1800000
+        # 连接测试query(配置检测连接是否有效)
+        connectionTestQuery: SELECT 1
+        # 多久检查一次连接的活性
+        keepaliveTime: 30000
+
+--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
+spring.data:
+  redis:
+    # 地址
+    host: localhost
+    # 端口,默认为6379
+    port: 6379
+    # 数据库索引
+    database: 0
+    # 密码(如没有密码请注释掉)
+    # password:
+    # 连接超时时间
+    timeout: 10s
+    # 是否开启ssl
+    ssl: false
+
+redisson:
+  # redis key前缀
+  keyPrefix:
+  # 线程池数量
+  threads: 4
+  # Netty线程池数量
+  nettyThreads: 8
+  # 单节点配置
+  singleServerConfig:
+    # 客户端名称
+    clientName: ${ruoyi.name}
+    # 最小空闲连接数
+    connectionMinimumIdleSize: 8
+    # 连接池大小
+    connectionPoolSize: 32
+    # 连接空闲超时,单位:毫秒
+    idleConnectionTimeout: 10000
+    # 命令等待超时,单位:毫秒
+    timeout: 3000
+    # 发布和订阅连接池大小
+    subscriptionConnectionPoolSize: 50
+
+--- # sms 短信
+sms:
+  enabled: false
+  # 阿里云 dysmsapi.aliyuncs.com
+  # 腾讯云 sms.tencentcloudapi.com
+  endpoint: "dysmsapi.aliyuncs.com"
+  accessKeyId: xxxxxxx
+  accessKeySecret: xxxxxx
+  signName: 测试
+  # 腾讯专用
+  sdkAppId:

+ 174 - 0
ruoyi-admin/src/main/resources/application-prod.yml

@@ -0,0 +1,174 @@
+--- # 临时文件存储位置 避免临时文件被系统清理报错
+spring.servlet.multipart.location: /ruoyi/server/temp
+
+--- # 监控中心配置
+spring.boot.admin.client:
+  # 增加客户端开关
+  enabled: true
+  url: http://localhost:9090/admin
+  instance:
+    service-host-type: IP
+  username: ruoyi
+  password: 123456
+
+--- # xxl-job 配置
+xxl.job:
+  # 执行器开关
+  enabled: false
+  # 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。
+  admin-addresses: http://localhost:9100/xxl-job-admin
+  # 执行器通讯TOKEN:非空时启用
+  access-token: xxl-job
+  executor:
+    # 执行器AppName:执行器心跳注册分组依据;为空则关闭自动注册
+    appname: xxl-job-executor
+    # 执行器端口号 执行器从9101开始往后写
+    port: 9101
+    # 执行器注册:默认IP:PORT
+    address:
+    # 执行器IP:默认自动获取IP
+    ip:
+    # 执行器运行日志文件存储磁盘路径
+    logpath: ./logs/xxl-job
+    # 执行器日志文件保存天数:大于3生效
+    logretentiondays: 30
+
+--- # 数据源配置
+spring:
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+    # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
+    dynamic:
+      # 性能分析插件(有性能损耗 不建议生产环境使用)
+      p6spy: false
+      # 设置默认的数据源或者数据源组,默认值即为 master
+      primary: master
+      # 严格模式 匹配不到数据源则报错
+      strict: true
+      datasource:
+        # 主库数据源
+        master:
+          type: ${spring.datasource.type}
+          driverClassName: com.mysql.cj.jdbc.Driver
+          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
+          # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
+          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
+          username: root
+          password: root
+        # 从库数据源
+        slave:
+          lazy: true
+          type: ${spring.datasource.type}
+          driverClassName: com.mysql.cj.jdbc.Driver
+          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
+          username:
+          password:
+#        oracle:
+#          type: ${spring.datasource.type}
+#          driverClassName: oracle.jdbc.OracleDriver
+#          url: jdbc:oracle:thin:@//localhost:1521/XE
+#          username: ROOT
+#          password: root
+#          hikari:
+#            connectionTestQuery: SELECT 1 FROM DUAL
+#        postgres:
+#          type: ${spring.datasource.type}
+#          driverClassName: org.postgresql.Driver
+#          url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
+#          username: root
+#          password: root
+#        sqlserver:
+#          type: ${spring.datasource.type}
+#          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
+#          url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
+#          username: SA
+#          password: root
+      hikari:
+        # 最大连接池数量
+        maxPoolSize: 20
+        # 最小空闲线程数量
+        minIdle: 10
+        # 配置获取连接等待超时的时间
+        connectionTimeout: 30000
+        # 校验超时时间
+        validationTimeout: 5000
+        # 空闲连接存活最大时间,默认10分钟
+        idleTimeout: 600000
+        # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
+        maxLifetime: 1800000
+        # 连接测试query(配置检测连接是否有效)
+        connectionTestQuery: SELECT 1
+        # 多久检查一次连接的活性
+        keepaliveTime: 30000
+
+--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
+spring.data:
+  redis:
+    # 地址
+    host: localhost
+    # 端口,默认为6379
+    port: 6379
+    # 数据库索引
+    database: 0
+    # 密码(如没有密码请注释掉)
+    # password:
+    # 连接超时时间
+    timeout: 10s
+    # 是否开启ssl
+    ssl: false
+
+redisson:
+  # redis key前缀
+  keyPrefix:
+  # 线程池数量
+  threads: 16
+  # Netty线程池数量
+  nettyThreads: 32
+  # 单节点配置
+  singleServerConfig:
+    # 客户端名称
+    clientName: ${ruoyi.name}
+    # 最小空闲连接数
+    connectionMinimumIdleSize: 32
+    # 连接池大小
+    connectionPoolSize: 64
+    # 连接空闲超时,单位:毫秒
+    idleConnectionTimeout: 10000
+    # 命令等待超时,单位:毫秒
+    timeout: 3000
+    # 发布和订阅连接池大小
+    subscriptionConnectionPoolSize: 50
+
+--- # mail 邮件发送
+mail:
+  enabled: false
+  host: smtp.163.com
+  port: 465
+  # 是否需要用户名密码验证
+  auth: true
+  # 发送方,遵循RFC-822标准
+  from: xxx@163.com
+  # 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
+  user: xxx@163.com
+  # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助)
+  pass: xxxxxxxxxx
+  # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
+  starttlsEnable: true
+  # 使用SSL安全连接
+  sslEnable: true
+  # SMTP超时时长,单位毫秒,缺省值不超时
+  timeout: 0
+  # Socket连接超时值,单位毫秒,缺省值不超时
+  connectionTimeout: 0
+
+--- # sms 短信
+sms:
+  enabled: false
+  # 阿里云 dysmsapi.aliyuncs.com
+  # 腾讯云 sms.tencentcloudapi.com
+  endpoint: "dysmsapi.aliyuncs.com"
+  accessKeyId: xxxxxxx
+  accessKeySecret: xxxxxx
+  signName: 测试
+  # 腾讯专用
+  sdkAppId:

+ 345 - 0
ruoyi-admin/src/main/resources/application.yml

@@ -0,0 +1,345 @@
+# 项目相关配置
+ruoyi:
+  # 名称
+  name: "xmzs"
+  # 版本
+  version: ${revision}
+  # 版权年份
+  copyrightYear: 2023
+  # 实例演示开关
+  demoEnabled: true
+  # 获取ip地址开关
+  addressEnabled: false
+
+captcha:
+  enable: false
+  # 页面 <参数设置> 可开启关闭 验证码校验
+  # 验证码类型 math 数组计算 char 字符验证
+  type: MATH
+  # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
+  category: CIRCLE
+  # 数字验证码位数
+  numberLength: 1
+  # 字符验证码长度
+  charLength: 4
+
+# 开发环境配置
+server:
+  # 服务器的HTTP端口,默认为8080
+  port: 6039
+  servlet:
+    # 应用的访问路径
+    context-path: /
+  # undertow 配置
+  undertow:
+    # HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
+    max-http-post-size: -1
+    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
+    # 每块buffer的空间大小,越小的空间被利用越充分
+    buffer-size: 512
+    # 是否分配的直接内存
+    direct-buffers: true
+    threads:
+      # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
+      io: 8
+      # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
+      worker: 256
+
+# 日志配置
+logging:
+  level:
+    com.xmzs: @logging.level@
+    org.springframework: warn
+  config: classpath:logback-plus.xml
+
+# 用户配置
+user:
+  password:
+    # 密码最大错误次数
+    maxRetryCount: 5
+    # 密码锁定时间(默认10分钟)
+    lockTime: 10
+
+# Spring配置
+spring:
+  application:
+    name: ${ruoyi.name}
+  # 资源信息
+  messages:
+    # 国际化资源文件路径
+    basename: i18n/messages
+  profiles:
+    active: @profiles.active@
+  # 文件上传
+  servlet:
+    multipart:
+      # 单个文件大小
+      max-file-size: 10MB
+      # 设置总上传的文件大小
+      max-request-size: 20MB
+  mvc:
+    format:
+      date-time: yyyy-MM-dd HH:mm:ss
+  jackson:
+    # 日期格式化
+    date-format: yyyy-MM-dd HH:mm:ss
+    serialization:
+      # 格式化输出
+      indent_output: false
+      # 忽略无法转换的对象
+      fail_on_empty_beans: false
+    deserialization:
+      # 允许对象忽略json中不存在的属性
+      fail_on_unknown_properties: false
+
+# Sa-Token配置
+sa-token:
+  # token名称 (同时也是cookie名称)
+  token-name: Authorization
+  # token有效期 设为7天 (必定过期) 单位: 秒
+  timeout: 604800
+  # token临时有效期 (指定时间无操作就过期) 单位: 秒
+  activity-timeout: 604800
+  # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
+  is-concurrent: true
+  # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
+  is-share: false
+  # 是否尝试从header里读取token
+  is-read-header: true
+  # 是否尝试从cookie里读取token
+  is-read-cookie: false
+  # token前缀
+  token-prefix: "Bearer"
+  # jwt秘钥
+  jwt-secret-key: abcdefghijklmnopqrstuvwxyz
+
+# security配置
+security:
+  # 排除路径
+  excludes:
+    # 修改用户头像
+    - /system/user/edit/avatar
+    - /pay/returnUrl
+    - /pay/notifyUrl
+    # 上传文件
+    - /resource/oss/upload
+    # 重置密码
+    - /auth/reset/password
+    # 聊天接口
+    - /chat
+    # 静态资源
+    - /*.html
+    - /**/*.html
+    - /**/*.css
+    - /**/*.js
+    # 公共路径
+    - /favicon.ico
+    - /error
+    # swagger 文档配置
+    - /*/api-docs
+    - /*/api-docs/**
+    # actuator 监控配置
+    - /actuator
+    - /actuator/**
+# 多租户配置
+tenant:
+  # 是否开启
+  enable: false
+  # 排除表
+  excludes:
+    - sys_menu
+    - sys_tenant
+    - sys_tenant_package
+    - sys_role_dept
+    - sys_role_menu
+    - sys_user_post
+    - sys_user_role
+
+# MyBatisPlus配置
+# https://baomidou.com/config/
+mybatis-plus:
+  # 不支持多包, 如有需要可在注解配置 或 提升扫包等级
+  # 例如 com.**.**.mapper
+  mapperPackage: com.xmzs.**.mapper
+  # 对应的 XML 文件位置
+  mapperLocations: classpath*:mapper/**/*Mapper.xml
+  # 实体扫描,多个package用逗号或者分号分隔
+  typeAliasesPackage: com.xmzs.**.domain
+  # 启动时是否检查 MyBatis XML 文件的存在,默认不检查
+  checkConfigLocation: false
+  configuration:
+    # 自动驼峰命名规则(camel case)映射
+    mapUnderscoreToCamelCase: true
+    # MyBatis 自动映射策略
+    # NONE:不启用 PARTIAL:只对非嵌套 resultMap 自动映射 FULL:对所有 resultMap 自动映射
+    autoMappingBehavior: FULL
+    # MyBatis 自动映射时未知列或未知属性处理策
+    # NONE:不做处理 WARNING:打印相关警告 FAILING:抛出异常和详细信息
+    autoMappingUnknownColumnBehavior: NONE
+    # 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl
+    # 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl
+    # 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl
+    logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl
+  global-config:
+    # 是否打印 Logo banner
+    banner: true
+    dbConfig:
+      # 主键类型
+      # AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID
+      idType: ASSIGN_ID
+      # 逻辑已删除值
+      logicDeleteValue: 2
+      # 逻辑未删除值
+      logicNotDeleteValue: 0
+      # 字段验证策略之 insert,在 insert 的时候的字段验证策略
+      # IGNORED 忽略 NOT_NULL 非NULL NOT_EMPTY 非空 DEFAULT 默认 NEVER 不加入 SQL
+      insertStrategy: NOT_NULL
+      # 字段验证策略之 update,在 update 的时候的字段验证策略
+      updateStrategy: NOT_NULL
+      # 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
+      where-strategy: NOT_NULL
+
+# 数据加密
+mybatis-encryptor:
+  # 是否开启加密
+  enable: false
+  # 默认加密算法
+  algorithm: BASE64
+  # 编码方式 BASE64/HEX。默认BASE64
+  encode: BASE64
+  # 安全秘钥 对称算法的秘钥 如:AES,SM4
+  password:
+  # 公私钥 非对称算法的公私钥 如:SM2,RSA
+  publicKey:
+  privateKey:
+--- # mail 邮件发送
+mail:
+  enabled: true
+  host: smtp.163.com
+  port: 465
+  # 是否需要用户名密码验证
+  auth: true
+  # 发送方,遵循RFC-822标准
+  from: xxx@163.com
+  # 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
+  user: xxx@163.com
+  # 密码(填写授权码)
+  pass: pass
+  # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
+  starttlsEnable: true
+  # 使用SSL安全连接
+  sslEnable: true
+  # SMTP超时时长,单位毫秒,缺省值不超时
+  timeout: 0
+  # Socket连接超时值,单位毫秒,缺省值不超时
+  connectionTimeout: 0
+
+# Swagger配置
+swagger:
+  info:
+    # 标题
+    title: '标题:${ruoyi.name}多租户管理系统_接口文档'
+    # 描述
+    description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
+    # 版本
+    version: '版本号: ${ruoyi.version}'
+    # 作者信息
+    contact:
+      name: ageerle
+      email: ageerle@163.com
+      url: https://gitee.com/ageerle/ruoyi-ai
+  components:
+    # 鉴权方式配置
+    security-schemes:
+      apiKey:
+        type: APIKEY
+        in: HEADER
+        name: ${sa-token.token-name}
+
+springdoc:
+  api-docs:
+    # 是否开启接口文档
+    enabled: true
+  swagger-ui:
+    # 持久化认证数据
+    persistAuthorization: true
+  #这里定义了两个分组,可定义多个,也可以不定义
+  group-configs:
+    - group: 1.演示模块
+      packages-to-scan: com.xmzs.demo
+    - group: 2.通用模块
+      packages-to-scan: com.xmzs.web
+    - group: 3.系统模块
+      packages-to-scan: com.xmzs.system
+    - group: 4.代码生成模块
+      packages-to-scan: com.xmzs.generator
+
+# 防止XSS攻击
+xss:
+  # 过滤开关
+  enabled: true
+  # 排除链接(多个用逗号分隔)
+  excludes: /system/notice
+  # 匹配链接
+  urlPatterns: /system/*,/monitor/*,/tool/*
+
+# 全局线程池相关配置
+thread-pool:
+  # 是否开启线程池
+  enabled: false
+  # 队列最大长度
+  queueCapacity: 128
+  # 线程池维护线程所允许的空闲时间
+  keepAliveSeconds: 300
+
+--- # 分布式锁 lock4j 全局配置
+lock4j:
+  # 获取分布式锁超时时间,默认为 3000 毫秒
+  acquire-timeout: 3000
+  # 分布式锁的超时时间,默认为 30 秒
+  expire: 30000
+
+--- # Actuator 监控端点的配置项
+management:
+  endpoints:
+    web:
+      exposure:
+        include: '*'
+  endpoint:
+    health:
+      show-details: ALWAYS
+    logfile:
+      external-file: ./logs/sys-console.log
+
+--- # websocket
+websocket:
+  enabled: true
+  # 路径
+  path: ''
+  # 设置访问源地址
+  allowedOrigins: '*'
+# AI助手配置信息
+chat:
+  apiKey: ''
+  apiHost: ''
+# 中转接口
+transit:
+  apiKey: ''
+  apiHost: 'https://api.gptgod.online/'
+# 微信小程序配置信息
+wx:
+  miniapp:
+    configs:
+      - appid: # 你的appid
+        secret: # 你的secret
+        token: #微信小程序消息服务器配置的token
+        aesKey: #微信小程序消息服务器配置的EncodingAESKey
+        msgDataFormat: JSON
+baidu:
+  # 是否开启文本审核
+  enabled: false
+  # 文本审核
+  textReview:
+    apiKey: '' # apiKey
+    secretKey: '' # secretKey
+

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

@@ -0,0 +1,9 @@
+Application Version: ${revision}
+Spring Boot Version: ${spring-boot.version}
+                                 ██                         ██                  ██                     ██
+ ██████                         ░██                        ░██                 ░██    █████  ██████   ░██
+░██░░░██  ██████   ███████      ░██  ██████          █████ ░██       ██████   ██████ ██░░░██░██░░░██ ██████
+░██  ░██ ░░░░░░██ ░░██░░░██  ██████ ░░░░░░██  █████ ██░░░██░██████  ░░░░░░██ ░░░██░ ░██  ░██░██  ░██░░░██░
+░██████   ███████  ░██  ░██ ██░░░██  ███████ ░░░░░ ░██  ░░ ░██░░░██  ███████   ░██  ░░██████░██████   ░██
+░██░░░   ██░░░░██  ░██  ░██░██  ░██ ██░░░░██       ░██   ██░██  ░██ ██░░░░██   ░██   ░░░░░██░██░░░    ░██
+░██     ░░████████ ███  ░██░░██████░░████████      ░░█████ ░██  ░██░░████████  ░░██   █████ ░██       ░░██

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

@@ -0,0 +1,54 @@
+#错误消息
+not.null=* 必须填写
+user.jcaptcha.error=验证码错误
+user.jcaptcha.expire=验证码已失效
+user.not.exists=对不起, 您的账号:{0} 不存在.
+user.password.not.match=用户不存在/密码错误
+user.password.retry.limit.count=密码输入错误{0}次
+user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
+user.password.delete=对不起,您的账号:{0} 已被删除
+user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
+role.blocked=角色已封禁,请联系管理员
+user.logout.success=退出成功
+length.not.valid=长度必须在{min}到{max}个字符之间
+user.username.not.blank=用户名不能为空
+user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
+user.username.length.valid=账户长度必须在{min}到{max}个字符之间
+user.password.not.blank=用户密码不能为空
+user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
+user.password.not.valid=* 5-50个字符
+user.email.not.valid=邮箱格式错误
+user.email.not.blank=邮箱不能为空
+user.phonenumber.not.blank=用户手机号不能为空
+user.mobile.phone.number.not.valid=手机号格式错误
+user.login.success=登录成功
+user.register.success=注册成功
+user.register.save.error=保存用户 {0} 失败,注册账号已存在
+user.register.error=注册失败,请联系系统管理人员
+user.notfound=请重新登录
+user.forcelogout=管理员强制退出,请重新登录
+user.unknown.error=未知错误,请重新登录
+##文件上传消息
+upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
+upload.filename.exceed.length=上传的文件名最长{0}个字符
+##权限
+no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
+no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
+no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
+no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
+no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
+no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
+repeat.submit.message=不允许重复提交,请稍候再试
+rate.limiter.message=访问过于频繁,请稍候再试
+sms.code.not.blank=短信验证码不能为空
+sms.code.retry.limit.count=短信验证码输入错误{0}次
+sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
+email.code.not.blank=邮箱验证码不能为空
+email.code.retry.limit.count=邮箱验证码输入错误{0}次
+email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
+xcx.code.not.blank=小程序code不能为空
+##租户
+tenant.number.not.blank=租户编号不能为空
+tenant.not.exists=对不起, 您的租户不存在,请联系管理员
+tenant.blocked=对不起,您的租户已禁用,请联系管理员
+tenant.expired=对不起,您的租户已过期,请联系管理员

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

@@ -0,0 +1,54 @@
+#错误消息
+not.null=* Required fill in
+user.jcaptcha.error=Captcha error
+user.jcaptcha.expire=Captcha invalid
+user.not.exists=Sorry, your account: {0} does not exist
+user.password.not.match=User does not exist/Password error
+user.password.retry.limit.count=Password input error {0} times
+user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes
+user.password.delete=Sorry, your account:{0} has been deleted
+user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator
+role.blocked=Role disabled,please contact administrators
+user.logout.success=Exit successful
+length.not.valid=The length must be between {min} and {max} characters
+user.username.not.blank=Username cannot be blank
+user.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number
+user.username.length.valid=Account length must be between {min} and {max} characters
+user.password.not.blank=Password cannot be empty
+user.password.length.valid=Password length must be between {min} and {max} characters
+user.password.not.valid=* 5-50 characters
+user.email.not.valid=Mailbox format error
+user.email.not.blank=Mailbox cannot be blank
+user.phonenumber.not.blank=Phone number cannot be blank
+user.mobile.phone.number.not.valid=Phone number format error
+user.login.success=Login successful
+user.register.success=Register successful
+user.register.save.error=Failed to save user {0}, The registered account already exists
+user.register.error=Register failed, please contact system administrator
+user.notfound=Please login again
+user.forcelogout=The administrator is forced to exit,please login again
+user.unknown.error=Unknown error, please login again
+##文件上传消息
+upload.exceed.maxSize=The uploaded file size exceeds the limit file size!<br/>the maximum allowed file size is:{0}MB!
+upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters
+##权限
+no.permission=You do not have permission to the data,please contact your administrator to add permissions [{0}]
+no.create.permission=You do not have permission to create data,please contact your administrator to add permissions [{0}]
+no.update.permission=You do not have permission to modify data,please contact your administrator to add permissions [{0}]
+no.delete.permission=You do not have permission to delete data,please contact your administrator to add permissions [{0}]
+no.export.permission=You do not have permission to export data,please contact your administrator to add permissions [{0}]
+no.view.permission=You do not have permission to view data,please contact your administrator to add permissions [{0}]
+repeat.submit.message=Repeat submit is not allowed, please try again later
+rate.limiter.message=Visit too frequently, please try again later
+sms.code.not.blank=Sms code cannot be blank
+sms.code.retry.limit.count=Sms code input error {0} times
+sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes
+email.code.not.blank=Email code cannot be blank
+email.code.retry.limit.count=Email code input error {0} times
+email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes
+xcx.code.not.blank=Mini program code cannot be blank
+##租户
+tenant.number.not.blank=Tenant number cannot be blank
+tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator
+tenant.blocked=Sorry, your tenant is disabled. Please contact the administrator
+tenant.expired=Sorry, your tenant has expired. Please contact the administrator.

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

@@ -0,0 +1,54 @@
+#错误消息
+not.null=* 必须填写
+user.jcaptcha.error=验证码错误
+user.jcaptcha.expire=验证码已失效
+user.not.exists=对不起, 您的账号:{0} 不存在.
+user.password.not.match=用户不存在/密码错误
+user.password.retry.limit.count=密码输入错误{0}次
+user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
+user.password.delete=对不起,您的账号:{0} 已被删除
+user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
+role.blocked=角色已封禁,请联系管理员
+user.logout.success=退出成功
+length.not.valid=长度必须在{min}到{max}个字符之间
+user.username.not.blank=用户名不能为空
+user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
+user.username.length.valid=账户长度必须在{min}到{max}个字符之间
+user.password.not.blank=用户密码不能为空
+user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
+user.password.not.valid=* 5-50个字符
+user.email.not.valid=邮箱格式错误
+user.email.not.blank=邮箱不能为空
+user.phonenumber.not.blank=用户手机号不能为空
+user.mobile.phone.number.not.valid=手机号格式错误
+user.login.success=登录成功
+user.register.success=注册成功
+user.register.save.error=保存用户 {0} 失败,注册账号已存在
+user.register.error=注册失败,请联系系统管理人员
+user.notfound=请重新登录
+user.forcelogout=管理员强制退出,请重新登录
+user.unknown.error=未知错误,请重新登录
+##文件上传消息
+upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
+upload.filename.exceed.length=上传的文件名最长{0}个字符
+##权限
+no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
+no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
+no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
+no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
+no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
+no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
+repeat.submit.message=不允许重复提交,请稍候再试
+rate.limiter.message=访问过于频繁,请稍候再试
+sms.code.not.blank=短信验证码不能为空
+sms.code.retry.limit.count=短信验证码输入错误{0}次
+sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
+email.code.not.blank=邮箱验证码不能为空
+email.code.retry.limit.count=邮箱验证码输入错误{0}次
+email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
+xcx.code.not.blank=小程序code不能为空
+##租户
+tenant.number.not.blank=租户编号不能为空
+tenant.not.exists=对不起, 您的租户不存在,请联系管理员
+tenant.blocked=对不起,您的租户已禁用,请联系管理员
+tenant.expired=对不起,您的租户已过期,请联系管理员

BIN
ruoyi-admin/src/main/resources/ip2region.xdb


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

@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <property name="log.path" value="./logs"/>
+    <property name="console.log.pattern"
+              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
+    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
+
+    <!-- 控制台输出 -->
+    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${console.log.pattern}</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+    </appender>
+
+    <!-- 控制台输出 -->
+    <appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/sys-console.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-console.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大 1天 -->
+            <maxHistory>1</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+        </filter>
+    </appender>
+
+    <!-- 系统日志输出 -->
+    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- info异步输出 -->
+    <appender name="async_info" class="ch.qos.logback.classic.AsyncAppender">
+        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
+        <discardingThreshold>0</discardingThreshold>
+        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
+        <queueSize>512</queueSize>
+        <!-- 添加附加的appender,最多只能添加一个 -->
+        <appender-ref ref="file_info"/>
+    </appender>
+
+    <!-- error异步输出 -->
+    <appender name="async_error" class="ch.qos.logback.classic.AsyncAppender">
+        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
+        <discardingThreshold>0</discardingThreshold>
+        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
+        <queueSize>512</queueSize>
+        <!-- 添加附加的appender,最多只能添加一个 -->
+        <appender-ref ref="file_error"/>
+    </appender>
+
+    <!-- 整合 skywalking 控制台输出 tid -->
+<!--    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">-->
+<!--        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
+<!--            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
+<!--                <pattern>[%tid] ${console.log.pattern}</pattern>-->
+<!--            </layout>-->
+<!--            <charset>utf-8</charset>-->
+<!--        </encoder>-->
+<!--    </appender>-->
+
+    <!-- 整合 skywalking 推送采集日志 -->
+<!--    <appender name="sky_log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">-->
+<!--        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
+<!--            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
+<!--                <pattern>[%tid] ${console.log.pattern}</pattern>-->
+<!--            </layout>-->
+<!--            <charset>utf-8</charset>-->
+<!--        </encoder>-->
+<!--    </appender>-->
+
+    <!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="console" />
+        <appender-ref ref="async_info" />
+        <appender-ref ref="async_error" />
+        <appender-ref ref="file_console" />
+<!--        <appender-ref ref="sky_log"/>-->
+    </root>
+
+</configuration>

+ 1 - 0
ruoyi-admin/src/main/resources/spy.properties

@@ -0,0 +1 @@
+exclude=SELECT 1

BIN
ruoyi-admin/src/main/resources/static/1.jpg


+ 755 - 0
ruoyi-admin/src/main/resources/static/index.html

@@ -0,0 +1,755 @@
+<!doctype html>
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <title>开发教程分享</title>
+    <meta name="description" content="搜索">
+</head>
+<body>
+<!--header start-->
+<div class="header">
+    <div class="h_top">
+        <!--h_nav start-->
+        <div class="h_nav">
+            <ul>
+                <li><a href="#">首页</a></li>
+                <li><a href="#">课程</a></li>
+                <li><a href="#">问答</a></li>
+                <li><a href="#">专栏</a></li>
+                <li><a href="#">论坛</a></li>
+            </ul>
+        </div>
+        <!--end h_nav-->
+
+        <!--h_search start-->
+        <div class="h_search">
+            <input type="text" class="h_text">
+        </div>
+        <!--end h_search-->
+    </div>
+</div>
+<!--end header-->
+
+<!--banner start-->
+<div class="banner">
+    <img src="1.jpg" width="70%;" height="80%;" id="img1">
+</div>
+<!--end banner-->
+
+<!--menu start-->
+<div class="menu">
+    <div class="m_con">
+
+        <div class="m_list">
+            <h2>软件教程</h2>
+            <ul>
+                <li><a href="#">网页制作</a></li>
+                <li><a href="#">Java</a></li>
+                <li><a href="#">微信二次开发</a></li>
+                <li><a href="#">C++</a></li>
+                <li><a href="#">Android</a></li>
+                <li><a href="#">iOS</a></li>
+                <li><a href="#">PHP</a></li>
+                <li><a href="#">游戏开发</a></li>
+            </ul>
+        </div>
+
+        <div class="m_list">
+            <h2>技能学习</h2>
+            <ul>
+                <li><a href="#">网络营销</a></li>
+                <li><a href="#">SEO</a></li>
+                <li><a href="#">淘宝美工</a></li>
+                <li><a href="#">影楼后期</a></li>
+                <li><a href="#">淘宝运营</a></li>
+            </ul>
+        </div>
+        <div class="m_list">
+            <h2>语言学习</h2>
+            <ul>
+                <li><a href="#">韩语</a></li>
+                <li><a href="#">日语</a></li>
+                <li><a href="#">泰语</a></li>
+                <li><a href="#">西班牙语</a></li>
+            </ul>
+        </div>
+
+
+    </div>
+</div>
+<!--footer start-->
+<div class="footer">
+    <div class="f_con">
+        <div class="f_desc">
+            <dl>
+                <dt>关于我们</dt>
+                <dd><a href="#">熊猫问问</a></dd>
+                <dd><a href="#">加入我们</a></dd>
+                <dd><a href="#">联系我们</a></dd>
+            </dl>
+
+            <dl>
+                <dt>学习资讯</dt>
+                <dd><a href="#">学习课程</a></dd>
+                <dd><a href="#">就业指南</a></dd>
+            </dl>
+
+            <dl>
+                <dt>服务中心</dt>
+                <dd><a href="#">安卓APP</a></dd>
+                <dd><a href="#">IOS APP</a></dd>
+            </dl>
+        </div>
+
+        <div class="f_tel">
+            <div class="f_time">
+                <p class="f_phone">400-009-6359</p>
+                <p class="f_price">
+                    <span>周一至周六 9:30-23:00</span>
+                    <span>(仅收市话费)</span>
+                </p>
+                <p class="f_mm">24小时在线客服</p>
+            </div>
+        </div>
+        <div class="clear"></div>
+    </div>
+
+</div>
+<!--end footer-->
+
+<!--copyright start-->
+<div class="copyright">
+    Copyright &copy; 2023-2023  版权所有:xmzs 备案号:<a href="https://beian.miit.gov.cn/">鄂ICP备2023007672号</a>
+</div>
+<!--end copyright-->
+
+</body>
+
+<style>
+
+    * {
+        margin: 0;
+        padding: 0;
+    }
+
+    p{
+        text-align: center;
+    }
+
+    img{
+        margin-top: 10px;
+        margin-left: 20%;
+        margin-bottom: 10px;
+    }
+
+    body {
+        font-size: 12px;
+        font-family: "微软雅黑";
+        color: #666;
+        background: #eeeff3;
+    }
+
+    /*header start*/
+    .header {
+        width: 100%;
+        height: 70px;
+        background: #15171f;
+    }
+
+    .header .h_top {
+        width: 1180px;
+        height: 70px;
+        margin: 0 auto;
+    }
+
+    .header .h_top .h_logo {
+        width: 220px;
+        padding-top: 16px;
+        float: left;
+    }
+
+    /*h_nav start*/
+    .header .h_top .h_nav {
+        width: 650px;
+        height: 70px;
+        float: left;
+        color: #fff;
+        list-style: none;
+    }
+
+    .header .h_top .h_nav ul li {
+        list-style: none;
+        float: left;
+    }
+
+    .header .h_top .h_nav ul li a {
+        text-decoration: none;
+        display: block;
+        line-height: 70px;
+        padding: 0 20px 0 20px;
+        color: #fff;
+        font-size: 14px;
+        margin: 0 5px 0 5px;
+    }
+
+    .header .h_top .h_nav ul li a:hover {
+        background: #323744;
+    }
+
+    /*end h_nav*/
+
+    /*h_search start*/
+    .header .h_top .h_search {
+        width: 160px;
+        height: 32px;
+        border: 1px solid #323744;
+        background: #323744;
+        float: left;
+        margin-top: 18px;
+    }
+
+    .header .h_top .h_search:hover {
+        border: 1px solid #818997;
+    }
+
+    .header .h_top .h_search .h_text {
+        width: 125px;
+        height: 32px;
+        border: 0;
+        background: #323744;
+        font-size: 14px;
+        font-family: "微软雅黑";
+        color: #fff;
+        line-height: 32px;
+        padding-left: 10px;
+        float: left;
+    }
+
+    .header .h_top .h_search .h_btn {
+        width: 20px;
+        height: 20px;
+        display: block;
+        float: left;
+        margin-top: 5px;
+        background: url("picture/pe_icon.png") no-repeat -368px 0;
+    }
+
+    /*end h_search*/
+
+    /*h_message start*/
+    .header .h_top .h_message {
+        width: 112px;
+        height: 70px;
+        float: left;
+        margin-left: 34px;
+    }
+
+    .header .h_top .h_message .h_info {
+        width: 52px;
+        height: 70px;
+        float: left;
+        position: relative;
+    }
+
+    .header .h_top .h_message .h_info:hover {
+        background: #323744;
+    }
+
+    .header .h_top .h_message .h_info a {
+        width: 22px;
+        height: 16px;
+        display: block;
+        background: url("picture/pe_icon.png") no-repeat -368px -23px;
+        margin: 26px auto;
+    }
+
+    .header .h_top .h_message .h_info i {
+        width: 7px;
+        height: 7px;
+        display: block;
+        background: #eb6b83;
+        border-radius: 4px;
+        border: 1px solid #FFF;
+        position: absolute;
+        top: 17px;
+        left: 34px;
+    }
+
+    .header .h_top .h_message .h_pic a {
+        display: block;
+        width: 30px;
+        height: 30px;
+        margin: 20px auto;
+    }
+
+    .header .h_top .h_message .h_pic a img {
+        border-radius: 15px;
+    }
+
+    /*end h_message*/
+
+    /*banner start*/
+    .banner {
+        width: 100%;
+        height:60%;
+    }
+
+    /*end banner*/
+
+    /*menu start*/
+    .menu {
+        width: 100%;
+        height: 110px;
+        border-bottom: 1px solid #d9dce1;
+        background: #fff;
+    }
+
+    .menu .m_con {
+        width: 1180px;
+        height: 110px;
+        margin: 0 auto;
+    }
+
+    .menu .m_con .m_list {
+        width: 215px;
+        height: 80px;
+        border-right: 1px solid #d9dce1;
+        margin-top: 12px;
+        margin-left: 20px;
+        float: left;
+    }
+
+    .menu .m_con .m_list h2 {
+        font-size: 18px;
+        color: #5580fb;
+        font-weight: 500;
+        line-height: 36px;
+    }
+
+    .menu .m_con .m_list ul li {
+        list-style: none;
+        float: left;
+        margin-right: 10px;
+        line-height: 22px;
+    }
+
+    .menu .m_con .m_list ul li a {
+        color: #333;
+        text-decoration: none;
+    }
+
+    .menu .m_con .m_list ul li a:hover {
+        color: #5580fb;
+    }
+
+    .menu .m_con .m_list_end {
+        border: 0;
+    }
+
+    .course_list .c_course {
+        width: 222px;
+        height: 185px;
+        float: left;
+        margin: 0 7px;
+        margin-bottom: 20px;
+    }
+
+    .course_list .c_course .c_con {
+        width: 222px;
+        height: 185px;
+        position: relative;
+    }
+
+    /*c_first start*/
+    .course_list .c_course .c_first {
+        background: #5580fb;
+        text-align: center;
+        color: #fff;
+        font-size: 14px;
+    }
+
+    .course_list .c_course .c_first h2 {
+        padding-top: 50px;
+        font-weight: 500;
+        font-size: 24px;
+    }
+
+    .course_list .c_course .c_first p {
+        line-height: 36px;
+    }
+
+    .course_list .c_course .c_first a {
+        text-decoration: none;
+        color: #fff;
+    }
+
+    /*end c_first*/
+
+    /*c_list start*/
+    .course_list .c_course .c_list .c_yy {
+        width: 222px;
+        height: 145px;
+        background: #000;
+        position: absolute;
+        top: 0;
+        left: 0;
+        opacity: 0.6;
+        filter: alpha(opacity=60);
+        display: none;
+    }
+
+    .course_list .c_course .c_list .c_desc {
+        width: 222px;
+        height: 145px;
+        position: absolute;
+        top: 0;
+        left: 0;
+        color: #999;
+        display: none;
+    }
+
+    .course_list .c_course .c_list:hover .c_desc {
+        display: block;
+    }
+
+    .course_list .c_course .c_list:hover .c_yy {
+        display: block;
+    }
+
+    .course_list .c_course .c_list .c_desc p {
+        padding-left: 15px;
+        line-height: 24px;
+    }
+
+    .course_list .c_course .c_list .c_desc .c_title {
+        color: #fff;
+        font-size: 14px;
+        padding-top: 15px;
+    }
+
+    .course_list .c_course .c_list .c_tit_main {
+        text-align: center;
+        display: block;
+        line-height: 40px;
+        text-decoration: none;
+        color: #333;
+        font-size: 14px;
+    }
+
+    .course_list .c_course .c_list .c_tit_main:hover {
+        color: #5580fb;
+    }
+
+    .course_list .c_course .c_list .c_desc .c_btn {
+        width: 80px;
+        height: 30px;
+        background: #5580fb;
+        display: block;
+        text-decoration: none;
+        text-align: center;
+        line-height: 30px;
+        margin: 10px auto;
+        color: #fff;
+        font-size: 14px;
+        border-radius: 2px;
+    }
+
+    .clear {
+        clear: both;
+    }
+
+    .course_list .c_course .c_zhiye {
+        background: #7784a9;
+    }
+
+    .course_list .c_course .c_yuyan {
+        background: #3faa77;
+    }
+
+    .course_list .c_course .c_student {
+        background: #6a9a36;
+    }
+
+    /*end c_list*/
+
+    .teams {
+        width: 100%;
+        background: #fff;
+        padding-top: 20px;
+    }
+
+    /*teacher start*/
+    .teacher {
+        width: 1180px;
+        margin: 0 auto;
+        border-bottom: 1px solid #d9dce1;
+    }
+
+    .teacher h2 {
+        font-size: 24px;
+        font-weight: 500;
+    }
+
+    .teacher .t_team {
+        width: 1180px;
+    }
+
+    .teacher .t_team ul li {
+        list-style: none;
+        float: left;
+        width: 216px;
+        height: 66px;
+        margin: 30px 9px;
+    }
+
+    .teacher .t_team ul li:hover {
+        background: #EFEFEF;
+    }
+
+    .teacher .t_team ul li .t_pic {
+        width: 66px;
+        float: left;
+    }
+
+    .teacher .t_team ul li .t_desc {
+        width: 130px;
+        height: 66px;
+        float: right;
+    }
+
+    .teacher .t_team ul li .t_desc p {
+        line-height: 26px;
+    }
+
+    .teacher .t_team ul li .t_desc a {
+        font-size: 14px;
+        color: #5580fb;
+        text-decoration: none;
+    }
+
+    .student h2 {
+        font-weight: 500;
+        font-size: 24px;
+        line-height: 70px;
+    }
+
+    .student .s_box ul li {
+        list-style: none;
+        float: left;
+        width: 216px;
+        height: 280px;
+        margin-right: 22px;
+        position: relative;
+    }
+
+    .student .s_box ul .s_en_li {
+        margin-right: 0;
+    }
+
+    .student .s_box ul li .s_yy {
+        width: 216px;
+        height: 280px;
+        background: #000;
+        position: absolute;
+        top: 0;
+        left: 0;
+        opacity: 0.6;
+        filter: alpha(opacity=60);
+        display: none;
+    }
+
+    .student .s_box ul li .s_desc {
+        width: 216px;
+        height: 280px;
+        position: absolute;
+        top: 0;
+        left: 0;
+        color: #fff;
+        display: none;
+    }
+
+    .student .s_box ul li .s_desc h3 {
+        padding: 15px 0 0 20px;
+        font-weight: 500;
+        font-size: 18px;
+    }
+
+    .student .s_box ul li .s_desc p {
+        padding: 20px;
+        line-height: 26px;
+    }
+
+    .student .s_box ul li .s_desc a {
+        width: 60px;
+        height: 60px;
+        display: block;
+        border-radius: 30px;
+        background: #5580fb;
+        margin: 0 auto;
+        line-height: 60px;
+        text-align: center;
+        font-size: 16px;
+        text-decoration: none;
+        color: #fff;
+    }
+
+    .student .s_box ul li:hover .s_yy {
+        display: block;
+    }
+
+    .student .s_box ul li:hover .s_desc {
+        display: block;
+    }
+
+    .links h2 {
+        font-size: 24px;
+        font-weight: 500;
+        line-height: 70px;
+    }
+
+    .links .l_a {
+        width: 1180px;
+        color: #8e949f;
+    }
+
+    .links .l_a a {
+        padding: 0 10px;
+        line-height: 30px;
+        text-decoration: none;
+        color: #8e949f;
+        font-size: 14px;
+        display: inline-block;
+    }
+
+    .links .l_a a:hover {
+        color: #5580fb;
+    }
+
+    /*end links*/
+
+    /*footer start*/
+    .footer {
+        width: 100%;
+        background: #15171f;
+    }
+
+    .footer .f_con {
+        width: 1180px;
+        margin: 0px auto;
+        color: #51555d;
+        padding: 40px 0 40px 0;
+    }
+
+    .footer .f_con .f_desc dl {
+        float: left;
+        margin-right: 60px;
+    }
+
+    .footer .f_con .f_desc dl dt {
+        font-size: 18px;
+        margin-bottom: 10px;
+    }
+
+    .footer .f_con .f_desc dl dd {
+        line-height: 34px;
+    }
+
+    .footer .f_con .f_desc dl dd a {
+        text-decoration: none;
+        color: #51555d;
+        font-size: 14px;
+    }
+
+    .footer .f_con .f_desc dl dd a:hover {
+        color: #fff;
+    }
+
+    .footer .f_con .f_desc dl dd .share {
+        width: 35px;
+        height: 34px;
+        display: block;
+        float: left;
+        background: url("picture/pe_icon.png") no-repeat;
+        margin-right: 5px;
+    }
+
+    .footer .f_con .f_desc dl dd .f_wb {
+        background-position: -227px -77px;
+    }
+
+    .footer .f_con .f_desc dl dd .f_wb:hover {
+        background-position: -227px -113px;
+    }
+
+    .footer .f_con .f_desc dl dd .f_qq {
+        background-position: -265px -77px;
+    }
+
+    .footer .f_con .f_desc dl dd .f_qq:hover {
+        background-position: -265px -113px;
+    }
+
+    .footer .f_con .f_tel {
+        width: 365px;
+        float: right;
+    }
+
+    .footer .f_con .f_tel .f_qrcode {
+        width: 153px;
+        height: 135px;
+        border-right: 1px solid #202328;
+        float: left;
+    }
+
+    .footer .f_con .f_tel .f_time {
+        float: left;
+        padding-left: 51px;
+    }
+
+    .footer .f_con .f_tel .f_time .f_price {
+        margin: 10px auto;
+    }
+
+    .footer .f_con .f_tel .f_time .f_price span {
+        display: block;
+    }
+
+    .footer .f_con .f_tel .f_time .f_phone {
+        font-size: 24px;
+        color: #fff;
+        margin-bottom: 10px;
+    }
+
+    .footer .f_con .f_tel .f_time .f_price span {
+        line-height: 24px;
+        font-size: 14px;
+    }
+
+    .footer .f_con .f_tel .f_time .f_mm {
+        width: 152px;
+        height: 39px;
+        text-align: center;
+        border: 1px solid #292c35;
+        line-height: 39px;
+        font-size: 14px;
+        border-radius: 19px;
+    }
+
+    /*end footer*/
+
+    /*copyright start*/
+    .copyright {
+        width: 100%;
+        height: 70px;
+        background: #191b24;
+        line-height: 54px;
+        text-align: center;
+        font-size: 18px;
+    }
+
+    /*end copyright*/
+
+</style>
+
+</html>
+

+ 45 - 0
ruoyi-admin/src/test/java/com/xmzs/test/AssertUnitTest.java

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

+ 70 - 0
ruoyi-admin/src/test/java/com/xmzs/test/DemoUnitTest.java

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

+ 72 - 0
ruoyi-admin/src/test/java/com/xmzs/test/ParamUnitTest.java

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

+ 54 - 0
ruoyi-admin/src/test/java/com/xmzs/test/TagUnitTest.java

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

+ 46 - 0
ruoyi-common/pom.xml

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>ruoyi-ai</artifactId>
+        <groupId>com.xmzs</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <modules>
+        <module>ruoyi-common-bom</module>
+        <module>ruoyi-common-core</module>
+        <module>ruoyi-common-doc</module>
+        <module>ruoyi-common-excel</module>
+        <module>ruoyi-common-idempotent</module>
+        <module>ruoyi-common-job</module>
+        <module>ruoyi-common-log</module>
+        <module>ruoyi-common-mail</module>
+        <module>ruoyi-common-mybatis</module>
+        <module>ruoyi-common-oss</module>
+        <module>ruoyi-common-ratelimiter</module>
+        <module>ruoyi-common-redis</module>
+        <module>ruoyi-common-satoken</module>
+        <module>ruoyi-common-security</module>
+        <module>ruoyi-common-sms</module>
+        <module>ruoyi-common-web</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-chat</module>
+        <module>ruoyi-common-pay</module>
+    </modules>
+
+    <artifactId>ruoyi-common</artifactId>
+    <packaging>pom</packaging>
+
+    <description>
+        common 通用模块
+    </description>
+
+</project>

+ 178 - 0
ruoyi-common/ruoyi-common-bom/pom.xml

@@ -0,0 +1,178 @@
+<?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>com.xmzs</groupId>
+    <artifactId>ruoyi-common-bom</artifactId>
+    <version>${revision}</version>
+    <packaging>pom</packaging>
+
+    <description>
+        ruoyi-common-bom common依赖项
+    </description>
+
+    <properties>
+        <revision>1.0.0</revision>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- 核心模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-core</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 接口模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-doc</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- excel -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-excel</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 幂等 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-idempotent</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 调度模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-job</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 日志记录 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-log</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 邮件服务 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-mail</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 数据库服务 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-mybatis</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- OSS -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-oss</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 限流 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-ratelimiter</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 缓存服务 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-redis</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- satoken -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-satoken</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 安全模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-security</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 短信模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-sms</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- web服务 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-web</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 翻译模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-translation</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 脱敏模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-sensitive</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 序列化模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-json</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 数据库加解密模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-encrypt</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 租户模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-tenant</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- chat模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-chat</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 支付模块 -->
+            <dependency>
+                <groupId>com.xmzs</groupId>
+                <artifactId>ruoyi-common-pay</artifactId>
+                <version>${revision}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+</project>

+ 86 - 0
ruoyi-common/ruoyi-common-chat/pom.xml

@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.xmzs</groupId>
+        <artifactId>ruoyi-common</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ruoyi-common-chat</artifactId>
+
+    <description>
+        ruoyi-common-chat 模块
+    </description>
+
+    <properties>
+        <retrofit2.version>2.9.0</retrofit2.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.xmzs</groupId>
+            <artifactId>ruoyi-common-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.xmzs</groupId>
+            <artifactId>ruoyi-common-json</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.xmzs</groupId>
+            <artifactId>ruoyi-common-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.xmzs</groupId>
+            <artifactId>ruoyi-common-satoken</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.xmzs</groupId>
+            <artifactId>ruoyi-common-json</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.squareup.retrofit2</groupId>
+            <artifactId>retrofit</artifactId>
+            <version>${retrofit2.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.squareup.retrofit2</groupId>
+            <artifactId>converter-jackson</artifactId>
+            <version>${retrofit2.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.squareup.retrofit2</groupId>
+            <artifactId>adapter-rxjava2</artifactId>
+            <version>${retrofit2.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.knuddels</groupId>
+            <artifactId>jtokkit</artifactId>
+            <version>0.5.0</version>
+        </dependency>
+
+        <!--   azure-ai    -->
+        <dependency>
+            <groupId>com.azure</groupId>
+            <artifactId>azure-ai-openai</artifactId>
+            <version>1.0.0-beta.6</version>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.12</version>
+        </dependency>
+    </dependencies>
+</project>

+ 49 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/ChatConfig.java

@@ -0,0 +1,49 @@
+package com.xmzs.common.chat.config;
+
+
+import okhttp3.OkHttpClient;
+import okhttp3.logging.HttpLoggingInterceptor;
+import com.xmzs.common.chat.openai.OpenAiStreamClient;
+import com.xmzs.common.chat.openai.function.KeyRandomStrategy;
+import com.xmzs.common.chat.openai.interceptor.OpenAILogger;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * chat配置类
+ *
+ * @author: wangle
+ * @date: 2023/5/16
+ */
+@Configuration
+public class ChatConfig {
+    @Value("${chat.apiKey}")
+    private List<String> apiKey;
+    @Value("${chat.apiHost}")
+    private String apiHost;
+
+    @Bean(name = "openAiStreamClient")
+    public OpenAiStreamClient openAiStreamClient() {
+        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger());
+        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
+        OkHttpClient okHttpClient = new OkHttpClient
+            .Builder()
+            .addInterceptor(httpLoggingInterceptor)
+            .connectTimeout(30, TimeUnit.SECONDS)
+            .writeTimeout(600, TimeUnit.SECONDS)
+            .readTimeout(600, TimeUnit.SECONDS)
+            .build();
+        return OpenAiStreamClient
+            .builder()
+            .apiHost(apiHost)
+            .apiKey(apiKey)
+            //自定义key使用策略 默认随机策略
+            .keyStrategy(new KeyRandomStrategy())
+            .okHttpClient(okHttpClient)
+            .build();
+    }
+}

+ 36 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/LocalCache.java

@@ -0,0 +1,36 @@
+package com.xmzs.common.chat.config;
+
+import cn.hutool.cache.CacheUtil;
+import cn.hutool.cache.impl.TimedCache;
+import cn.hutool.core.date.DateUnit;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @date 2023-03-10
+ */
+@Slf4j
+public class LocalCache {
+    /**
+     * 缓存时长
+     */
+    public static final long TIMEOUT = 30 * DateUnit.MINUTE.getMillis();
+    /**
+     * 清理间隔
+     */
+    private static final long CLEAN_TIMEOUT = 30 * DateUnit.MINUTE.getMillis();
+
+    /**
+     * 缓存对象
+     */
+    public static final TimedCache<String, Object> CACHE = CacheUtil.newTimedCache(TIMEOUT);
+
+
+    static {
+        //启动定时任务
+        CACHE.schedulePrune(CLEAN_TIMEOUT);
+    }
+
+}

+ 61 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/WebSocketConfig.java

@@ -0,0 +1,61 @@
+package com.xmzs.common.chat.config;
+
+import cn.hutool.core.util.StrUtil;
+import com.xmzs.common.chat.config.properties.WebSocketProperties;
+import com.xmzs.common.chat.handler.PlusWebSocketHandler;
+import com.xmzs.common.chat.interceptor.PlusWebSocketInterceptor;
+import com.xmzs.common.chat.listener.WebSocketTopicListener;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+/**
+ * WebSocket 配置
+ *
+ * @author zendwang
+ */
+@AutoConfiguration
+@ConditionalOnProperty(value = "websocket.enabled", havingValue = "true")
+@EnableConfigurationProperties(WebSocketProperties.class)
+@EnableWebSocket
+public class WebSocketConfig {
+
+    @Bean
+    public WebSocketConfigurer webSocketConfigurer(HandshakeInterceptor handshakeInterceptor,
+                                                   WebSocketHandler webSocketHandler,
+                                                   WebSocketProperties webSocketProperties) {
+        if (StrUtil.isBlank(webSocketProperties.getPath())) {
+            webSocketProperties.setPath("/websocket");
+        }
+
+        if (StrUtil.isBlank(webSocketProperties.getAllowedOrigins())) {
+            webSocketProperties.setAllowedOrigins("*");
+        }
+
+        return registry -> registry
+            .addHandler(webSocketHandler, webSocketProperties.getPath())
+            .addInterceptors(handshakeInterceptor)
+            .setAllowedOrigins(webSocketProperties.getAllowedOrigins());
+    }
+
+    @Bean
+    public HandshakeInterceptor handshakeInterceptor() {
+        return new PlusWebSocketInterceptor();
+    }
+
+    @Bean
+    public WebSocketHandler webSocketHandler() {
+        return new PlusWebSocketHandler();
+    }
+
+    @Bean
+    public WebSocketTopicListener topicListener() {
+        return new WebSocketTopicListener();
+    }
+}

+ 26 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/config/properties/WebSocketProperties.java

@@ -0,0 +1,26 @@
+package com.xmzs.common.chat.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * WebSocket 配置项
+ *
+ * @author zendwang
+ */
+@ConfigurationProperties("websocket")
+@Data
+public class WebSocketProperties {
+
+    private Boolean enabled;
+
+    /**
+     * 路径
+     */
+    private String path;
+
+    /**
+     *  设置访问源地址
+     */
+    private String allowedOrigins;
+}

+ 31 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/constant/OpenAIConst.java

@@ -0,0 +1,31 @@
+package com.xmzs.common.chat.constant;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since  2023-03-06
+ */
+public class OpenAIConst {
+
+    public final static String OPENAI_HOST = "https://api.openai.com/";
+
+    public final static int SUCCEED_CODE = 200;
+
+    public final static double GPT3_COST = 0.03;
+
+    public final static double GPT4_COST = 0.3;
+
+    /** 绘图费用 */
+    public final static double DALL3_COST = 0.3;
+
+    /** 绘图费用-高清 */
+    public final static double DALL3_HD_COST = 0.6;
+
+    /** mdjourney绘图费用 */
+    public final static double MJ_COST = 0.3;
+
+    /** 默认账户余额 */
+    public final static double USER_BALANCE = 5;
+
+}

+ 28 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/constant/WebSocketConstants.java

@@ -0,0 +1,28 @@
+package com.xmzs.common.chat.constant;
+
+/**
+ * websocket的常量配置
+ *
+ * @author zendwang
+ */
+public interface WebSocketConstants {
+    /**
+     * websocketSession中的参数的key
+     */
+    String LOGIN_USER_KEY = "loginUser";
+
+    /**
+     * 订阅的频道
+     */
+    String WEB_SOCKET_TOPIC = "global:websocket";
+
+    /**
+     * 前端心跳检查的命令
+     */
+    String PING = "ping";
+
+    /**
+     * 服务端心跳恢复的字符串
+     */
+    String PONG = "pong";
+}

+ 31 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/ChatProcessRequest.java

@@ -0,0 +1,31 @@
+package com.xmzs.common.chat.domain.request;
+
+
+import lombok.Data;
+
+/**
+ * @author hncboy
+ * @date 2023/3/23 13:17
+ * 消息处理请求
+ */
+@Data
+public class ChatProcessRequest {
+
+    private String prompt;
+
+    private Options options;
+
+    private String systemMessage;
+
+    @Data
+    public static class Options {
+
+        private String conversationId;
+
+        /**
+         * 这里的父级消息指的是回答的父级消息 id
+         * 前端发送问题,需要上下文的话传回答的父级消息 id
+         */
+        private String parentMessageId;
+    }
+}

+ 55 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/ChatRequest.java

@@ -0,0 +1,55 @@
+package com.xmzs.common.chat.domain.request;
+
+import com.xmzs.common.chat.entity.chat.Content;
+import com.xmzs.common.chat.entity.chat.Message;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @sine 2023-04-08
+ */
+@Data
+public class ChatRequest {
+
+    @NotEmpty(message = "传入的模型不能为空")
+    private String model;
+
+    @NotEmpty(message = "对话消息不能为空")
+    List<Message> messages;
+
+    List<Content> content;
+
+    private String prompt;
+
+    private String userId;
+
+    /**
+     * 需要识别的图片地址
+     */
+    private String imgurl;
+
+    /**
+     * gpt的默认设置
+     */
+    private String systemMessage = "";
+
+    private double top_p = 1;
+
+    private double temperature = 0.2;
+
+    /**
+     * 上下文的条数
+     */
+    private Integer contentNumber = 10;
+
+    /**
+     * 是否携带上下文
+     */
+    private Boolean usingContext = Boolean.TRUE;
+
+}

+ 33 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/Dall3Request.java

@@ -0,0 +1,33 @@
+package com.xmzs.common.chat.domain.request;
+
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @sine 2023-04-08
+ */
+@Data
+public class Dall3Request {
+
+    @NotEmpty(message = "传入的模型不能为空")
+    private String model;
+
+    @NotEmpty(message = "提示词不能为空")
+    private String prompt;
+
+    /** 图片大小 */
+    @NotEmpty(message = "图片大小不能为空")
+    private String size ;
+
+    /** 图片质量 */
+    @NotEmpty(message = "图片质量不能为空")
+    private String quality;
+
+    /** 图片风格 */
+    @NotEmpty(message = "图片风格不能为空")
+    private String style;
+
+}

+ 21 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/request/SaveMsgRequest.java

@@ -0,0 +1,21 @@
+package com.xmzs.common.chat.domain.request;
+
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @sine 2023-04-08
+ */
+@Data
+public class SaveMsgRequest {
+
+    @NotEmpty(message = "传入的模型不能为空")
+    private String model;
+
+    @NotEmpty(message = "对话消息不能为空")
+    private String msg;
+
+}

+ 19 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/domain/response/ChatResponse.java

@@ -0,0 +1,19 @@
+package com.xmzs.common.chat.domain.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @sine 2023-04-08
+ */
+@Data
+public class ChatResponse {
+    /**
+     * 问题消耗tokens
+     */
+    @JsonProperty("question_tokens")
+    private long questionTokens = 0;
+}

+ 33 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/BillingUsage.java

@@ -0,0 +1,33 @@
+package com.xmzs.common.chat.entity.billing;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 描述:金额消耗信息
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-04-08
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BillingUsage {
+
+    @JsonProperty("object")
+    private String object;
+    /**
+     * 账号金额消耗明细
+     */
+    @JsonProperty("daily_costs")
+    private List<DailyCost> dailyCosts;
+    /**
+     * 总使用金额:美分
+     */
+    @JsonProperty("total_usage")
+    private BigDecimal totalUsage;
+
+}

+ 39 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/CreditGrantsResponse.java

@@ -0,0 +1,39 @@
+package com.xmzs.common.chat.entity.billing;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 描述:余额查询接口返回值
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-03-18
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class CreditGrantsResponse implements Serializable {
+    private String object;
+    /**
+     * 总金额:美元
+     */
+    @JsonProperty("total_granted")
+    private BigDecimal totalGranted;
+    /**
+     * 总使用金额:美元
+     */
+    @JsonProperty("total_used")
+    private BigDecimal totalUsed;
+    /**
+     * 总剩余金额:美元
+     */
+    @JsonProperty("total_available")
+    private BigDecimal totalAvailable;
+    /**
+     * 余额明细
+     */
+    private Grants grants;
+}

+ 28 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/DailyCost.java

@@ -0,0 +1,28 @@
+package com.xmzs.common.chat.entity.billing;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 描述:金额消耗列表
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-04-08
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DailyCost {
+    /**
+     * 时间戳
+     */
+    @JsonProperty("timestamp")
+    private long timestamp;
+    /**
+     * 模型消耗金额详情
+     */
+    @JsonProperty("line_items")
+    private List<LineItem> lineItems;
+}

+ 40 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Datum.java

@@ -0,0 +1,40 @@
+package com.xmzs.common.chat.entity.billing;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-03-18
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Datum {
+    private String object;
+    private String id;
+    /**
+     * 赠送金额:美元
+     */
+    @JsonProperty("grant_amount")
+    private BigDecimal grantAmount;
+    /**
+     * 使用金额:美元
+     */
+    @JsonProperty("used_amount")
+    private BigDecimal usedAmount;
+    /**
+     * 生效时间戳
+     */
+    @JsonProperty("effective_at")
+    private Long effectiveAt;
+    /**
+     * 过期时间戳
+     */
+    @JsonProperty("expires_at")
+    private Long expiresAt;
+}

+ 21 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Grants.java

@@ -0,0 +1,21 @@
+package com.xmzs.common.chat.entity.billing;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-03-18
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Grants {
+    private String object;
+    @JsonProperty("data")
+    private List<Datum> data;
+}

+ 56 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/KeyInfo.java

@@ -0,0 +1,56 @@
+package com.xmzs.common.chat.entity.billing;
+
+import lombok.*;
+
+import java.time.LocalDate;
+
+/**
+ * openKey信息
+ *
+ * @author admin
+ * @date 2023/6/15
+ */
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+@ToString
+public class KeyInfo {
+    /**
+     * 订阅类型
+     */
+    private String planTitle;
+    /**
+     * key值
+     */
+    private String keyValue;
+    /**
+     * 剩余额度
+     */
+    private Double remaining;
+
+    /**
+     * 账户总余额
+     */
+    private Double totalAmount;
+
+    /**
+     * 已使用的额度
+     */
+    private Double totalUsage;
+
+    /**
+     * 截至日期
+     */
+    private LocalDate limitDate;
+
+    /**
+     * 是否绑卡
+     */
+    private Boolean isHasPaymentMethod;
+
+    /**
+     * 最高可用模型
+     */
+    private String model;
+}

+ 25 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/LineItem.java

@@ -0,0 +1,25 @@
+package com.xmzs.common.chat.entity.billing;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 描述:金额消耗列表
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-04-08
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class LineItem {
+    /**
+     * 模型名称
+     */
+    private String name;
+    /**
+     * 消耗金额
+     */
+    private BigDecimal cost;
+}

+ 17 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Plan.java

@@ -0,0 +1,17 @@
+package com.xmzs.common.chat.entity.billing;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since  2023-04-08
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Plan {
+    private String title;
+    private String id;
+}

+ 73 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/billing/Subscription.java

@@ -0,0 +1,73 @@
+package com.xmzs.common.chat.entity.billing;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 描述:账户信息
+ *
+ * @author https:www.unfbx.com
+ * @since  2023-04-08
+ */
+@Data
+public class Subscription {
+
+    @JsonProperty("object")
+    private String object;
+
+    /**
+     * 付款方式
+     */
+    @JsonProperty("has_payment_method")
+    private boolean hasPaymentMethod;
+
+    @JsonProperty("canceled")
+    private boolean canceled;
+    @JsonProperty("canceled_at")
+    private Object canceledAt;
+
+    @JsonProperty("delinquent")
+    private Object delinquent;
+    @JsonProperty("access_until")
+    private long accessUntil;
+    @JsonProperty("soft_limit")
+    private long softLimit;
+    @JsonProperty("hard_limit")
+    private long hardLimit;
+    @JsonProperty("system_hard_limit")
+    private long systemHardLimit;
+    @JsonProperty("soft_limit_usd")
+    private double softLimitUsd;
+    @JsonProperty("hard_limit_usd")
+    private double hardLimitUsd;
+    @JsonProperty("system_hard_limit_usd")
+    private double systemHardLimitUsd;
+    /**
+     * 计划
+     */
+    @JsonProperty("plan")
+    private Plan plan;
+
+    /**
+     * 账户名称
+     */
+    @JsonProperty("account_name")
+    private String accountName;
+
+    @JsonProperty("po_number")
+    private Object poNumber;
+
+    /**
+     * 账单邮箱
+     */
+    @JsonProperty("billing_email")
+    private Object billingEmail;
+    @JsonProperty("tax_ids")
+    private Object taxIds;
+    @JsonProperty("billing_address")
+    private Object billingAddress;
+    @JsonProperty("business_address")
+    private Object businessAddress;
+    @JsonProperty("primary")
+    private Boolean primary;
+}

+ 238 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/BaseChatCompletion.java

@@ -0,0 +1,238 @@
+package com.xmzs.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import com.xmzs.common.chat.constant.OpenAIConst;
+import com.xmzs.common.chat.entity.chat.tool.Tools;
+import lombok.*;
+import lombok.experimental.SuperBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import static com.xmzs.common.chat.entity.chat.BaseChatCompletion.Model.GPT_3_5_TURBO;
+
+/**
+ * 描述: chat模型基础类
+ *
+ * @author https:www.unfbx.com
+ * @since 1.1.2
+ * 2023-11-10
+ */
+@Data
+@SuperBuilder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class BaseChatCompletion implements Serializable {
+
+    @NonNull
+    @Builder.Default
+    private String model = GPT_3_5_TURBO.getName();
+
+    /**
+     * 指定模型必须输出的格式的对象。
+     *
+     * @since 1.1.2
+     */
+    @JsonProperty("response_format")
+    private ResponseFormat responseFormat;
+
+    /**
+     * 已过时
+     *
+     * @see #tools
+     */
+    @Deprecated
+    private List<Functions> functions;
+
+    /**
+     * 取值:null,auto或者自定义
+     * functions没有值的时候默认为:null
+     * functions存在值得时候默认为:auto
+     * 也可以自定义
+     * <p>已过时</p>
+     *
+     * @see #toolChoice
+     */
+    @Deprecated
+    @JsonProperty("function_call")
+    private Object functionCall;
+
+    /**
+     * 模型可能调用的工具列表。
+     * 当前版本仅支持:functions
+     *
+     * @since 1.1.2
+     */
+    private List<Tools> tools;
+
+    /**
+     * 取值:String或者ToolChoiceObj
+     *
+     * @since 1.1.2
+     */
+    @JsonProperty("tool_choice")
+    private Object toolChoice;
+
+    /**
+     * 使用什么取样温度,0到2之间。较高的值(如0.8)将使输出更加随机,而较低的值(如0.2)将使输出更加集中和确定。
+     * <p>
+     * We generally recommend altering this or but not both.top_p
+     */
+    @Builder.Default
+    private double temperature = 0.2;
+
+    /**
+     * 使用温度采样的替代方法称为核心采样,其中模型考虑具有top_p概率质量的令牌的结果。因此,0.1 意味着只考虑包含前 10% 概率质量的代币。
+     * <p>
+     * 我们通常建议更改此设置,但不要同时更改两者。temperature
+     */
+    @JsonProperty("top_p")
+    @Builder.Default
+    private Double topP = 1d;
+
+
+    /**
+     * 为每个提示生成的完成次数。
+     */
+    @Builder.Default
+    private Integer n = 1;
+
+
+    /**
+     * 是否流式输出.
+     * default:false
+     */
+    @Builder.Default
+    private boolean stream = false;
+    /**
+     * 停止输出标识
+     */
+    private List<String> stop;
+    /**
+     * 最大支持4096
+     */
+    @JsonProperty("max_tokens")
+    @Builder.Default
+    private Integer maxTokens = 2048;
+
+
+    @JsonProperty("presence_penalty")
+    @Builder.Default
+    private double presencePenalty = 0;
+
+    /**
+     * -2.0 ~~ 2.0
+     */
+    @JsonProperty("frequency_penalty")
+    @Builder.Default
+    private double frequencyPenalty = 0;
+
+    @JsonProperty("logit_bias")
+    private Map logitBias;
+    /**
+     * 用户唯一值,确保接口不被重复调用
+     */
+    private String user;
+
+    /**
+     * @since 1.1.2
+     */
+    private Integer seed;
+
+
+    /**
+     * 最新模型参考官方文档:
+     * <a href="https://platform.openai.com/docs/models/model-endpoint-compatibility">官方稳定模型列表</a>
+     */
+    @Getter
+    @AllArgsConstructor
+    public enum Model {
+        /**
+         * gpt-3.5-turbo
+         */
+        GPT_3_5_TURBO("gpt-3.5-turbo"),
+        /**
+         * 临时模型,不建议使用,2023年9 月 13 日将被弃用
+         */
+        @Deprecated
+        GPT_3_5_TURBO_0301("gpt-3.5-turbo-0301"),
+        /**
+         * gpt-3.5-turbo-0613 支持函数
+         */
+        GPT_3_5_TURBO_1106("gpt-3.5-turbo-1106"),
+
+        GPT_3_5_TURBO_0613("gpt-3.5-turbo-0613"),
+        /**
+         * gpt-3.5-turbo-16k 超长上下文
+         */
+        GPT_3_5_TURBO_16K("gpt-3.5-turbo-16k"),
+        /**
+         * gpt-3.5-turbo-16k-0613 超长上下文 支持函数
+         */
+        GPT_3_5_TURBO_16K_0613("gpt-3.5-turbo-16k-0613"),
+        /**
+         * GPT4.0
+         */
+        GPT_4("gpt-4"),
+        /**
+         * 临时模型,不建议使用,2023年9 月 13 日将被弃用
+         */
+        @Deprecated
+        GPT_4_0314("gpt-4-0314"),
+        /**
+         * GPT4.0 超长上下文
+         */
+        GPT_4_32K("gpt-4-32k"),
+        /**
+         * 临时模型,不建议使用,2023年9 月 13 日将被弃用
+         */
+        @Deprecated
+        GPT_4_32K_0314("gpt-4-32k-0314"),
+
+        /**
+         * gpt-4-0613,支持函数
+         */
+        GPT_4_0613("gpt-4-0613"),
+        /**
+         * gpt-4-0613,支持函数
+         */
+        GPT_4_32K_0613("gpt-4-32k-0613"),
+        /**
+         * 支持数组模式,支持function call,支持可重复输出
+         */
+        GPT_4_1106_PREVIEW("gpt-4-1106-preview"),
+        /**
+         * 支持图片
+         */
+        GPT_4_VISION_PREVIEW("gpt-4-vision-preview"),
+        ;
+        private final String name;
+    }
+
+    @Getter
+    @AllArgsConstructor
+    public enum ChatType {
+        /**
+         * 对话类型 - 输入
+         */
+        CHAT_IN("in"),
+        /**
+         * 对话类型 - 输出
+         */
+        CHAT_OUT("out"),
+
+        ;
+        private final String name;
+    }
+
+    public static double getModelCost(String modelName) {
+        return switch (modelName) {
+            case "gpt-3.5-turbo-0613" -> OpenAIConst.GPT3_COST;
+            default -> OpenAIConst.GPT4_COST;
+        };
+    }
+}

+ 84 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/BaseMessage.java

@@ -0,0 +1,84 @@
+package com.xmzs.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.unfbx.chatgpt.entity.chat.tool.ToolCalls;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since 1.1.2
+ * 2023-03-02
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+@AllArgsConstructor
+public class BaseMessage implements Serializable {
+
+    /**
+     * 目前支持四个中角色参考官网,进行情景输入:
+     * https://platform.openai.com/docs/guides/chat/introduction
+     */
+    private String role;
+
+
+    private String name;
+
+    /**
+     * The tool calls generated by the model, such as function calls.
+     * @since 1.1.2
+     */
+    @JsonProperty("tool_calls")
+    private List<ToolCalls> toolCalls;
+
+    /**
+     * @since 1.1.2
+     */
+    @JsonProperty("tool_call_id")
+    private String toolCallId;
+
+    @Deprecated
+    @JsonProperty("function_call")
+    private FunctionCall functionCall;
+
+
+    /**
+     * 构造函数
+     *
+     * @param role         角色
+     * @param name         name
+     * @param functionCall functionCall
+     */
+    public BaseMessage(String role, String name, FunctionCall functionCall) {
+        this.role = role;
+        this.name = name;
+        this.functionCall = functionCall;
+    }
+
+    public BaseMessage() {
+    }
+
+
+    @Getter
+    @AllArgsConstructor
+    public enum Role {
+
+        SYSTEM("system"),
+        USER("user"),
+        ASSISTANT("assistant"),
+        FUNCTION("function"),
+        TOOL("tool"),
+        ;
+        private final String name;
+    }
+
+}

+ 31 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatChoice.java

@@ -0,0 +1,31 @@
+package com.xmzs.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-03-02
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ChatChoice implements Serializable {
+    private long index;
+    /**
+     * 请求参数stream为true返回是delta
+     */
+    @JsonProperty("delta")
+    private Message delta;
+    /**
+     * 请求参数stream为false返回是message
+     */
+    @JsonProperty("message")
+    private Message message;
+    @JsonProperty("finish_reason")
+    private String finishReason;
+}

+ 34 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletion.java

@@ -0,0 +1,34 @@
+package com.xmzs.common.chat.entity.chat;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.*;
+import lombok.experimental.SuperBuilder;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述: chat模型参数
+ *
+ * @author https:www.unfbx.com
+ * 2023-03-02
+ */
+@Data
+@SuperBuilder
+@Slf4j
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class ChatCompletion extends BaseChatCompletion implements Serializable {
+
+    /**
+     * 问题描述
+     */
+    @NonNull
+    private List<Message> messages;
+
+}

+ 26 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletionResponse.java

@@ -0,0 +1,26 @@
+package com.xmzs.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.xmzs.common.chat.entity.common.Usage;
+import lombok.Data;
+
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述: chat答案类
+ *
+ * @author https:www.unfbx.com
+ * 2023-03-02
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ChatCompletionResponse implements Serializable {
+    private String id;
+    private String object;
+    private long created;
+    private String model;
+    private List<ChatChoice> choices;
+    private Usage usage;
+}

+ 30 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ChatCompletionWithPicture.java

@@ -0,0 +1,30 @@
+package com.xmzs.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.*;
+import lombok.experimental.SuperBuilder;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述: chat模型附带图片的参数
+ *
+ * @author https:www.unfbx.com
+ * @since 1.1.2
+ * 2023-11-10
+ */
+@Data
+@SuperBuilder
+@Slf4j
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class ChatCompletionWithPicture extends BaseChatCompletion implements Serializable {
+    /**
+     * 问题描述
+     */
+    private List<MessagePicture> messages;
+
+}

+ 43 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Content.java

@@ -0,0 +1,43 @@
+package com.xmzs.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 描述:
+ *
+ * @author https://www.unfbx.com
+ * @since 1.1.2
+ * 2023-11-10
+ */
+@Data
+@Builder
+@Slf4j
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class Content {
+    /**
+     * 输入类型:text、image_url
+     *
+     * @see Type
+     */
+    private String type;
+    private String text;
+    @JsonProperty("image_url")
+    private ImageUrl imageUrl;
+
+    /**
+     * 生成图片风格
+     */
+    @Getter
+    @AllArgsConstructor
+    public enum Type {
+        TEXT("text"),
+        IMAGE_URL("image_url"),
+        ;
+        private final String name;
+    }
+}

+ 27 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/FunctionCall.java

@@ -0,0 +1,27 @@
+package com.xmzs.common.chat.entity.chat;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 描述:函数调用返回值
+ *
+ * @author https://www.unfbx.com
+ * @since 2023-06-14
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class FunctionCall {
+    /**
+     * 方法名
+     */
+    private String name;
+    /**
+     * 方法参数
+     */
+    private String arguments;
+}

+ 46 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Functions.java

@@ -0,0 +1,46 @@
+package com.xmzs.common.chat.entity.chat;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:方法参数实体类,实例数据如下
+ * <pre>
+ *     {
+ *          "name": "get_current_weather",
+ *          "description": "Get the current weather in a given location",
+ *          "parameters": {
+ *              "type": "object",
+ *              "properties": {
+ *                  "location": {
+ *                      "type": "string",
+ *                      "description": "The city and state, e.g. San Francisco, CA"
+ *                  },
+ *                  "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
+ *              },
+ *              "required": ["location"]
+ *          },
+ *     }
+ * </pre>
+ * @author https:www.unfbx.com
+ * @since  2023-06-14
+ */
+@Data
+@Builder
+public class Functions implements Serializable {
+    /**
+     * 方法名称
+     */
+    private String name;
+    /**
+     * 方法描述
+     */
+    private String description;
+    /**
+     * 方法参数
+     * 扩展参数可以继承Parameters自己实现,json格式的数据
+     */
+    private Parameters parameters;
+}

+ 28 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ImageUrl.java

@@ -0,0 +1,28 @@
+package com.xmzs.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 描述:
+ *
+ * @author https://www.unfbx.com
+ * 2023-11-10
+ */
+@Data
+@Builder
+@Slf4j
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class ImageUrl {
+    /**
+     * 图片地址,支持base64. eg: data:image/jpeg;base64,{base64_image}   <p\>
+     * https://platform.openai.com/docs/guides/vision
+     */
+    private String url;
+}

+ 117 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Message.java

@@ -0,0 +1,117 @@
+package com.xmzs.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-03-02
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Message implements Serializable {
+
+    /**
+     * 目前支持四个中角色参考官网,进行情景输入:
+     * https://platform.openai.com/docs/guides/chat/introduction
+     */
+    private String role;
+
+    private String content;
+
+    private String name;
+
+    @JsonProperty("function_call")
+    private FunctionCall functionCall;
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * 构造函数
+     *
+     * @param role         角色
+     * @param content      描述主题信息
+     * @param name         name
+     * @param functionCall functionCall
+     */
+    public Message(String role, String content, String name, FunctionCall functionCall) {
+        this.role = role;
+        this.content = content;
+        this.name = name;
+        this.functionCall = functionCall;
+    }
+
+    public Message() {
+    }
+
+    private Message(Builder builder) {
+        setRole(builder.role);
+        setContent(builder.content);
+        setName(builder.name);
+        setFunctionCall(builder.functionCall);
+    }
+
+
+    @Getter
+    @AllArgsConstructor
+    public enum Role {
+
+        SYSTEM("system"),
+        USER("user"),
+        ASSISTANT("assistant"),
+        FUNCTION("function"),
+        ;
+        private String name;
+    }
+
+    public static final class Builder {
+        private String role;
+        private String content;
+        private String name;
+        private FunctionCall functionCall;
+
+        public Builder() {
+        }
+
+        public Builder role(Role role) {
+            this.role = role.getName();
+            return this;
+        }
+
+        public Builder role(String role) {
+            this.role = role;
+            return this;
+        }
+
+        public Builder content(String content) {
+            this.content = content;
+            return this;
+        }
+
+        public Builder name(String name) {
+            this.name = name;
+            return this;
+        }
+
+        public Builder functionCall(FunctionCall functionCall) {
+            this.functionCall = functionCall;
+            return this;
+        }
+
+        public Message build() {
+            return new Message(this);
+        }
+    }
+}

+ 114 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/MessagePicture.java

@@ -0,0 +1,114 @@
+package com.xmzs.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.unfbx.chatgpt.entity.chat.tool.ToolCalls;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-03-02
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+@AllArgsConstructor
+public class MessagePicture extends BaseMessage implements Serializable {
+    /**
+     * Content数组支持多图片输入
+     * https://platform.openai.com/docs/guides/vision
+     */
+    private List<Content> content;
+
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * 构造函数
+     *
+     * @param role         角色
+     * @param name         name
+     * @param content      content
+     * @param functionCall functionCall
+     */
+    public MessagePicture(String role, String name, List<Content> content, List<ToolCalls> toolCalls, String toolCallId, FunctionCall functionCall) {
+        this.content = content;
+        super.setRole(role);
+        super.setName(name);
+        super.setToolCalls(toolCalls);
+        super.setToolCallId(toolCallId);
+        super.setFunctionCall(functionCall);
+    }
+
+    public MessagePicture() {
+    }
+
+    private MessagePicture(Builder builder) {
+        setContent(builder.content);
+        super.setRole(builder.role);
+        super.setName(builder.name);
+        super.setFunctionCall(builder.functionCall);
+        super.setToolCalls(builder.toolCalls);
+        super.setToolCallId(builder.toolCallId);
+    }
+
+    public static final class Builder {
+        private String role;
+        private List<Content> content;
+        private String name;
+        private String toolCallId;
+        private List<ToolCalls> toolCalls;
+        private FunctionCall functionCall;
+
+        public Builder() {
+        }
+
+        public Builder role(Role role) {
+            this.role = role.getName();
+            return this;
+        }
+
+        public Builder role(String role) {
+            this.role = role;
+            return this;
+        }
+
+        public Builder content(List<Content> content) {
+            this.content = content;
+            return this;
+        }
+
+        public Builder name(String name) {
+            this.name = name;
+            return this;
+        }
+
+        public Builder functionCall(FunctionCall functionCall) {
+            this.functionCall = functionCall;
+            return this;
+        }
+
+        public Builder toolCalls(List<ToolCalls> toolCalls) {
+            this.toolCalls = toolCalls;
+            return this;
+        }
+
+        public Builder toolCallId(String toolCallId) {
+            this.toolCallId = toolCallId;
+            return this;
+        }
+
+        public MessagePicture build() {
+            return new MessagePicture(this);
+        }
+    }
+
+}

+ 42 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/Parameters.java

@@ -0,0 +1,42 @@
+package com.xmzs.common.chat.entity.chat;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+/**
+ * 描述:方法参数类,扩展参数可以继承Parameters自己实现
+ * 参考:
+ * <pre>
+ * {
+ *     "type": "object",
+ *     "properties": {
+ *         "location": {
+ *             "type": "string",
+ *             "description": "The city and state, e.g. San Francisco, CA"
+ *         },
+ *         "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
+ *     },
+ *     "required": ["location"]
+ * }
+ * </pre>
+ * @author https:www.unfbx.com
+ * @since  2023-06-14
+ */
+@Data
+@Builder
+public class Parameters implements Serializable {
+    /**
+     * 参数类型
+     */
+    private String type;
+    /**
+     * 参数属性、描述
+     */
+    private Object properties;
+    /**
+     * 方法必输字段
+     */
+    private List<String> required;
+}

+ 28 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/ResponseFormat.java

@@ -0,0 +1,28 @@
+package com.xmzs.common.chat.entity.chat;
+
+import lombok.*;
+
+/**
+ * 指定模型必须输出的格式的对象。
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ResponseFormat {
+    /**
+     * 默认:text
+     *
+     * @see Type
+     */
+    private String type;
+
+    @Getter
+    @AllArgsConstructor
+    public enum Type {
+        JSON_OBJECT("json_object"),
+        TEXT("text"),
+        ;
+        private final String name;
+    }
+}

+ 31 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolCallFunction.java

@@ -0,0 +1,31 @@
+package com.xmzs.common.chat.entity.chat.tool;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * ToolCall 的 Function参数
+ * The function that the model called.
+ *
+ * @author https:www.unfbx.com
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ToolCallFunction implements Serializable {
+    /**
+     * 方法名
+     */
+    private String name;
+    /**
+     * 方法参数
+     */
+    private String arguments;
+}

+ 38 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolCalls.java

@@ -0,0 +1,38 @@
+package com.unfbx.chatgpt.entity.chat.tool;
+
+import com.xmzs.common.chat.entity.chat.tool.ToolCallFunction;
+import lombok.*;
+
+import java.io.Serializable;
+
+/**
+ * The tool calls generated by the model, such as function calls.
+ *
+ * @author <a href="https://www.unfbx.com">unfbx</a>
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ToolCalls implements Serializable {
+    /**
+     * The ID of the tool call.
+     */
+    private String id;
+    /**
+     * The type of the tool. Currently, only function is supported.
+     */
+    private String type;
+
+    private ToolCallFunction function;
+
+    @Getter
+    @AllArgsConstructor
+    public enum Type {
+        FUNCTION("function"),
+        ;
+        private final String name;
+    }
+}

+ 25 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoice.java

@@ -0,0 +1,25 @@
+package com.xmzs.common.chat.entity.chat.tool;
+
+import lombok.*;
+
+import java.io.Serializable;
+
+/**
+ * choice和object同时存在是以object为准
+ *
+ * @author <a href="https://www.unfbx.com">unfbx</a>
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+public class ToolChoice implements Serializable {
+
+    @Getter
+    @AllArgsConstructor
+    public enum Choice {
+        NONE("none"),
+        AUTO("auto"),
+        ;
+        private final String name;
+    }
+}

+ 33 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoiceObj.java

@@ -0,0 +1,33 @@
+package com.xmzs.common.chat.entity.chat.tool;
+
+import lombok.*;
+
+/**
+ * @author <a href="https://www.unfbx.com">unfbx</a>
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ToolChoiceObj {
+    /**
+     * 需要调用的方法名称
+     */
+    private ToolChoiceObjFunction function;
+    /**
+     * 工具的类型。目前仅支持函数。
+     *
+     * @see Type
+     */
+    private String type;
+
+    @Getter
+    @AllArgsConstructor
+    public enum Type {
+        FUNCTION("function"),
+        ;
+        private final String name;
+    }
+}

+ 21 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolChoiceObjFunction.java

@@ -0,0 +1,21 @@
+package com.xmzs.common.chat.entity.chat.tool;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author <a href="https://www.unfbx.com">unfbx</a>
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ToolChoiceObjFunction {
+
+    private String name;
+}

+ 35 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/Tools.java

@@ -0,0 +1,35 @@
+package com.xmzs.common.chat.entity.chat.tool;
+
+
+import lombok.*;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="https://www.unfbx.com">unfbx</a>
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class Tools implements Serializable {
+
+    /**
+     * 目前只支持:function
+     *
+     * @see Type
+     */
+    private String type;
+
+    private ToolsFunction function;
+
+    @Getter
+    @AllArgsConstructor
+    public enum Type {
+        FUNCTION("function"),
+        ;
+        private final String name;
+    }
+}

+ 36 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/chat/tool/ToolsFunction.java

@@ -0,0 +1,36 @@
+package com.xmzs.common.chat.entity.chat.tool;
+
+
+import com.xmzs.common.chat.entity.chat.Parameters;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="https://www.unfbx.com">unfbx</a>
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ToolsFunction implements Serializable {
+
+    /**
+     * 要调用的函数的名称。必须是 a-z、A-Z、0-9,或包含下划线和破折号,最大长度为 64
+     */
+    private String name;
+    /**
+     * 对函数功能的描述,模型使用它来选择何时以及如何调用该函数。
+     */
+    private String description;
+    /**
+     * 函数接受的参数,描述为 JSON Schema 对象
+     * 扩展参数可以继承Parameters自己实现,json格式的数据
+     */
+    private Parameters parameters;
+}

+ 23 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/Choice.java

@@ -0,0 +1,23 @@
+package com.xmzs.common.chat.entity.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ *  2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Choice implements Serializable {
+    private String text;
+    private long index;
+    private Object logprobs;
+    @JsonProperty("finish_reason")
+    private String finishReason;
+}

+ 20 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/DeleteResponse.java

@@ -0,0 +1,20 @@
+package com.xmzs.common.chat.entity.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ *  2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DeleteResponse implements Serializable {
+    private String id;
+    private String object;
+    private boolean deleted;
+}

+ 30 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/OpenAiResponse.java

@@ -0,0 +1,30 @@
+package com.xmzs.common.chat.entity.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ *  2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class OpenAiResponse<T> implements Serializable {
+    private String object;
+    private List<T> data;
+    private Error error;
+
+
+    @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public class Error {
+        private String message;
+        private String type;
+        private String param;
+        private String code;
+    }
+}

+ 24 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/common/Usage.java

@@ -0,0 +1,24 @@
+package com.xmzs.common.chat.entity.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ *  2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Usage implements Serializable {
+    @JsonProperty("prompt_tokens")
+    private long promptTokens;
+    @JsonProperty("completion_tokens")
+    private long completionTokens;
+    @JsonProperty("total_tokens")
+    private long totalTokens;
+}

+ 126 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/completions/Completion.java

@@ -0,0 +1,126 @@
+package com.xmzs.common.chat.entity.completions;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 描述: 问题类
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-11
+ */
+@Data
+@Builder
+@Slf4j
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class Completion implements Serializable {
+
+    @NonNull
+    @Builder.Default
+    private String model = Model.DAVINCI_003.getName();
+    /**
+     * 问题描述
+     */
+    @NonNull
+    private String prompt;
+    /**
+     * 完成输出后的后缀,用于格式化输出结果
+     */
+    private String suffix;
+
+    /**
+     * 最大支持4096
+     */
+    @JsonProperty("max_tokens")
+    @Builder.Default
+    private Integer maxTokens = 2048;
+    /**
+     * 使用什么取样温度,0到2之间。较高的值(如0.8)将使输出更加随机,而较低的值(如0.2)将使输出更加集中和确定。
+     * <p>
+     * We generally recommend altering this or but not both.top_p
+     */
+    @Builder.Default
+    private double temperature = 0;
+
+    /**
+     * 使用温度采样的替代方法称为核心采样,其中模型考虑具有top_p概率质量的令牌的结果。因此,0.1 意味着只考虑包含前 10% 概率质量的代币。
+     * <p>
+     * 我们通常建议更改此设置,但不要同时更改两者。temperature
+     */
+    @JsonProperty("top_p")
+    @Builder.Default
+    private Double topP = 1d;
+
+    /**
+     * 为每个提示生成的完成次数。
+     */
+    @Builder.Default
+    private Integer n = 1;
+
+    @Builder.Default
+    private boolean stream = false;
+    /**
+     * 最大值:5
+     */
+    private Integer logprobs;
+
+    @Builder.Default
+    private boolean echo = false;
+
+    private List<String> stop;
+
+    @JsonProperty("presence_penalty")
+    @Builder.Default
+    private double presencePenalty = 0;
+
+    /**
+     * -2.0 ~~ 2.0
+     */
+    @JsonProperty("frequency_penalty")
+    @Builder.Default
+    private double frequencyPenalty = 0;
+
+    @JsonProperty("best_of")
+    @Builder.Default
+    private Integer bestOf = 1;
+
+    @JsonProperty("logit_bias")
+    private Map logitBias;
+    /**
+     * 用户唯一值,确保接口不被重复调用
+     */
+    private String user;
+
+    /**
+     * 获取当前参数的tokens数
+     * @return  token数量
+     */
+//    public long tokens() {
+//        if (StrUtil.isBlank(this.prompt) || StrUtil.isBlank(this.model)) {
+//            log.warn("参数异常model:{},prompt:{}", this.model, this.prompt);
+//            return 0;
+//        }
+//        return TikTokensUtil.tokens(this.model, this.prompt);
+//    }
+
+    @Getter
+    @AllArgsConstructor
+    public enum Model {
+        DAVINCI_003("text-davinci-003"),
+        DAVINCI_002("text-davinci-002"),
+        DAVINCI("davinci"),
+        ;
+        private String name;
+    }
+}
+
+

+ 27 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/completions/CompletionResponse.java

@@ -0,0 +1,27 @@
+package com.xmzs.common.chat.entity.completions;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.xmzs.common.chat.entity.common.Choice;
+import com.xmzs.common.chat.entity.common.OpenAiResponse;
+import com.xmzs.common.chat.entity.common.Usage;
+import lombok.Data;
+
+
+import java.io.Serializable;
+
+/**
+ * 描述: 答案类
+ *
+ * @author https:www.unfbx.com
+ *  2023-02-11
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class CompletionResponse extends OpenAiResponse implements Serializable {
+    private String id;
+    private String object;
+    private long created;
+    private String model;
+    private Choice[] choices;
+    private Usage usage;
+}

+ 29 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/dto/WebSocketMessageDto.java

@@ -0,0 +1,29 @@
+package com.xmzs.common.chat.entity.dto;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 消息的dto
+ *
+ * @author zendwang
+ */
+@Data
+public class WebSocketMessageDto implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 需要推送到的session key 列表
+     */
+    private List<Long> sessionKeys;
+
+    /**
+     * 需要发送的消息
+     */
+    private String message;
+}

+ 104 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/edits/Edit.java

@@ -0,0 +1,104 @@
+package com.xmzs.common.chat.entity.edits;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ *  2023-02-15
+ */
+@Getter
+@Builder
+@Slf4j
+@NoArgsConstructor
+@AllArgsConstructor
+public class Edit implements Serializable {
+    /**
+     * 编辑模型,目前支持两种
+     */
+    @NonNull
+    private String model;
+
+    @NonNull
+    private String input;
+    /**
+     * 提示说明。告知模型如何修改。
+     */
+    @NonNull
+    private String instruction;
+
+
+    /**
+     * 使用什么取样温度,0到2之间。较高的值(如0.8)将使输出更加随机,而较低的值(如0.2)将使输出更加集中和确定。
+     *
+     * We generally recommend altering this or but not both.top_p
+     */
+    @Builder.Default
+    private double temperature = 0;
+
+    /**
+     * 使用温度采样的替代方法称为核心采样,其中模型考虑具有top_p概率质量的令牌的结果。因此,0.1 意味着只考虑包含前 10% 概率质量的代币。
+     *
+     * 我们通常建议更改此设置,但不要同时更改两者。temperature
+     */
+    @JsonProperty("top_p")
+    @Builder.Default
+    private Double topP = 1d;
+
+    /**
+     * 为每个提示生成的完成次数。
+     */
+    @Builder.Default
+    private Integer n = 1;
+
+    public void setModel(Model model) {
+        this.model = model.getName();
+    }
+
+    public void setTemperature(double temperature) {
+        if (temperature > 2 || temperature < 0) {
+            log.error("temperature参数异常,temperature属于[0,2]");
+            this.temperature = 2;
+            return;
+        }
+        if (temperature < 0) {
+            log.error("temperature参数异常,temperature属于[0,2]");
+            this.temperature = 0;
+            return;
+        }
+        this.temperature = temperature;
+    }
+
+
+    public void setTopP(Double topP) {
+        this.topP = topP;
+    }
+
+    public void setN(Integer n) {
+        this.n = n;
+    }
+
+    public void setInput(String input) {
+        this.input = input;
+    }
+
+    public void setInstruction(String instruction) {
+        this.instruction = instruction;
+    }
+    @Getter
+    @AllArgsConstructor
+    public enum Model {
+        TEXT_DAVINCI_EDIT_001("text-davinci-edit-001"),
+        CODE_DAVINCI_EDIT_001("code-davinci-edit-001"),
+        ;
+        private String name;
+    }
+}
+
+
+

+ 27 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/edits/EditResponse.java

@@ -0,0 +1,27 @@
+package com.xmzs.common.chat.entity.edits;
+
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.xmzs.common.chat.entity.common.Choice;
+import com.xmzs.common.chat.entity.common.Usage;
+import lombok.Data;
+
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ *  2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class EditResponse implements Serializable {
+    private String id;
+    private String object;
+    private long created;
+    private String model;
+    private Choice[] choices;
+    private Usage usage;
+}

+ 54 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/Embedding.java

@@ -0,0 +1,54 @@
+package com.xmzs.common.chat.entity.embeddings;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ *  2023-02-15
+ */
+@Getter
+@Slf4j
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class Embedding implements Serializable {
+    @NonNull
+    @Builder.Default
+    private String model = Model.TEXT_EMBEDDING_ADA_002.getName();
+    /**
+     * 必选项:长度不能超过:8192
+     */
+    @NonNull
+    private List<String> input;
+
+    private String user;
+
+    public void setModel(Model model) {
+        if (Objects.isNull(model)) {
+            model = Model.TEXT_EMBEDDING_ADA_002;
+        }
+        this.model = model.getName();
+    }
+
+
+    public void setUser(String user) {
+        this.user = user;
+    }
+
+    @Getter
+    @AllArgsConstructor
+    public enum Model {
+        TEXT_EMBEDDING_ADA_002("text-embedding-ada-002"),
+        ;
+        private String name;
+    }
+}

+ 25 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/EmbeddingResponse.java

@@ -0,0 +1,25 @@
+package com.xmzs.common.chat.entity.embeddings;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.xmzs.common.chat.entity.common.Usage;
+import lombok.Data;
+
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ *  2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class EmbeddingResponse implements Serializable {
+
+    private String object;
+    private List<Item> data;
+    private String model;
+    private Usage usage;
+}

+ 16 - 0
ruoyi-common/ruoyi-common-chat/src/main/java/com/xmzs/common/chat/entity/embeddings/Item.java

@@ -0,0 +1,16 @@
+package com.xmzs.common.chat.entity.embeddings;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Item implements Serializable {
+    private String object;
+    private List<BigDecimal> embedding;
+    private Integer index;
+}

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