This commit is contained in:
ageer
2024-01-16 12:38:04 +08:00
parent c36313513b
commit 1f7f97e86a
702 changed files with 59052 additions and 2 deletions

18
.editorconfig Normal file
View File

@@ -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
.gitignore vendored Normal file
View File

@@ -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
LICENSE Normal file
View File

@@ -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
README.en.md Normal file
View File

@@ -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/)

View File

@@ -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 Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

BIN
image/02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
image/03.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

BIN
image/04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

BIN
image/05.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 KiB

BIN
image/06.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

BIN
image/07.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

BIN
image/08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 KiB

BIN
image/09.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

BIN
image/10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
image/11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

BIN
image/12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

471
pom.xml Normal file
View File

@@ -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
ruoyi-admin/Dockerfile Normal file
View File

@@ -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
ruoyi-admin/pom.xml Normal file
View File

@@ -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>

View File

@@ -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智能助手启动成功 ლ(´ڡ`ლ)゙");
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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";
}
}

View File

@@ -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";
}
}

View File

@@ -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:

View File

@@ -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:

View File

@@ -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
# 安全秘钥 对称算法的秘钥 如AESSM4
password:
# 公私钥 非对称算法的公私钥 如SM2RSA
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

View File

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

View File

@@ -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=对不起,您的租户已过期,请联系管理员

View File

@@ -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 disabledplease 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 exitplease 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 dataplease contact your administrator to add permissions [{0}]
no.create.permission=You do not have permission to create dataplease contact your administrator to add permissions [{0}]
no.update.permission=You do not have permission to modify dataplease contact your administrator to add permissions [{0}]
no.delete.permission=You do not have permission to delete dataplease contact your administrator to add permissions [{0}]
no.export.permission=You do not have permission to export dataplease contact your administrator to add permissions [{0}]
no.view.permission=You do not have permission to view dataplease 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.

View File

@@ -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=对不起,您的租户已过期,请联系管理员

Binary file not shown.

View File

@@ -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>

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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 ==================");
}
}

View File

@@ -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 ==================");
}
}

View File

@@ -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
ruoyi-common/pom.xml Normal file
View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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";
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
};
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

Some files were not shown because too many files have changed in this diff Show More