first commit
This commit is contained in:
parent
ff102fa689
commit
f1065dada5
|
@ -0,0 +1,33 @@
|
|||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
|
@ -0,0 +1,69 @@
|
|||
# Getting Started
|
||||
|
||||
> 声明:此项目只发布于 Github,基于 MIT 协议,免费且作为开源学习使用。并且不会有任何形式的卖号等行为,谨防受骗。
|
||||
|
||||
|
||||
![1691582252468](image/README/1691582252468.png)
|
||||
|
||||
![1691583184761](image/README/1691583184761.png)
|
||||
|
||||
![1691583124744](image/README/1691583124744.png)
|
||||
|
||||
![1691583329105](image/README/1691583329105.png)
|
||||
|
||||
该仓库为后端服务,前端项目见[aideepin-web](https://github.com/moyangzhan/aideepin-web)
|
||||
|
||||
### 如何部署
|
||||
|
||||
#### 初始化
|
||||
|
||||
* 初始化数据库
|
||||
|
||||
* 创建数据库aideepin
|
||||
* 执行docs/create.sql
|
||||
* 填充openai的secret_key
|
||||
|
||||
```
|
||||
update adi_sys_config set value = 'my_chatgpt_secret_key' where name = 'secret_key'
|
||||
```
|
||||
|
||||
* 修改配置文件
|
||||
|
||||
* mysql: application-[dev|prod].xml中的spring.datasource
|
||||
* redis: application-[dev|prod].xml中的spring.data.redis
|
||||
* mail: application.xml中的spring.mail
|
||||
|
||||
#### 编译及运行
|
||||
|
||||
* 进入项目
|
||||
|
||||
```
|
||||
cd aideepin
|
||||
```
|
||||
|
||||
* 打包:
|
||||
|
||||
```
|
||||
mvn clean package -Dmaven.test.skip=true
|
||||
```
|
||||
|
||||
* 运行
|
||||
|
||||
a. jar包启动:
|
||||
|
||||
```
|
||||
cd adi-chat/target
|
||||
nohup java -jar -Xms768m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError adi-chat-0.0.1-SNAPSHOT.jar --spring.profiles.active=[dev|prod] dev/null 2>&1 &
|
||||
```
|
||||
|
||||
b. docker启动
|
||||
|
||||
```
|
||||
cd adi-chat
|
||||
docker build . -t aideepin:0.0.1
|
||||
docker run -d \
|
||||
--name=aideepin \
|
||||
-e APP_PROFILE=[dev|prod] \
|
||||
-v="/data/aideepin/logs:/data/logs" \
|
||||
aideepin:0.0.1
|
||||
```
|
|
@ -0,0 +1,4 @@
|
|||
Admin site
|
||||
|
||||
|
||||
TODO...
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.moyz</groupId>
|
||||
<artifactId>aideepin</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>adi-admin</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.moyz</groupId>
|
||||
<artifactId>adi-bootstrap</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.moyz</groupId>
|
||||
<artifactId>adi-common</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.moyz</groupId>
|
||||
<artifactId>aideepin</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>adi-bootstrap</artifactId>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,17 @@
|
|||
package com.moyz.adi;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableAsync
|
||||
@EnableScheduling
|
||||
public class BootstrapApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BootstrapApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/aideepin?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&tinyInt1isBit=false&allowMultiQueries=true
|
||||
username: root
|
||||
password: 123456
|
||||
data:
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password:
|
||||
database: 0
|
||||
lettuce:
|
||||
pool:
|
||||
#连接池最大连接数
|
||||
max-active: 20
|
||||
#连接池最大阻塞等待时间
|
||||
max-wait: -1
|
||||
#连接池中的最大空闲连接
|
||||
max-idle: 5
|
||||
#连接池中的最小空闲连接
|
||||
min-idle: 1
|
||||
|
||||
logging:
|
||||
file:
|
||||
path: D:/data/logs
|
||||
|
||||
openai:
|
||||
proxy:
|
||||
enable: true
|
||||
host: 127.0.0.1
|
||||
http-port: 1087
|
||||
|
||||
adi:
|
||||
frontend-url: http://localhost:1002
|
||||
backend-url: http://localhost:1002/api
|
||||
|
||||
|
||||
local:
|
||||
files: D:/data/files/
|
||||
images: D:/data/images/
|
||||
tmp_images: D:/data/tmp_images/
|
|
@ -0,0 +1,22 @@
|
|||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/aideepin?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&tinyInt1isBit=false&allowMultiQueries=true
|
||||
username: your-mysql-account
|
||||
password: your-mysql-password
|
||||
data:
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password:
|
||||
database: 0
|
||||
lettuce:
|
||||
pool:
|
||||
#连接池最大连接数
|
||||
max-active: 20
|
||||
#连接池最大阻塞等待时间
|
||||
max-wait: -1
|
||||
#连接池中的最大空闲连接
|
||||
max-idle: 5
|
||||
#连接池中的最小空闲连接
|
||||
min-idle: 1
|
|
@ -0,0 +1,60 @@
|
|||
server:
|
||||
port: 9999
|
||||
context-path: /
|
||||
session:
|
||||
timeout: 28800
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: AiDeepIn
|
||||
profiles:
|
||||
active: dev
|
||||
jackson:
|
||||
date-format: "yyyy-MM-dd HH:mm:ss"
|
||||
time-zone: "GMT+8"
|
||||
serialization: { write-dates-as-timestamps: false }
|
||||
cache:
|
||||
type: redis
|
||||
redis:
|
||||
key-prefix: CACHE
|
||||
time-to-live: 1d
|
||||
mail:
|
||||
default-encoding: UTF-8
|
||||
protocol: smtps
|
||||
host: your-email-host # smtp.exmail.qq.com
|
||||
username: your-email-username # xxx@qq.com
|
||||
password: your-email-password
|
||||
port: 465
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
ssl:
|
||||
enable: true
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 10MB
|
||||
max-request-size: 20MB
|
||||
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html
|
||||
|
||||
mybatis-plus:
|
||||
# 支持统配符 * 或者 ; 分割
|
||||
mapper-locations: classpath*:/mapper/*.xml
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
|
||||
logging:
|
||||
file: /data/logs
|
||||
|
||||
adi:
|
||||
frontend-url: http://www.aideepin.com
|
||||
backend-url: http://www.aideepin.com/api
|
||||
|
||||
local:
|
||||
files: /data/files/
|
||||
images: /data/images/
|
||||
tmp_images: /data/tmp_images/
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<!-- <include resource="org/springframework/boot/logging/logback/base.xml"/>-->
|
||||
<springProperty name="APP_NAME" scope="context" source="spring.application.name"/>
|
||||
<springProperty name="LOG_FILEMAXDAY" scope="context" source="logback.filemaxday" defaultValue="30"/>
|
||||
<springProperty name="LOG_MAXFILESIZE" scope="context" source="logback.filesize" defaultValue="50MB"/>
|
||||
|
||||
<!-- 彩色日志 -->
|
||||
<!-- 彩色日志依赖的渲染类 -->
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
|
||||
<conversionRule conversionWord="wex"
|
||||
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
|
||||
<conversionRule conversionWord="wEx"
|
||||
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
|
||||
|
||||
<!-- 彩色日志格式 -->
|
||||
<property name="CONSOLE_LOG_PATTERN"
|
||||
value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%level){blue} %clr([%thread]){orange} %clr(%logger){cyan} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
|
||||
<property name="CONSOLE_LOG_PATTERN_NO_COLOR"
|
||||
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread] %logger %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
|
||||
|
||||
<!-- 控制台日志 -->
|
||||
<appender name="StdoutAppender" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
<!-- 按照每天生成常规日志文件 -->
|
||||
<appender name="FileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>/data/logs/adi.log</file>
|
||||
<encoder>
|
||||
<pattern>${CONSOLE_LOG_PATTERN_NO_COLOR}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<!-- 基于时间的分包策略 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>/data/logs/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<!--保留时间,单位:天-->
|
||||
<maxHistory>${LOG_FILEMAXDAY}</maxHistory>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>${LOG_MAXFILESIZE}</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
</rollingPolicy>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<springProfile name="prod">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="FileAppender"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<springProfile name="dev">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="StdoutAppender"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
</configuration>
|
|
@ -0,0 +1,17 @@
|
|||
FROM openjdk:17
|
||||
|
||||
ENV LANG="en_US.UTF-8" \
|
||||
LANGUAGE="en_US:en" \
|
||||
LC_ALL="en_US.UTF-8" \
|
||||
APP_VERSION="1.0.0-SNAPSHOT" \
|
||||
TZ="Asia/Shanghai" \
|
||||
APP_PROFILE="dev" \
|
||||
JAVA_OPTS="-Xms768m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError"
|
||||
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
ADD ./target/adi-chat-0.0.1-SNAPSHOT.jar /data/app/aideepin.jar
|
||||
|
||||
ENTRYPOINT ["sh","-c", "java $JAVA_OPTS -jar /data/app/aideepin.jar --spring.profiles.active=$APP_PROFILE"]
|
||||
|
||||
EXPOSE 9999
|
|
@ -0,0 +1 @@
|
|||
User side
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.moyz</groupId>
|
||||
<artifactId>aideepin</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>adi-chat</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.moyz</groupId>
|
||||
<artifactId>adi-bootstrap</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.moyz</groupId>
|
||||
<artifactId>adi-common</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 引入本地lib包 -->
|
||||
<dependency>
|
||||
<groupId>com.ramostear</groupId>
|
||||
<artifactId>Happy-Captcha</artifactId>
|
||||
<scope>system</scope>
|
||||
<version>1.0.1</version>
|
||||
<systemPath>${project.basedir}/src/lib/Happy-Captcha-1.0.1.jar</systemPath>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<mainClass>com.moyz.adi.BootstrapApplication</mainClass>
|
||||
<includeSystemScope>true</includeSystemScope>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>build-info</goal>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
Binary file not shown.
|
@ -0,0 +1,64 @@
|
|||
package com.moyz.adi.chat.controller;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.moyz.adi.common.dto.*;
|
||||
import com.moyz.adi.common.service.AiImageService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/ai-image")
|
||||
@Validated
|
||||
public class AiImageController {
|
||||
|
||||
@Resource
|
||||
private AiImageService imageService;
|
||||
|
||||
@PostMapping("/generation")
|
||||
public Map<String, String> generation(@RequestBody @Validated GenerateImageReq generateImageReq) {
|
||||
String uuid = imageService.createByPrompt(generateImageReq);
|
||||
return Map.of("uuid", uuid);
|
||||
}
|
||||
|
||||
@PostMapping("/regenerate/{uuid}")
|
||||
public void regenerate(@PathVariable @Length(min = 32, max = 32) String uuid) {
|
||||
imageService.regenerate(uuid);
|
||||
}
|
||||
|
||||
@Operation(summary = "Edit image")
|
||||
@PostMapping("/edit")
|
||||
public Map<String, String> edit(@RequestBody EditImageReq editImageReq) {
|
||||
String uuid = imageService.editByOriginalImage(editImageReq);
|
||||
return Map.of("uuid", uuid);
|
||||
}
|
||||
|
||||
@Operation(summary = "Image variation")
|
||||
@PostMapping("/variation")
|
||||
public Map<String, String> variation(@RequestBody VariationImageReq variationImageReq) {
|
||||
String uuid = imageService.variationImage(variationImageReq);
|
||||
return Map.of("uuid", uuid);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
public AiImagesListResp list(@RequestParam Long maxId, @RequestParam int pageSize) {
|
||||
return imageService.listAll(maxId, pageSize);
|
||||
}
|
||||
|
||||
@GetMapping("/detail/{uuid}")
|
||||
public AiImageDto getOne(@PathVariable String uuid) {
|
||||
return imageService.getOne(uuid);
|
||||
}
|
||||
|
||||
@GetMapping("/del/{uuid}")
|
||||
public boolean del(@PathVariable String uuid) {
|
||||
return imageService.del(uuid);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package com.moyz.adi.chat.controller;
|
||||
|
||||
import com.moyz.adi.common.dto.LoginReq;
|
||||
import com.moyz.adi.common.dto.LoginResp;
|
||||
import com.moyz.adi.common.dto.RegisterReq;
|
||||
import com.moyz.adi.common.service.UserService;
|
||||
import com.ramostear.captcha.HappyCaptcha;
|
||||
import com.ramostear.captcha.support.CaptchaType;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Tag(name = "权限controller", description = "权限controller")
|
||||
@Validated
|
||||
@RestController
|
||||
@RequestMapping("auth")
|
||||
public class AuthController {
|
||||
|
||||
@Value("${adi.frontend-url}")
|
||||
private String frontendUrl;
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@Operation(summary = "注册")
|
||||
@PostMapping(value = "/register", produces = MediaType.TEXT_PLAIN_VALUE)
|
||||
public String register(@RequestBody RegisterReq registerReq) {
|
||||
userService.register(registerReq.getEmail(), registerReq.getPassword(), registerReq.getCaptchaId(), registerReq.getCaptchaCode());
|
||||
return "激活链接已经发送到邮箱,请登录邮箱进行激活";
|
||||
}
|
||||
|
||||
@Operation(summary = "注册的验证码")
|
||||
@GetMapping("/register/captcha")
|
||||
public void registerCaptcha(@Parameter(description = "验证码ID") @RequestParam @Length(min = 32) String captchaId,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
HappyCaptcha happyCaptcha = HappyCaptcha.require(request, response).type(CaptchaType.WORD_NUMBER_UPPER).build().finish();
|
||||
String captchaCode = happyCaptcha.getCode();
|
||||
userService.cacheRegisterCaptcha(captchaId, captchaCode);
|
||||
happyCaptcha.output();
|
||||
}
|
||||
|
||||
@Operation(summary = "激活")
|
||||
@GetMapping("active")
|
||||
public boolean active(@RequestParam("code") String activeCode, HttpServletResponse response) {
|
||||
|
||||
try {
|
||||
userService.active(activeCode);
|
||||
response.sendRedirect(frontendUrl + "/#/active?active=success&msg=" + URLEncoder.encode("激活成功,请登录"));
|
||||
} catch (IOException e) {
|
||||
log.error("auth.active:", e);
|
||||
try {
|
||||
response.sendRedirect(frontendUrl + "/#/active?active=fail&msg=" + URLEncoder.encode("激活失败:系统错误,请重新注册或者登录"));
|
||||
} catch (IOException ex) {
|
||||
log.error("auth.active:", ex);
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
response.sendRedirect(frontendUrl + "/#/active?active=fail&msg=" + URLEncoder.encode(e.getMessage()));
|
||||
} catch (IOException ex) {
|
||||
log.error("auth.active:", ex);
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Operation(summary = "忘记密码")
|
||||
@PostMapping("password/forgot")
|
||||
public String forgotPassword(@RequestParam @NotBlank String email) {
|
||||
userService.forgotPassword(email);
|
||||
return "重置密码链接已发送";
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "重置密码")
|
||||
@GetMapping("/password/reset")
|
||||
public void resetPassword(@RequestParam @NotBlank String code, HttpServletResponse response) {
|
||||
userService.resetPassword(code);
|
||||
try {
|
||||
response.sendRedirect(frontendUrl + "/#/active?active=success&msg=" + URLEncoder.encode("密码已经重置"));
|
||||
} catch (IOException e) {
|
||||
log.error("resetPassword:", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "登录")
|
||||
@PostMapping("login")
|
||||
public LoginResp login(@Validated @RequestBody LoginReq loginReq, HttpServletResponse response) {
|
||||
LoginResp loginResp = userService.login(loginReq);
|
||||
response.setHeader(AUTHORIZATION, loginResp.getToken());
|
||||
Cookie cookie = new Cookie(AUTHORIZATION, loginResp.getToken());
|
||||
response.addCookie(cookie);
|
||||
return loginResp;
|
||||
}
|
||||
|
||||
@Operation(summary = "获取登录验证码")
|
||||
@GetMapping("/login/captcha")
|
||||
public void captcha(@RequestParam @Length(min = 32) String captchaId, HttpServletRequest request, HttpServletResponse response) {
|
||||
HappyCaptcha happyCaptcha = HappyCaptcha.require(request, response).type(CaptchaType.WORD_NUMBER_UPPER).build().finish();
|
||||
String captchaCode = happyCaptcha.getCode();
|
||||
userService.cacheLoginCaptcha(captchaId, captchaCode);
|
||||
happyCaptcha.output();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package com.moyz.adi.chat.controller;
|
||||
|
||||
import com.moyz.adi.common.dto.ConvDto;
|
||||
import com.moyz.adi.common.dto.ConvEditReq;
|
||||
import com.moyz.adi.common.service.ConversationService;
|
||||
import com.moyz.adi.common.dto.ConvMsgListResp;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 对话controller
|
||||
*/
|
||||
@Tag(name = "对话controller", description = "对话controller")
|
||||
@RequestMapping("/conversation")
|
||||
@RestController
|
||||
public class ConversationController {
|
||||
|
||||
@Resource
|
||||
private ConversationService conversationService;
|
||||
|
||||
@Operation(summary = "获取当前用户所有的对话")
|
||||
@GetMapping("/list")
|
||||
public List<ConvDto> list() {
|
||||
return conversationService.listByUser();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询某个对话的信息列表")
|
||||
@GetMapping("/{uuid}")
|
||||
public ConvMsgListResp detail(
|
||||
@Parameter(name = "对话uuid") @PathVariable @NotBlank(message = "对话uuid不能为空") String uuid
|
||||
, @Parameter(name = "最大uuid") @RequestParam String maxMsgUuid
|
||||
, @Parameter(name = "每页数量") @RequestParam @Min(1) @Max(100) int pageSize) {
|
||||
return conversationService.detail(uuid, maxMsgUuid, pageSize);
|
||||
}
|
||||
|
||||
@PostMapping("/edit/{uuid}")
|
||||
public boolean edit(@PathVariable String uuid, @RequestBody ConvEditReq convEditReq) {
|
||||
return conversationService.edit(uuid, convEditReq);
|
||||
}
|
||||
|
||||
@PostMapping("/del/{uuid}")
|
||||
public boolean softDel(@PathVariable String uuid) {
|
||||
return conversationService.softDel(uuid);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.moyz.adi.chat.controller;
|
||||
|
||||
import com.moyz.adi.common.dto.AskReq;
|
||||
import com.moyz.adi.common.service.ConversationMessageService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/conversation/message")
|
||||
@Validated
|
||||
public class ConversationMessageController {
|
||||
|
||||
@Resource
|
||||
private ConversationMessageService conversationMessageService;
|
||||
|
||||
@Operation(summary = "发送一个prompt给模型")
|
||||
@PostMapping(value = "/process", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public SseEmitter ask(@RequestBody @Validated AskReq askReq) {
|
||||
return conversationMessageService.sseAsk(askReq);
|
||||
}
|
||||
|
||||
@PostMapping("/del/{uuid}")
|
||||
public boolean softDelete(@PathVariable String uuid) {
|
||||
return conversationMessageService.softDelete(uuid);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package com.moyz.adi.chat.controller;
|
||||
|
||||
import com.moyz.adi.common.service.FileService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@Validated
|
||||
public class FileController {
|
||||
|
||||
@Resource
|
||||
private FileService fileService;
|
||||
|
||||
@GetMapping(value = "/image/{uuid}", produces = MediaType.IMAGE_PNG_VALUE)
|
||||
public void image(@Length(min = 32, max = 32) @PathVariable String uuid, HttpServletResponse response) {
|
||||
BufferedImage bufferedImage = fileService.readBufferedImage(uuid);
|
||||
//把图片写给浏览器
|
||||
try {
|
||||
ImageIO.write(bufferedImage, "png", response.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping(path = "/file/upload", headers = "content-type=multipart/form-data", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Map<String, String> upload(@RequestPart(value = "file") MultipartFile file) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("uuid", fileService.writeToLocal(file));
|
||||
return result;
|
||||
}
|
||||
|
||||
@PostMapping("/file/del/{uuid}")
|
||||
public boolean del(@PathVariable String uuid) {
|
||||
return fileService.softDel(uuid);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package com.moyz.adi.chat.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.moyz.adi.common.base.ThreadContext;
|
||||
import com.moyz.adi.common.dto.*;
|
||||
import com.moyz.adi.common.entity.Prompt;
|
||||
import com.moyz.adi.common.service.PromptService;
|
||||
import com.moyz.adi.common.util.MPPageUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/prompt")
|
||||
@Validated
|
||||
public class PromptController {
|
||||
|
||||
@Resource
|
||||
private PromptService promptService;
|
||||
|
||||
@Operation(summary = "查询列表")
|
||||
@GetMapping(value = "/my/all")
|
||||
public List<PromptDto> myAll() {
|
||||
return promptService.getAll(ThreadContext.getCurrentUserId());
|
||||
}
|
||||
|
||||
@Operation(summary = "查询列表")
|
||||
@GetMapping(value = "/my/listByUpdateTime")
|
||||
public PromptListResp list(@RequestParam(required = false) LocalDateTime minUpdateTime) {
|
||||
return promptService.listByMinUpdateTime(minUpdateTime);
|
||||
}
|
||||
|
||||
@Operation(summary = "搜索列表")
|
||||
@GetMapping(value = "/my/search")
|
||||
public Page<PromptDto> search(String keyword, @NotNull @Min(1) Integer currentPage, @NotNull @Min(10) Integer pageSize) {
|
||||
return promptService.search(keyword, currentPage, pageSize);
|
||||
}
|
||||
|
||||
@Operation(summary = "自动填充列表")
|
||||
@GetMapping(value = "/my/autocomplete")
|
||||
public List<PromptDto> autocomplete(String keyword) {
|
||||
return promptService.autocomplete(keyword);
|
||||
}
|
||||
|
||||
@Operation(summary = "保存列表")
|
||||
@PostMapping(value = "/save")
|
||||
public Map<String, Long> SavePrompts(@RequestBody PromptsSaveReq savePromptsReq) {
|
||||
return promptService.savePrompts(savePromptsReq);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除")
|
||||
@PostMapping(value = "/del/{id}")
|
||||
public boolean softDelete(@PathVariable Long id) {
|
||||
return promptService.softDelete(id);
|
||||
}
|
||||
|
||||
@Operation(summary = "编辑")
|
||||
@PostMapping(value = "/edit/{id}")
|
||||
public boolean edit(@PathVariable Long id, @RequestBody PromptEditReq promptEditReq) {
|
||||
return promptService.edit(id, promptEditReq.getTitle(), promptEditReq.getRemark());
|
||||
}
|
||||
|
||||
@Operation(summary = "search")
|
||||
@GetMapping(value = "/search")
|
||||
public List<PromptDto> search(@Validated SearchReq searchReq) {
|
||||
return promptService.search(searchReq.getKeyword());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package com.moyz.adi.chat.controller;
|
||||
|
||||
import com.moyz.adi.common.service.UserService;
|
||||
import com.moyz.adi.common.base.ThreadContext;
|
||||
import com.moyz.adi.common.dto.ConfigResp;
|
||||
import com.moyz.adi.common.dto.ModifyPasswordReq;
|
||||
import com.moyz.adi.common.dto.UserUpdateReq;
|
||||
import com.moyz.adi.common.entity.User;
|
||||
import com.talanlabs.avatargenerator.Avatar;
|
||||
import com.talanlabs.avatargenerator.cat.CatAvatar;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
@Tag(name = "用户controller")
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
public class UserController {
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@Operation(summary = "用户信息")
|
||||
@GetMapping("/{uuid}")
|
||||
public void login(@Validated @PathVariable String uuid) {
|
||||
log.info(uuid);
|
||||
}
|
||||
|
||||
@Operation(summary = "配置信息")
|
||||
@GetMapping("/config")
|
||||
public ConfigResp configInfo() {
|
||||
return userService.getConfig();
|
||||
}
|
||||
|
||||
@Operation(summary = "更新信息")
|
||||
@PostMapping("/edit")
|
||||
public void update(@Validated UserUpdateReq userUpdateReq) {
|
||||
userService.updateConfig(userUpdateReq);
|
||||
}
|
||||
|
||||
@Operation(summary = "修改密码")
|
||||
@PostMapping("/password/modify")
|
||||
public String modifyPassword(@RequestBody ModifyPasswordReq modifyPasswordReq) {
|
||||
userService.modifyPassword(modifyPasswordReq.getOldPassword(), modifyPasswordReq.getNewPassword());
|
||||
return "修改成功";
|
||||
}
|
||||
|
||||
@Operation(summary = "退出")
|
||||
@PostMapping("/logout")
|
||||
public void logout() {
|
||||
userService.logout();
|
||||
}
|
||||
|
||||
@Operation(summary = "头像")
|
||||
@GetMapping(value = "/avatar", produces = MediaType.IMAGE_JPEG_VALUE)
|
||||
public void avatar(HttpServletResponse response) {
|
||||
User user = ThreadContext.getCurrentUser();
|
||||
Avatar avatar = CatAvatar.newAvatarBuilder().build();
|
||||
BufferedImage bufferedImage = avatar.create(user.getId());
|
||||
//把图片写给浏览器
|
||||
try {
|
||||
ImageIO.write(bufferedImage, "png", response.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.moyz</groupId>
|
||||
<artifactId>aideepin</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>adi-common</artifactId>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,44 @@
|
|||
package com.moyz.adi.common;
|
||||
|
||||
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
|
||||
import com.baomidou.mybatisplus.generator.config.OutputFile;
|
||||
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
|
||||
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
|
||||
|
||||
import java.sql.Types;
|
||||
import java.util.Collections;
|
||||
|
||||
public class CodeGenerator {
|
||||
public static void main(String[] args) {
|
||||
FastAutoGenerator.create("jdbc:mysql://localhost:3306/aideepin?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&tinyInt1isBit=false&allowMultiQueries=true", "root", "123456")
|
||||
.globalConfig(builder -> {
|
||||
builder.author("moyz") // 设置作者
|
||||
.enableSwagger() // 开启 swagger 模式
|
||||
.fileOverride() // 覆盖已生成文件
|
||||
.outputDir("D://"); // 指定输出目录
|
||||
})
|
||||
.dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
|
||||
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
|
||||
if (typeCode == Types.SMALLINT) {
|
||||
// 自定义类型转换
|
||||
return DbColumnType.INTEGER;
|
||||
}
|
||||
return typeRegistry.getColumnType(metaInfo);
|
||||
|
||||
}))
|
||||
.packageConfig(builder -> {
|
||||
builder.mapper("com.adi.common.mapper")
|
||||
.parent("")
|
||||
.moduleName("")
|
||||
.entity("po")
|
||||
.serviceImpl("service.impl")
|
||||
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://mybatisplus-generatorcode")); // 设置mapperXml生成路径
|
||||
})
|
||||
.strategyConfig(builder -> {
|
||||
builder.addInclude("adi_user,adi_conversation,adi_conversation_message") // 设置需要生成的表名
|
||||
.addTablePrefix("adi_");
|
||||
builder.mapperBuilder().enableBaseResultMap().enableMapperAnnotation().build();
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.moyz.adi.common.annotation;
|
||||
|
||||
import com.moyz.adi.common.validator.AskReqValidator;
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
|
||||
@Constraint(validatedBy = {
|
||||
AskReqValidator.class,
|
||||
})
|
||||
@Target({TYPE, FIELD, PARAMETER, METHOD, CONSTRUCTOR, ANNOTATION_TYPE})
|
||||
@Retention(RUNTIME)
|
||||
@Documented
|
||||
public @interface AskReqCheck {
|
||||
String message() default "dddd";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.moyz.adi.common.annotation;
|
||||
|
||||
import com.moyz.adi.common.validator.CreateImageReqValidator;
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
|
||||
@Constraint(validatedBy = {
|
||||
CreateImageReqValidator.class,
|
||||
})
|
||||
@Target({TYPE, FIELD, PARAMETER, METHOD, CONSTRUCTOR, ANNOTATION_TYPE})
|
||||
@Retention(RUNTIME)
|
||||
@Documented
|
||||
public @interface CreateImageReqCheck {
|
||||
String message() default "dddd";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.moyz.adi.common.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 分布式锁注解
|
||||
*
|
||||
* @author moyz
|
||||
* date:2021-07-15
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface DistributeLock {
|
||||
|
||||
/**
|
||||
* redis key
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String redisKey() default "";
|
||||
|
||||
/**
|
||||
* clientId标识用来加锁的客户端
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String clientId() default "";
|
||||
|
||||
/**
|
||||
* 失效时间(秒)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int expireInSeconds() default 0;
|
||||
|
||||
/**
|
||||
* 如果获取锁失败,是否继续执行
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean continueIfAcquireFail() default true;
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.moyz.adi.common.annotation;
|
||||
|
||||
import com.moyz.adi.common.validator.AskReqValidator;
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@Constraint(validatedBy = {
|
||||
AskReqValidator.class,
|
||||
})
|
||||
@Target({TYPE, FIELD, PARAMETER, METHOD, CONSTRUCTOR, ANNOTATION_TYPE})
|
||||
@Retention(RUNTIME)
|
||||
@Documented
|
||||
public @interface NotAllFieldsEmptyCheck {
|
||||
String message() default "all filed is null";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.moyz.adi.common.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 参数打印注解
|
||||
*
|
||||
* @author moyz
|
||||
* date:2021-07-15
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface ParamsLog {
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.moyz.adi.common.aop;
|
||||
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 打印controller的请求参数
|
||||
*
|
||||
* @author moyz
|
||||
* date:2021-07-15 03:16:59
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class ControllerParamsLogAspect {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ControllerParamsLogAspect.class);
|
||||
|
||||
@Pointcut("execution(public * com.adi.*.controller..*.*(..))")
|
||||
public void controllerMethods() {
|
||||
}
|
||||
|
||||
@Before("controllerMethods()")
|
||||
public void before(JoinPoint joinPoint) {
|
||||
ParamsLogAspect.paramsLog(joinPoint, logger);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package com.moyz.adi.common.aop;
|
||||
|
||||
import com.moyz.adi.common.annotation.DistributeLock;
|
||||
import com.moyz.adi.common.util.RedisTemplateUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
/**
|
||||
* 通用分页式锁
|
||||
*
|
||||
* @author moyz
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
public class DistributeLockAspect {
|
||||
|
||||
@Resource
|
||||
private RedisTemplateUtil redisTemplateUtil;
|
||||
|
||||
@Around("@annotation(distributeLock)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, DistributeLock distributeLock) throws Throwable {
|
||||
String key = distributeLock.redisKey();
|
||||
int expireInSeconds = distributeLock.expireInSeconds();
|
||||
boolean continueIfAcquireFail = distributeLock.continueIfAcquireFail();
|
||||
String clientId = distributeLock.clientId();
|
||||
boolean lockAndContinue = checkAndLock(key, clientId, expireInSeconds, continueIfAcquireFail, redisTemplateUtil);
|
||||
if (!lockAndContinue) {
|
||||
log.warn("该次请求忽略");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return joinPoint.proceed();
|
||||
} finally {
|
||||
boolean unlockResult = redisTemplateUtil.unlock(key, clientId);
|
||||
log.info("unlock:{},key:{},clientId:{}", unlockResult, key, clientId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验参数及加锁,如果没有加锁方标识(clientId),则自动生成uuid做为clientId
|
||||
*
|
||||
* @param key
|
||||
* @param clientId 加锁方标识
|
||||
* @param expireInSeconds 超时时间 (秒)
|
||||
* @param continueIfAcquireFail 获取锁失败是否继续执行后面的业务逻辑
|
||||
* @param redisTemplateUtil redis工具类
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static boolean checkAndLock(String key, String clientId, int expireInSeconds, boolean continueIfAcquireFail, RedisTemplateUtil redisTemplateUtil) throws Exception {
|
||||
log.info("lock info,key:{},clientId:{},expireInSecond:{},continueIfAcquireFail:{}", key, clientId, expireInSeconds, continueIfAcquireFail);
|
||||
if (StringUtils.isBlank(key) || expireInSeconds < 1) {
|
||||
log.warn("加锁参数有误,请确认后再操作");
|
||||
throw new Exception("加锁参数有误,请确认后再操作");
|
||||
}
|
||||
if (StringUtils.isBlank(clientId)) {
|
||||
clientId = UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
boolean lock = redisTemplateUtil.lock(key, clientId, expireInSeconds);
|
||||
if (!lock && !continueIfAcquireFail) {
|
||||
log.warn("由于参数continueIfAcquireFail为false并且获取锁失败,此次请求忽略");
|
||||
return false;
|
||||
}
|
||||
return lock;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.moyz.adi.common.aop;
|
||||
|
||||
import com.moyz.adi.common.annotation.ParamsLog;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
|
||||
/**
|
||||
* @author myz
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
public class ParamsLogAspect {
|
||||
|
||||
@Before(value = "@annotation(paramsLog)")
|
||||
public void before(JoinPoint joinPoint, ParamsLog paramsLog) {
|
||||
paramsLog(joinPoint, log);
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出方法参数到日志
|
||||
*
|
||||
* @param joinPoint joinPoint
|
||||
* @param logger 日志
|
||||
*/
|
||||
static void paramsLog(JoinPoint joinPoint, Logger logger) {
|
||||
String className = joinPoint.getSignature().getDeclaringType().getName();
|
||||
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
|
||||
Object[] args = joinPoint.getArgs();
|
||||
Parameter[] parameters = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameters();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(className);
|
||||
sb.append(".");
|
||||
sb.append(method.getName());
|
||||
sb.append(" params:[");
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String paramName = parameters[i].getName();
|
||||
sb.append(parameters[i].getName());
|
||||
sb.append("=>");
|
||||
if ("password".equals(paramName)) {
|
||||
sb.append("***");
|
||||
} else {
|
||||
sb.append(args[i]);
|
||||
}
|
||||
sb.append(";");
|
||||
}
|
||||
sb.append("]");
|
||||
String log = sb.toString();
|
||||
logger.info(StringUtils.substring(log, 0, 1000));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.moyz.adi.common.base;
|
||||
|
||||
import com.moyz.adi.common.enums.ErrorEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class BaseResponse<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* 是否成功
|
||||
*/
|
||||
private boolean success;
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 提示
|
||||
*/
|
||||
private String message;
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
private T data;
|
||||
|
||||
public BaseResponse() {
|
||||
}
|
||||
|
||||
public BaseResponse(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public BaseResponse(boolean success, T data) {
|
||||
this.data = data;
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public BaseResponse(String code, String message, T data) {
|
||||
this.code = code;
|
||||
this.success = false;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static BaseResponse success(String message){
|
||||
return new BaseResponse(ErrorEnum.SUCCESS.getCode(), message, "");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.moyz.adi.common.base;
|
||||
|
||||
import com.moyz.adi.common.util.JsonUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||
|
||||
@Slf4j
|
||||
@RestControllerAdvice(basePackages = {"com.moyz.adi"})
|
||||
public class ResponseWrapper implements ResponseBodyAdvice<Object> {
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter methodParameter, Class aClass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object beforeBodyWrite(Object result, MethodParameter methodParameter,
|
||||
MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest,
|
||||
ServerHttpResponse serverHttpResponse) {
|
||||
if (result instanceof BaseResponse) {
|
||||
return result;
|
||||
} else if (result instanceof String) {
|
||||
return JsonUtil.toJson(new BaseResponse(true, result));
|
||||
}
|
||||
log.info("result:" + result);
|
||||
return new BaseResponse(true, result);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.moyz.adi.common.base;
|
||||
|
||||
import com.moyz.adi.common.entity.User;
|
||||
|
||||
public class ThreadContext {
|
||||
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
|
||||
private static final ThreadLocal<String> currentToken = new ThreadLocal<>();
|
||||
|
||||
public static void setCurrentUser(User user) {
|
||||
currentUser.set(user);
|
||||
}
|
||||
|
||||
public static User getCurrentUser() {
|
||||
return currentUser.get();
|
||||
}
|
||||
|
||||
public static Long getCurrentUserId() {
|
||||
return currentUser.get().getId();
|
||||
}
|
||||
|
||||
public static void setToken(String token) {
|
||||
currentToken.set(token);
|
||||
}
|
||||
|
||||
|
||||
public static String getToken() {
|
||||
return currentToken.get();
|
||||
}
|
||||
|
||||
|
||||
public static User getExistCurrentUser() {
|
||||
User user = ThreadContext.getCurrentUser();
|
||||
if (null == user) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package com.moyz.adi.common.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
|
||||
import com.moyz.adi.common.util.LocalDateTimeUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.http.client.BufferingClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class BeanConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
log.info("Configuration==create restTemplate");
|
||||
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
|
||||
// 设置建立连接超时时间 毫秒
|
||||
requestFactory.setConnectTimeout(60000);
|
||||
// 设置读取数据超时时间 毫秒
|
||||
requestFactory.setReadTimeout(60000);
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
// 注册LOG拦截器
|
||||
restTemplate.setInterceptors(Lists.newArrayList(new LogClientHttpRequestInterceptor()));
|
||||
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(requestFactory));
|
||||
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
|
||||
log.info("Configuration==create objectMapper");
|
||||
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
|
||||
objectMapper.registerModules(LocalDateTimeUtil.getSimpleModule(), new JavaTimeModule(), new Jdk8Module());
|
||||
//设置null值不参与序列化(字段不被显示)
|
||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
@Bean(name = "mainExecutor")
|
||||
@Primary
|
||||
public AsyncTaskExecutor mainExecutor() {
|
||||
int processorsNum = Runtime.getRuntime().availableProcessors();
|
||||
log.info("mainExecutor,processorsNum:{}", processorsNum);
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(processorsNum * 2);
|
||||
executor.setMaxPoolSize(100);
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Bean(name = "imagesExecutor")
|
||||
public AsyncTaskExecutor imagesExecutor() {
|
||||
int processorsNum = Runtime.getRuntime().availableProcessors();
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
log.info("imagesExecutor corePoolSize:{},maxPoolSize:{}", processorsNum, processorsNum * 2);
|
||||
executor.setCorePoolSize(processorsNum);
|
||||
executor.setMaxPoolSize(processorsNum * 2);
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public SqlSessionFactory sqlSessionFactory(DataSource dataSource)
|
||||
throws Exception {
|
||||
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
|
||||
bean.setDataSource(dataSource);
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 分页插件
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
// 防止全表更新
|
||||
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
|
||||
bean.setPlugins(interceptor);
|
||||
bean.setMapperLocations(
|
||||
new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/*.xml"));
|
||||
return bean.getObject();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package com.moyz.adi.common.config;
|
||||
|
||||
import com.moyz.adi.common.base.BaseResponse;
|
||||
import com.moyz.adi.common.enums.ErrorEnum;
|
||||
import com.moyz.adi.common.exception.BaseException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
/**
|
||||
* 参数校验异常
|
||||
*
|
||||
* @return BaseResponse
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
private BaseResponse handleMethodArgumentNotValidException(
|
||||
final MethodArgumentNotValidException exception) {
|
||||
Map<Object, Object> error = wrapperError(exception.getBindingResult());
|
||||
log.error("参数校验异常:{}", error);
|
||||
return new BaseResponse(ErrorEnum.A_PARAMS_ERROR.getCode(), ErrorEnum.A_PARAMS_ERROR.getInfo(), error);
|
||||
}
|
||||
|
||||
@ExceptionHandler(BaseException.class)
|
||||
private BaseResponse handleBaseException(final BaseException exception) {
|
||||
log.error("拦截业务异常:{}", exception);
|
||||
return new BaseResponse(exception.getCode(), exception.getInfo(), exception.getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* 兜底
|
||||
*
|
||||
* @return BaseResponse
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
private BaseResponse handleException(final Exception exception) {
|
||||
log.error("拦截全局异常:", exception);
|
||||
return new BaseResponse(ErrorEnum.B_GLOBAL_ERROR.getCode(), ErrorEnum.B_GLOBAL_ERROR.getInfo(), exception.getMessage());
|
||||
}
|
||||
|
||||
private Map<Object, Object> wrapperError(BindingResult result) {
|
||||
Map<Object, Object> errorMap = new HashMap<>(5);
|
||||
result.getFieldErrors().forEach(x -> errorMap.put(x.getField(), x.getDefaultMessage()));
|
||||
return errorMap;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package com.moyz.adi.common.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Slf4j
|
||||
public class LogClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
|
||||
ClientHttpRequestExecution execution) throws IOException {
|
||||
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
ClientHttpResponse response = execution.execute(request, body);
|
||||
|
||||
stopWatch.stop();
|
||||
StringBuilder resBody = new StringBuilder();
|
||||
try (BufferedReader bufferedReader = new BufferedReader(
|
||||
new InputStreamReader(response.getBody(), StandardCharsets.UTF_8))) {
|
||||
String line = bufferedReader.readLine();
|
||||
while (line != null) {
|
||||
resBody.append(line);
|
||||
line = bufferedReader.readLine();
|
||||
}
|
||||
}
|
||||
if (request.getHeaders().getContentType() != null && request.getHeaders().getContentType()
|
||||
.includes(MediaType.MULTIPART_FORM_DATA)) {
|
||||
body = new byte[]{};
|
||||
}
|
||||
|
||||
log.info("rest log status:{},time:{},url:{},body:{},response:{}",
|
||||
response.getRawStatusCode(), stopWatch.getLastTaskTimeMillis(),
|
||||
request.getURI(), new String(body, StandardCharsets.UTF_8), resBody);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package com.moyz.adi.common.config;
|
||||
|
||||
import com.moyz.adi.common.helper.HttpHelper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
@Slf4j
|
||||
//@Service
|
||||
public class LogInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
if (handler instanceof HandlerMethod) {
|
||||
HandlerMethod method = (HandlerMethod) handler;
|
||||
if (HttpMethod.GET.matches(request.getMethod())) {
|
||||
log.info("url:{},ip:{},method:{},param:{}", request.getRequestURL(),
|
||||
request.getRemoteAddr(), method.getMethod().getName(),
|
||||
objectMapper.writeValueAsString(request.getParameterMap()));
|
||||
} else {
|
||||
String bodyString = HttpHelper.getBodyString(request);
|
||||
log.info("url:{},ip:{},method:{},param:{},body:{}", request.getRequestURL(),
|
||||
request.getRemoteAddr(), method.getMethod().getName(),
|
||||
objectMapper.writeValueAsString(request.getParameterMap()), bodyString);
|
||||
}
|
||||
} else {
|
||||
log.info("url:{},ip:{}", request.getRequestURL(),
|
||||
request.getRemoteAddr());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
|
||||
ModelAndView modelAndView) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception ex) throws Exception {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.moyz.adi.common.config;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties("openai")
|
||||
@Data
|
||||
public class OpenAiProperties {
|
||||
|
||||
private boolean proxyEnable;
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.moyz.adi.common.config;
|
||||
|
||||
import jakarta.servlet.ReadListener;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* 缓存请求的输入流,以便重复使用
|
||||
*/
|
||||
public class RequestReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private final byte[] body;
|
||||
|
||||
public RequestReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
|
||||
super(request);
|
||||
body = StreamUtils.copyToByteArray(request.getInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader() throws IOException {
|
||||
return new BufferedReader(new InputStreamReader(getInputStream()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
|
||||
final ByteArrayInputStream newInputStream = new ByteArrayInputStream(body);
|
||||
|
||||
return new ServletInputStream() {
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return newInputStream.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return newInputStream.available() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return newInputStream.available() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener readListener) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package com.moyz.adi.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class SpringdocConfig {
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.moyz.adi.common.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
log.info("WebMvcConfig==configureMessageConverters");
|
||||
WebMvcConfigurer.super.configureMessageConverters(converters);
|
||||
converters.add(new StringHttpMessageConverter());
|
||||
converters.add(new MappingJackson2HttpMessageConverter(objectMapper));
|
||||
converters.add(new ByteArrayHttpMessageConverter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
//使用ParamsLogAspect代替
|
||||
// log.info("WebMvcConfig==addInterceptors");
|
||||
// registry.addInterceptor(logInterceptor)
|
||||
// .addPathPatterns("/**")
|
||||
// .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v3/**", "/swagger-ui.html");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package com.moyz.adi.common.cosntant;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AdiConstant {
|
||||
|
||||
public static final int DEFAULT_PAGE_SIZE = 1;
|
||||
|
||||
/**
|
||||
* 验证码id过期时间:1小时
|
||||
*/
|
||||
public static final int AUTH_CAPTCHA_ID_EXPIRE = 1;
|
||||
|
||||
/**
|
||||
* 验证码过期时间,5分钟
|
||||
*/
|
||||
public static final int AUTH_CAPTCHA_EXPIRE = 5;
|
||||
|
||||
/**
|
||||
* 注册激活码有效时长,8小时
|
||||
*/
|
||||
public static final int AUTH_ACTIVE_CODE_EXPIRE = 8;
|
||||
|
||||
/**
|
||||
* token存活时间(8小时)
|
||||
*/
|
||||
public static final int USER_TOKEN_EXPIRE = 8;
|
||||
|
||||
public static final String DEFAULT_PASSWORD = "123456";
|
||||
|
||||
public static final int LOGIN_MAX_FAIL_TIMES = 3;
|
||||
|
||||
public static final String[] WEB_RESOURCES = {
|
||||
"/swagger-ui/index.html",
|
||||
"/swagger-ui",
|
||||
"/swagger-resources",
|
||||
"/v3/api-docs",
|
||||
"/favicon.ico",
|
||||
".css",
|
||||
".js",
|
||||
"/doc.html"
|
||||
};
|
||||
|
||||
public static final int SECRET_KEY_TYPE_SYSTEM = 1;
|
||||
public static final int SECRET_KEY_TYPE_CUSTOM = 2;
|
||||
|
||||
public static final String OPENAI_MESSAGE_DONE_FLAG = "[DONE]";
|
||||
|
||||
public static final String DEFAULT_MODEL = "gpt-3.5-turbo";
|
||||
|
||||
public static final String CREATE_IMAGE_RESP_FORMATS_B64JSON = "b64_json";
|
||||
public static final String OPENAI_CREATE_IMAGE_RESP_FORMATS_URL = "url";
|
||||
|
||||
public static final List<String> OPENAI_CREATE_IMAGE_SIZES = List.of("256x256", "512x512", "1024x1024");
|
||||
|
||||
|
||||
|
||||
public static class GenerateImage{
|
||||
public static final int INTERACTING_METHOD_GENERATE_IMAGE = 1;
|
||||
public static final int INTERACTING_METHOD_EDIT_IMAGE = 2;
|
||||
public static final int INTERACTING_METHOD_VARIATION = 3;
|
||||
|
||||
public static final int STATUS_DOING = 1;
|
||||
public static final int STATUS_FAIL = 2;
|
||||
public static final int STATUS_SUCCESS = 3;
|
||||
}
|
||||
|
||||
public static class SysConfigKey {
|
||||
public static final String SECRET_KEY = "secret_key";
|
||||
public static final String REQUEST_TEXT_RATE_LIMIT = "request_text_rate_limit";
|
||||
public static final String REQUEST_IMAGE_RATE_LIMIT = "request_image_rate_limit";
|
||||
public static final String CONVERSATION_MAX_NUM = "conversation_max_num";
|
||||
public static final String QUOTA_BY_TOKEN_DAILY = "quota_by_token_daily";
|
||||
public static final String QUOTA_BY_TOKEN_MONTHLY = "quota_by_token_monthly";
|
||||
public static final String QUOTA_BY_REQUEST_DAILY = "quota_by_request_daily";
|
||||
public static final String QUOTA_BY_REQUEST_MONTHLY = "quota_by_request_monthly";
|
||||
public static final String QUOTA_BY_IMAGE_DAILY = "quota_by_image_daily";
|
||||
public static final String QUOTA_BY_IMAGE_MONTHLY = "quota_by_image_monthly";
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package com.moyz.adi.common.cosntant;
|
||||
|
||||
public class RedisKeyConstant {
|
||||
|
||||
/**
|
||||
* 账号激活码的key
|
||||
*/
|
||||
public static final String AUTH_ACTIVE_CODE = "auth:activeCode:{0}";
|
||||
|
||||
/**
|
||||
* 注册时使用的验证码
|
||||
* 参数:验证码id
|
||||
* 值:验证码
|
||||
*/
|
||||
public static final String AUTH_REGISTER_CAPTCHA_ID = "auth:register:captcha:{0}";
|
||||
|
||||
/**
|
||||
* 登录时使用的验证码id缓存
|
||||
* 参数:验证码id
|
||||
* 值:验证码
|
||||
*/
|
||||
public static final String AUTH_LOGIN_CAPTCHA_ID = "auth:login:captcha:{0}";
|
||||
|
||||
/**
|
||||
* 注册验证码缓存
|
||||
* 参数:验证码
|
||||
* 值:1
|
||||
*/
|
||||
public static final String AUTH_CAPTCHA = "auth:register:captcha:{0}";
|
||||
|
||||
|
||||
/**
|
||||
* 登录token
|
||||
* {0}:用户token
|
||||
* 值:json.format(user)
|
||||
*/
|
||||
public static final String USER_TOKEN = "user:token:{0}";
|
||||
|
||||
/**
|
||||
* 参数:游客的uuid
|
||||
* 值:json.format(guest)
|
||||
*/
|
||||
public static final String GUEST_UUID = "guest:uuid:{0}";
|
||||
|
||||
/**
|
||||
* 登录失败次数
|
||||
* 参数:用户邮箱
|
||||
* 值: 失效次数
|
||||
*/
|
||||
public static final String LOGIN_FAIL_COUNT = "user:login:fail:{0}";
|
||||
|
||||
/**
|
||||
* 用户是否请求ai中
|
||||
* 参数:用户id
|
||||
* 值: 1或者0
|
||||
*/
|
||||
public static final String USER_ASKING = "user:asking:{0}";
|
||||
|
||||
/**
|
||||
* 用户是否画画中
|
||||
* 参数:用户id
|
||||
* 值: 1或者0
|
||||
*/
|
||||
public static final String USER_DRAWING = "user:drawing:{0}";
|
||||
|
||||
/**
|
||||
* 用户提问限流计数
|
||||
* 参数:用户id
|
||||
* 值: 当前时间窗口访问量
|
||||
*/
|
||||
public static final String USER_REQUEST_TEXT_TIMES = "user:request-text:times:{0}";
|
||||
|
||||
public static final String USER_REQUEST_IMAGE_TIMES = "user:request-image:times:{0}";
|
||||
|
||||
/**
|
||||
* 找回密码的请求绑在
|
||||
* 参数:随机数
|
||||
* 值: 用户id,用于校验后续流程中的重置密码使用
|
||||
*/
|
||||
public static final String FIND_MY_PASSWORD = "user:find:password:{0}";
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class AiImageDto {
|
||||
private Long id;
|
||||
private String uuid;
|
||||
private String prompt;
|
||||
|
||||
private String originalImageUrl;
|
||||
|
||||
private String maskImageUrl;
|
||||
|
||||
private Integer interactingMethod;
|
||||
|
||||
@JsonIgnore
|
||||
private String generatedImages;
|
||||
/**
|
||||
* http url
|
||||
*/
|
||||
private List<String> imageUrlList;
|
||||
private Integer processStatus;
|
||||
private LocalDateTime createTime;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class AiImagesListResp {
|
||||
private Long minId;
|
||||
private List<AiImageDto> imageItems;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import com.moyz.adi.common.annotation.AskReqCheck;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
@Schema(description = "对话的请求对象")
|
||||
@Data
|
||||
@AskReqCheck
|
||||
public class AskReq {
|
||||
|
||||
@Length(min = 32, max = 32)
|
||||
private String conversationUuid;
|
||||
|
||||
private String parentMessageId;
|
||||
|
||||
private String prompt;
|
||||
|
||||
/**
|
||||
* If not empty, it means will request AI with the exist prompt, param {@code prompt} is ignored
|
||||
*/
|
||||
private String regenerateQuestionUuid;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ConfigResp {
|
||||
private String secretKey;
|
||||
private Boolean contextEnable;
|
||||
private Integer contextMsgPairNum;
|
||||
private Integer quotaByTokenDaily;
|
||||
private Integer quotaByTokenMonthly;
|
||||
private Integer quotaByRequestDaily;
|
||||
private Integer quotaByRequestMonthly;
|
||||
private Integer quotaByImageDaily;
|
||||
private Integer quotaByImageMonthly;
|
||||
private Integer todayTokenCost;
|
||||
private Integer todayRequestTimes;
|
||||
private Integer todayGeneratedImageNumber;
|
||||
private Integer currMonthTokenCost;
|
||||
private Integer currMonthRequestTimes;
|
||||
private Integer currMonthGeneratedImageNumber;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ConvDto {
|
||||
|
||||
private String uuid;
|
||||
|
||||
@NotBlank
|
||||
private String title;
|
||||
|
||||
private Integer tokens;
|
||||
|
||||
@Schema(title = "set the system message to ai, ig: you are a lawyer")
|
||||
private String aiSystemMessage;
|
||||
|
||||
private Boolean understandContextEnable;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ConvEditReq {
|
||||
|
||||
@NotBlank
|
||||
private String title;
|
||||
|
||||
@Schema(title = "set the system message to ai, ig: you are a lawyer")
|
||||
private String aiSystemMessage;
|
||||
|
||||
private Boolean understandContextEnable;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ConvMsgListResp {
|
||||
|
||||
private String minMsgUuid;
|
||||
|
||||
private List<ConvMsgResp> msgList;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ConvMsgResp {
|
||||
|
||||
@JsonIgnore
|
||||
private Long id;
|
||||
|
||||
@Schema(title = "消息的uuid")
|
||||
private String uuid;
|
||||
|
||||
@Schema(title = "父级消息id")
|
||||
private Long parentMessageId;
|
||||
|
||||
@Schema(title = "对话的消息")
|
||||
@TableField("content")
|
||||
private String content;
|
||||
|
||||
@Schema(title = "产生该消息的角色:1: 用户,2:系统,3:助手")
|
||||
private String messageRole;
|
||||
|
||||
@Schema(title = "消耗的token数量")
|
||||
private Integer tokens;
|
||||
|
||||
@Schema(title = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(title = "子级消息(一般指的是AI的响应)")
|
||||
private List<ConvMsgResp> children;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CreateImageDto {
|
||||
private String prompt;
|
||||
private String size;
|
||||
private int number;
|
||||
private int interactingMethod;
|
||||
private String originalImage;
|
||||
private String maskImage;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
@Data
|
||||
public class EditImageReq {
|
||||
@Length(min = 32, max = 32)
|
||||
private String originalImage;
|
||||
@Length(min = 32, max = 32)
|
||||
private String maskImage;
|
||||
@NotBlank
|
||||
private String prompt;
|
||||
@NotBlank
|
||||
private String size;
|
||||
@Min(1)
|
||||
@Max(10)
|
||||
private int number;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GenerateImageReq {
|
||||
@NotBlank
|
||||
private String prompt;
|
||||
@NotBlank
|
||||
private String size;
|
||||
@Min(1)
|
||||
@Max(10)
|
||||
private int number;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ImagesReq {
|
||||
private Integer generateStatus;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(name = "登录请求参数")
|
||||
@Data
|
||||
public class LoginReq {
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
String email;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
String password;
|
||||
|
||||
String captchaId;
|
||||
|
||||
String captchaCode;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class LoginResp {
|
||||
|
||||
private String token;
|
||||
private String name;
|
||||
private String email;
|
||||
private String activeTime;
|
||||
private String captchaId;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@Data
|
||||
@Validated
|
||||
public class ModifyPasswordReq {
|
||||
|
||||
@NotBlank
|
||||
@Length(min = 6)
|
||||
private String oldPassword;
|
||||
|
||||
@NotBlank
|
||||
@Length(min = 6)
|
||||
private String newPassword;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PromptDto {
|
||||
private Long id;
|
||||
private String act;
|
||||
private String prompt;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@Data
|
||||
@Validated
|
||||
public class PromptEditReq {
|
||||
|
||||
@NotBlank
|
||||
private String title;
|
||||
|
||||
@NotBlank
|
||||
private String remark;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class PromptListResp {
|
||||
private String maxUpdateTime;
|
||||
private List<PromptDto> prompts;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Validated
|
||||
public class PromptsSaveReq {
|
||||
|
||||
@Length(min = 1)
|
||||
private List<PromptDto> prompts;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RegenerateImageReq {
|
||||
private String uuid;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@Schema(name = "注册请求参数")
|
||||
@Data
|
||||
@Validated
|
||||
public class RegisterReq {
|
||||
|
||||
@Parameter(description = "邮箱")
|
||||
@Email
|
||||
private String email;
|
||||
|
||||
@Parameter(description = "密码")
|
||||
@Min(6)
|
||||
private String password;
|
||||
|
||||
@Parameter(description = "验证码ID")
|
||||
@Length(min = 32)
|
||||
private String captchaId;
|
||||
|
||||
@Parameter(description = "验证码")
|
||||
@Length(min = 4, max = 4)
|
||||
private String captchaCode;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SearchReq {
|
||||
|
||||
@NotBlank
|
||||
private String keyword;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SessionResp {
|
||||
private Boolean auth;
|
||||
|
||||
private String model;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import com.moyz.adi.common.annotation.NotAllFieldsEmptyCheck;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@NotAllFieldsEmptyCheck
|
||||
public class UserUpdateReq {
|
||||
private String secretKey;
|
||||
private Boolean contextEnable;
|
||||
private Integer contextMsgPairNum;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
@Data
|
||||
public class VariationImageReq {
|
||||
@Length(min = 32, max = 32)
|
||||
private String originalImage;
|
||||
@NotBlank
|
||||
private String size;
|
||||
@Min(1)
|
||||
@Max(10)
|
||||
private int number;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.moyz.adi.common.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class VerifyResp {
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.moyz.adi.common.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("adi_file")
|
||||
@Schema(title = "文件表")
|
||||
public class AdiFile extends BaseEntity {
|
||||
@Schema(title = "用户id")
|
||||
@TableField(value = "user_id")
|
||||
private Long userId;
|
||||
|
||||
@Schema(title = "name")
|
||||
@TableField(value = "name")
|
||||
private String name;
|
||||
|
||||
@Schema(title = "uuid")
|
||||
@TableField(value = "uuid")
|
||||
private String uuid;
|
||||
|
||||
@Schema(title = "md5")
|
||||
@TableField(value = "md5")
|
||||
private String md5;
|
||||
|
||||
@Schema(title = "file extension")
|
||||
@TableField(value = "ext")
|
||||
private String ext;
|
||||
|
||||
@Schema(title = "路径")
|
||||
@TableField(value = "path")
|
||||
private String path;
|
||||
|
||||
@Schema(title = "引用数量")
|
||||
@TableField(value = "ref_count")
|
||||
private Integer refCount;
|
||||
|
||||
@Schema(title = "是否删除(0:未删除,1:已删除)")
|
||||
@TableField(value = "is_delete")
|
||||
private Boolean isDelete;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.moyz.adi.common.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("adi_ai_image")
|
||||
@Schema(title = "ai images", description = "Image generated by ai")
|
||||
public class AiImage extends BaseEntity {
|
||||
|
||||
@TableField("user_id")
|
||||
private Long userId;
|
||||
|
||||
@TableField("uuid")
|
||||
private String uuid;
|
||||
|
||||
@TableField("prompt")
|
||||
private String prompt;
|
||||
|
||||
@TableField("generate_size")
|
||||
private String generateSize;
|
||||
|
||||
@TableField("generate_number")
|
||||
private Integer generateNumber;
|
||||
|
||||
@Schema(title = "file uuid")
|
||||
@TableField("original_image")
|
||||
private String originalImage;
|
||||
|
||||
@Schema(title = "file uuid")
|
||||
@TableField("mask_image")
|
||||
private String maskImage;
|
||||
|
||||
@TableField("resp_images_path")
|
||||
private String respImagesPath;
|
||||
|
||||
@Schema(title = "generated image uuids")
|
||||
@TableField("generated_images")
|
||||
private String generatedImages;
|
||||
|
||||
@TableField("interacting_method")
|
||||
private Integer interactingMethod;
|
||||
|
||||
@TableField("process_status")
|
||||
private Integer processStatus;
|
||||
|
||||
@TableField("is_delete")
|
||||
private Boolean isDelete;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.moyz.adi.common.entity;
|
||||
|
||||
import com.moyz.adi.common.enums.AiModelStatus;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("adi_ai_model")
|
||||
@Schema(title = "AiModel对象", description = "AI模型表")
|
||||
public class AiModel extends BaseEntity {
|
||||
|
||||
@Schema(title = "模型名称")
|
||||
@TableField("name")
|
||||
private String name;
|
||||
|
||||
@Schema(title = "说明")
|
||||
@TableField("remark")
|
||||
private String remark;
|
||||
|
||||
@Schema(title = "状态(1:正常使用,2:不可用)")
|
||||
@TableField("model_status")
|
||||
private AiModelStatus modelStatus;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.moyz.adi.common.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class BaseEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@TableField(value = "create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@TableField(value = "update_time")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.moyz.adi.common.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 会话表
|
||||
* </p>
|
||||
*
|
||||
* @author moyz
|
||||
* @since 2023-04-11
|
||||
*/
|
||||
@Data
|
||||
@TableName("adi_conversation")
|
||||
@Schema(title = "对话实体", description = "对话表")
|
||||
public class Conversation extends BaseEntity {
|
||||
|
||||
@Schema(title = "用户id")
|
||||
@TableField("user_id")
|
||||
private Long userId;
|
||||
|
||||
@Schema(title = "对话uuid")
|
||||
@TableField("uuid")
|
||||
private String uuid;
|
||||
|
||||
@Schema(title = "会话标题")
|
||||
@TableField("title")
|
||||
private String title;
|
||||
|
||||
@Schema(title = "消耗的token数量")
|
||||
@TableField("tokens")
|
||||
private Integer tokens;
|
||||
|
||||
@Schema(title = "ai model name")
|
||||
@TableField("ai_model")
|
||||
private String aiModel;
|
||||
|
||||
@Schema(name = "是否开启理解上下文的功能")
|
||||
@TableField("understand_context_enable")
|
||||
private Boolean understandContextEnable;
|
||||
|
||||
@Schema(title = "set the system message to ai, ig: you are a lawyer")
|
||||
@TableField("ai_system_message")
|
||||
private String aiSystemMessage;
|
||||
|
||||
@TableField(value = "is_delete")
|
||||
private Boolean isDelete;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package com.moyz.adi.common.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
*
|
||||
* </p>
|
||||
*
|
||||
* @author moyz
|
||||
* @since 2023-04-11
|
||||
*/
|
||||
@Data
|
||||
@TableName("adi_conversation_message")
|
||||
@Schema(title = "ConversationMessage对象")
|
||||
public class ConversationMessage extends BaseEntity {
|
||||
|
||||
@Schema(title = "消息的uuid")
|
||||
@TableField("uuid")
|
||||
private String uuid;
|
||||
|
||||
@Schema(title = "父级消息id")
|
||||
@TableField("parent_message_id")
|
||||
private Long parentMessageId;
|
||||
|
||||
@Schema(title = "对话id")
|
||||
@TableField("conversation_id")
|
||||
private Long conversationId;
|
||||
|
||||
@Schema(title = "对话uuid")
|
||||
@TableField("conversation_uuid")
|
||||
private String conversationUuid;
|
||||
|
||||
@Schema(title = "用户id")
|
||||
@TableField("user_id")
|
||||
private Long userId;
|
||||
|
||||
@Schema(title = "对话的消息")
|
||||
@TableField("content")
|
||||
private String content;
|
||||
|
||||
@Schema(title = "产生该消息的角色:1: 用户,2:系统,3:助手")
|
||||
@TableField("message_role")
|
||||
private String messageRole;
|
||||
|
||||
@Schema(title = "消耗的token数量")
|
||||
@TableField("tokens")
|
||||
private Integer tokens;
|
||||
|
||||
@Schema(title = "secret key type(1:system secret key,2:custom secret key)")
|
||||
@TableField(value = "secret_key_type")
|
||||
private Integer secretKeyType;
|
||||
|
||||
@Schema(name = "上下文理解中携带的消息对数量(提示词及回复)")
|
||||
@TableField("understand_context_msg_pair_num")
|
||||
private Integer understandContextMsgPairNum;
|
||||
|
||||
@TableField(value = "is_delete")
|
||||
private Boolean isDelete;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.moyz.adi.common.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("adi_prompt")
|
||||
@Schema(title = "提示词实体")
|
||||
public class Prompt extends BaseEntity {
|
||||
|
||||
@Schema(title = "用户id")
|
||||
@TableField(value = "user_id")
|
||||
private Long userId;
|
||||
|
||||
@Schema(title = "标题")
|
||||
@TableField(value = "act")
|
||||
private String act;
|
||||
|
||||
@Schema(title = "内容")
|
||||
@TableField(value = "prompt")
|
||||
private String prompt;
|
||||
|
||||
@Schema(title = "是否删除(0:未删除,1:已删除)")
|
||||
@TableField(value = "is_delete")
|
||||
private Boolean isDelete;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.moyz.adi.common.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("adi_sys_config")
|
||||
@Schema(title = "系统配置表")
|
||||
public class SysConfig extends BaseEntity {
|
||||
|
||||
@Schema(title = "配置名称")
|
||||
@TableField("name")
|
||||
private String name;
|
||||
|
||||
@Schema(title = "配置项的值")
|
||||
private String value;
|
||||
|
||||
@Schema(title = "是否删除(0:未删除,1:已删除)")
|
||||
@TableField(value = "is_delete")
|
||||
private Boolean isDelete;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.moyz.adi.common.entity;
|
||||
|
||||
import com.moyz.adi.common.enums.UserStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("adi_user")
|
||||
@Schema(title = "User对象")
|
||||
public class User extends BaseEntity {
|
||||
|
||||
@Schema(name = "用户名称")
|
||||
@TableField("name")
|
||||
private String name;
|
||||
|
||||
@TableField("email")
|
||||
private String email;
|
||||
|
||||
@TableField("password")
|
||||
private String password;
|
||||
|
||||
@TableField("uuid")
|
||||
private String uuid;
|
||||
|
||||
@Schema(name = "openai secret key")
|
||||
@TableField("secret_key")
|
||||
private String secretKey;
|
||||
|
||||
@Schema(name = "上下文理解中需要携带的消息对数量(提示词及回复)")
|
||||
@TableField("understand_context_msg_pair_num")
|
||||
private Integer understandContextMsgPairNum;
|
||||
|
||||
@Schema(name = "token quota in one day")
|
||||
@TableField("quota_by_token_daily")
|
||||
private Integer quotaByTokenDaily;
|
||||
|
||||
@Schema(name = "token quota in one month")
|
||||
@TableField("quota_by_token_monthly")
|
||||
private Integer quotaByTokenMonthly;
|
||||
|
||||
@Schema(name = "request quota in one day")
|
||||
@TableField("quota_by_request_daily")
|
||||
private Integer quotaByRequestDaily;
|
||||
|
||||
@Schema(name = "request quota in one month")
|
||||
@TableField("quota_by_request_monthly")
|
||||
private Integer quotaByRequestMonthly;
|
||||
|
||||
@TableField("quota_by_image_daily")
|
||||
private Integer quotaByImageDaily;
|
||||
|
||||
@TableField("quota_by_image_monthly")
|
||||
private Integer quotaByImageMonthly;
|
||||
|
||||
@TableField("user_status")
|
||||
private UserStatusEnum userStatus;
|
||||
|
||||
@TableField("active_time")
|
||||
private LocalDateTime activeTime;
|
||||
|
||||
@TableField("is_delete")
|
||||
private Boolean isDelete;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.moyz.adi.common.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("adi_user_day_cost")
|
||||
@Schema(title = "用户每天使用量")
|
||||
public class UserDayCost extends BaseEntity {
|
||||
@Schema(title = "用户id")
|
||||
@TableField(value = "user_id")
|
||||
private Long userId;
|
||||
|
||||
@Schema(title = "日期")
|
||||
@TableField(value = "day")
|
||||
private Integer day;
|
||||
|
||||
@Schema(title = "请求量")
|
||||
@TableField(value = "requests")
|
||||
private Integer requests;
|
||||
|
||||
@Schema(title = "token数量")
|
||||
@TableField(value = "tokens")
|
||||
private Integer tokens;
|
||||
|
||||
@Schema(title = "The number of generated images")
|
||||
@TableField(value = "images_number")
|
||||
private Integer imagesNumber;
|
||||
|
||||
@Schema(title = "secret key type(1:system secret key,2:custom secret key)")
|
||||
@TableField(value = "secret_key_type")
|
||||
private Integer secretKeyType;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.moyz.adi.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AiModelStatus implements BaseEnum {
|
||||
ACTIVE(1, "启用"),
|
||||
INACTIVE(2, "停用");
|
||||
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.moyz.adi.common.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IEnum;
|
||||
|
||||
public interface BaseEnum extends IEnum<Integer> {
|
||||
/**
|
||||
* 获取对应名称
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String getDesc();
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.moyz.adi.common.enums;
|
||||
|
||||
public enum ErrorEnum {
|
||||
SUCCESS("00000", "成功"),
|
||||
A_URL_NOT_FOUND("A0001", "地址不存在"),
|
||||
A_PARAMS_ERROR("A0002", "参数校验不通过"),
|
||||
A_REQUEST_TOO_MUCH("A0003", "访问次数太多"),
|
||||
A_LOGIN_ERROR("A0004", "登陆失败,账号或密码错误"),
|
||||
A_LOGIN_ERROR_MAX("A0005", "失败次数太多,请输入验证码重试"),
|
||||
A_LOGIN_CAPTCHA_ERROR("A0006", "验证码不正确"),
|
||||
A_USER_NOT_EXIST("A0007", "用户不存在"),
|
||||
A_CONVERSATION_NOT_EXIST("A0008", "对话不存在"),
|
||||
A_IMAGE_NUMBER_ERROR("A0009", "图片数量不对"),
|
||||
A_IMAGE_SIZE_ERROR("A0010", "图片尺寸不对"),
|
||||
A_FILE_NOT_EXIST("A0011", "文件不存在"),
|
||||
A_DRAWING("A0012", "作图还未完成"),
|
||||
A_REGISTER_USER_EXIST("A0013", "账号已经存在,请使用账号密码登录"),
|
||||
A_FIND_PASSWORD_CODE_ERROR("A0014", "重置码已过期或不存在"),
|
||||
A_USER_WAIT_CONFIRM("A0015", "用户未激活"),
|
||||
B_UNCAUGHT_ERROR("B0001", "未捕捉异常"),
|
||||
B_COMMON_ERROR("B0002", "业务出错"),
|
||||
B_GLOBAL_ERROR("B0003", "全局异常"),
|
||||
B_SAVE_IMAGE_ERROR("B0004", "保存图片异常"),
|
||||
B_FIND_IMAGE_404("B0005", "无法找到图片"),
|
||||
B_DAILY_QUOTA_USED("B0006", "今天额度已经用完"),
|
||||
B_MONTHLY_QUOTA_USED("B0007", "当月额度已经用完"),
|
||||
|
||||
B_MESSAGE_NOT_FOUND("B0008", "消息不存在");
|
||||
|
||||
private String code;
|
||||
private String info;
|
||||
|
||||
ErrorEnum(String code, String info) {
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public static ErrorEnum getErrorEnum(String code) {
|
||||
ErrorEnum result = null;
|
||||
for (ErrorEnum c : ErrorEnum.values()) {
|
||||
if (c.getCode().equals(code)) {
|
||||
result = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (null == result) {
|
||||
result = B_COMMON_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
public void setInfo(String info) {
|
||||
this.info = info;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.moyz.adi.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum UserStatusEnum implements BaseEnum {
|
||||
|
||||
WAIT_CONFIRM(1, "待验证"),
|
||||
NORMAL(2, "正常"),
|
||||
FREEZE(3, "冻结");
|
||||
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package com.moyz.adi.common.exception;
|
||||
|
||||
|
||||
import com.moyz.adi.common.enums.ErrorEnum;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
public class BaseException extends RuntimeException {
|
||||
private String code;
|
||||
private String info;
|
||||
|
||||
private Object data;
|
||||
|
||||
public BaseException(String code, String info) {
|
||||
super(code + ":" + info);
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public BaseException(ErrorEnum errorEnum, String... infoValues) {
|
||||
super(errorEnum.getCode() + ":" + MessageFormat.format(errorEnum.getInfo(), infoValues));
|
||||
this.code = errorEnum.getCode();
|
||||
if (infoValues.length > 0) {
|
||||
this.info = MessageFormat.format(errorEnum.getInfo(), infoValues);
|
||||
} else {
|
||||
this.info = errorEnum.getInfo();
|
||||
}
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
public void setInfo(String info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
if (null != data) {
|
||||
return data;
|
||||
}
|
||||
return getMessage();
|
||||
}
|
||||
|
||||
public BaseException setData(Object data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.moyz.adi.common.filter;
|
||||
|
||||
import com.moyz.adi.common.config.RequestReaderHttpServletRequestWrapper;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 过滤器
|
||||
* 当前主要是配合LogInterceptor使用,已使用ControllerParamsLogAspect代替LogInterceptor
|
||||
*/
|
||||
@Slf4j
|
||||
//@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class HttpServletRequestReplacedFilter extends OncePerRequestFilter {
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
log.info("HttpServletRequestReplacedFilter:" + request.getRequestURI());
|
||||
ServletRequest requestWrapper = new RequestReaderHttpServletRequestWrapper(request);
|
||||
filterChain.doFilter(requestWrapper, response);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package com.moyz.adi.common.filter;
|
||||
|
||||
import com.moyz.adi.common.base.ThreadContext;
|
||||
import com.moyz.adi.common.cosntant.AdiConstant;
|
||||
import com.moyz.adi.common.cosntant.RedisKeyConstant;
|
||||
import com.moyz.adi.common.entity.User;
|
||||
import com.moyz.adi.common.util.JsonUtil;
|
||||
import io.micrometer.common.util.StringUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TokenFilter extends OncePerRequestFilter {
|
||||
|
||||
public static final String[] EXCLUDE_API = {
|
||||
"/auth/",
|
||||
};
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Value("${server.servlet.context-path:}")
|
||||
private String contextPath;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
String requestUri = request.getRequestURI();
|
||||
if (excludePath(requestUri)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
if (null == request.getCookies()) {
|
||||
log.warn("未授权:{}", requestUri);
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
Cookie cookie = Arrays.stream(request.getCookies()).filter(item -> item.getName().equals(AUTHORIZATION)).findFirst().orElse(null);
|
||||
if (null == cookie || StringUtils.isBlank(cookie.getValue())) {
|
||||
log.warn("未授权:{}", requestUri);
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
String token = cookie.getValue();
|
||||
String tokenKey = MessageFormat.format(RedisKeyConstant.USER_TOKEN, token);
|
||||
String userJson = stringRedisTemplate.opsForValue().get(tokenKey);
|
||||
if (StringUtils.isBlank(userJson)) {
|
||||
log.warn("未登录:{}", requestUri);
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
User user = JsonUtil.fromJson(userJson, User.class);
|
||||
if (null == user) {
|
||||
log.warn("用户不存在:{}", requestUri);
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
ThreadContext.setCurrentUser(user);
|
||||
ThreadContext.setToken(token);
|
||||
log.info("response::" + response);
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private boolean excludePath(String requestUri) {
|
||||
for (String path : EXCLUDE_API) {
|
||||
if (requestUri.startsWith(contextPath + path)) {
|
||||
// log.info("path exclude{}", requestUri);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (String path : AdiConstant.WEB_RESOURCES) {
|
||||
if (requestUri.startsWith(contextPath + path) || requestUri.endsWith(path)) {
|
||||
// log.info("path exclude{}", requestUri);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package com.moyz.adi.common.helper;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AdiMailSender {
|
||||
|
||||
@Resource
|
||||
private JavaMailSender javaMailSender;
|
||||
|
||||
@Value("${spring.mail.username}")
|
||||
private String from;
|
||||
|
||||
public void send(String subject, String content, String to) {
|
||||
log.info("mail sender:{}", from);
|
||||
if (StringUtils.isAnyBlank(from, to)) {
|
||||
return;
|
||||
}
|
||||
MimeMessage message = javaMailSender.createMimeMessage();
|
||||
try {
|
||||
MimeMessageHelper helper = new MimeMessageHelper(message, true);
|
||||
// 设置发件人名称和地址
|
||||
InternetAddress fromAddress = new InternetAddress(from, "AIDeepIn");
|
||||
helper.setFrom(fromAddress);
|
||||
|
||||
// 设置收件人、主题、内容等其他信息
|
||||
helper.setTo(to);
|
||||
helper.setSubject(subject);
|
||||
helper.setText(content);
|
||||
|
||||
javaMailSender.send(message);
|
||||
} catch (Exception e) {
|
||||
log.error("发送邮件时发生异常", e);
|
||||
}
|
||||
// SimpleMailMessage message = new SimpleMailMessage();
|
||||
// message.setFrom(from);
|
||||
// message.setTo(to);
|
||||
// message.setSubject(subject);
|
||||
// message.setText(content);
|
||||
// try {
|
||||
// javaMailSender.send(message);
|
||||
// } catch (Exception e) {
|
||||
// log.error("发送邮件时发生异常", e);
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.moyz.adi.common.helper;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
@Slf4j
|
||||
public class HttpHelper {
|
||||
public static String getBodyString(HttpServletRequest request) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
InputStream inputStream = null;
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
inputStream = request.getInputStream();
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
|
||||
String line = "";
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("error", e);
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
log.error("error", e);
|
||||
}
|
||||
}
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
log.error("error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,231 @@
|
|||
package com.moyz.adi.common.helper;
|
||||
|
||||
import com.didalgo.gpt3.ChatFormatDescriptor;
|
||||
import com.didalgo.gpt3.Encoding;
|
||||
import com.didalgo.gpt3.GPT3Tokenizer;
|
||||
import com.didalgo.gpt3.TokenCount;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.moyz.adi.common.cosntant.AdiConstant;
|
||||
import com.moyz.adi.common.entity.AiImage;
|
||||
import com.moyz.adi.common.entity.User;
|
||||
import com.moyz.adi.common.enums.ErrorEnum;
|
||||
import com.moyz.adi.common.exception.BaseException;
|
||||
import com.moyz.adi.common.model.AnswerMeta;
|
||||
import com.moyz.adi.common.model.ChatMeta;
|
||||
import com.moyz.adi.common.model.QuestionMeta;
|
||||
import com.moyz.adi.common.service.FileService;
|
||||
import com.moyz.adi.common.service.SysConfigService;
|
||||
import com.moyz.adi.common.util.FileUtil;
|
||||
import com.moyz.adi.common.util.ImageUtil;
|
||||
import com.moyz.adi.common.util.JsonUtil;
|
||||
import com.moyz.adi.common.util.TriConsumer;
|
||||
import com.theokanning.openai.OpenAiApi;
|
||||
import com.theokanning.openai.completion.chat.ChatCompletionChoice;
|
||||
import com.theokanning.openai.completion.chat.ChatCompletionRequest;
|
||||
import com.theokanning.openai.completion.chat.ChatMessage;
|
||||
import com.theokanning.openai.image.*;
|
||||
import com.theokanning.openai.service.OpenAiService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.moyz.adi.common.cosntant.AdiConstant.OPENAI_CREATE_IMAGE_RESP_FORMATS_URL;
|
||||
import static com.moyz.adi.common.cosntant.AdiConstant.OPENAI_CREATE_IMAGE_SIZES;
|
||||
import static com.theokanning.openai.service.OpenAiService.defaultClient;
|
||||
import static com.theokanning.openai.service.OpenAiService.defaultRetrofit;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class OpenAiHelper {
|
||||
|
||||
@Value("${openai.proxy.enable:false}")
|
||||
private boolean proxyEnable;
|
||||
|
||||
@Value("${openai.proxy.host:0}")
|
||||
private String proxyHost;
|
||||
|
||||
@Value("${openai.proxy.http-port:0}")
|
||||
private int proxyHttpPort;
|
||||
|
||||
@Value("${local.images}")
|
||||
private String localImagesPath;
|
||||
|
||||
@Resource
|
||||
private FileService fileService;
|
||||
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
public OpenAiService getOpenAiService(User user) {
|
||||
String secretKey = SysConfigService.getSecretKey();
|
||||
String userSecretKey = user.getSecretKey();
|
||||
if (StringUtils.isNotBlank(userSecretKey)) {
|
||||
secretKey = userSecretKey;
|
||||
}
|
||||
if (proxyEnable) {
|
||||
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyHttpPort));
|
||||
OkHttpClient client = defaultClient(secretKey, Duration.of(60, ChronoUnit.SECONDS))
|
||||
.newBuilder()
|
||||
.proxy(proxy)
|
||||
.build();
|
||||
Retrofit retrofit = defaultRetrofit(client, objectMapper);
|
||||
OpenAiApi api = retrofit.create(OpenAiApi.class);
|
||||
return new OpenAiService(api);
|
||||
}
|
||||
return new OpenAiService(secretKey, Duration.of(60, ChronoUnit.SECONDS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send http request to openai server <br/>
|
||||
* Calculate token
|
||||
*
|
||||
* @param user
|
||||
* @param regenerateQuestionUuid
|
||||
* @param chatMessageList
|
||||
* @param sseEmitter
|
||||
* @param consumer
|
||||
*/
|
||||
public void sseAsk(User user, String regenerateQuestionUuid, List<ChatMessage> chatMessageList, SseEmitter sseEmitter, TriConsumer<String, QuestionMeta, AnswerMeta> consumer) {
|
||||
final int[] answerTokens = {0};
|
||||
StringBuilder response = new StringBuilder();
|
||||
OpenAiService service = getOpenAiService(user);
|
||||
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest
|
||||
.builder()
|
||||
.model(AdiConstant.DEFAULT_MODEL)
|
||||
.messages(chatMessageList)
|
||||
.n(1)
|
||||
.logitBias(new HashMap<>())
|
||||
.build();
|
||||
service.streamChatCompletion(chatCompletionRequest)
|
||||
.doOnError(onError -> {
|
||||
log.error("openai error", onError);
|
||||
sseEmitter.send(SseEmitter.event().name("error").data(onError.getMessage()));
|
||||
sseEmitter.complete();
|
||||
}).subscribe(completionChunk -> {
|
||||
answerTokens[0]++;
|
||||
List<ChatCompletionChoice> choices = completionChunk.getChoices();
|
||||
String content = choices.get(0).getMessage().getContent();
|
||||
log.info("get content:{}", content);
|
||||
if (null == content && response.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (null == content || AdiConstant.OPENAI_MESSAGE_DONE_FLAG.equals(content)) {
|
||||
log.info("OpenAI返回数据结束了");
|
||||
sseEmitter.send(AdiConstant.OPENAI_MESSAGE_DONE_FLAG);
|
||||
|
||||
|
||||
GPT3Tokenizer tokenizer = new GPT3Tokenizer(Encoding.CL100K_BASE);
|
||||
int questionTokens = 0;
|
||||
try {
|
||||
questionTokens = TokenCount.fromMessages(chatMessageList, tokenizer, ChatFormatDescriptor.forModel(AdiConstant.DEFAULT_MODEL));
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.error("该模型的token无法统计,model:{}", AdiConstant.DEFAULT_MODEL);
|
||||
}
|
||||
System.out.println("requestTokens:" + questionTokens);
|
||||
System.out.println("返回内容:" + response);
|
||||
|
||||
String questionUuid = StringUtils.isNotBlank(regenerateQuestionUuid) ? regenerateQuestionUuid : UUID.randomUUID().toString().replace("-", "");
|
||||
QuestionMeta questionMeta = new QuestionMeta(questionTokens, questionUuid);
|
||||
AnswerMeta answerMeta = new AnswerMeta(answerTokens[0], UUID.randomUUID().toString().replace("-", ""));
|
||||
ChatMeta chatMeta = new ChatMeta(questionMeta, answerMeta);
|
||||
// String meta = JsonUtil.toJson(chatMeta).replaceAll("\r\n", "");
|
||||
String meta = JsonUtil.toJson(chatMeta).replaceAll("\r\n", "");
|
||||
log.info("meta:" + meta);
|
||||
sseEmitter.send(" [META]" + meta);
|
||||
// close eventSourceEmitter after tokens was calculated
|
||||
sseEmitter.complete();
|
||||
consumer.accept(response.toString(), questionMeta, answerMeta);
|
||||
return;
|
||||
}
|
||||
//加空格配合前端的fetchEventSource进行解析,见https://github.com/Azure/fetch-event-source/blob/45ac3cfffd30b05b79fbf95c21e67d4ef59aa56a/src/parse.ts#L129-L133
|
||||
sseEmitter.send(" " + content);
|
||||
response.append(content);
|
||||
});
|
||||
|
||||
|
||||
System.out.println("返回内容1111:" + response);
|
||||
}
|
||||
|
||||
public List<Image> createImage(User user, AiImage aiImage) {
|
||||
if (aiImage.getGenerateNumber() < 1 || aiImage.getGenerateNumber() > 10) {
|
||||
throw new BaseException(ErrorEnum.A_IMAGE_NUMBER_ERROR);
|
||||
}
|
||||
if (!OPENAI_CREATE_IMAGE_SIZES.contains(aiImage.getGenerateSize())) {
|
||||
throw new BaseException(ErrorEnum.A_IMAGE_SIZE_ERROR);
|
||||
}
|
||||
OpenAiService service = getOpenAiService(user);
|
||||
CreateImageRequest createImageRequest = new CreateImageRequest();
|
||||
createImageRequest.setPrompt(aiImage.getPrompt());
|
||||
createImageRequest.setN(aiImage.getGenerateNumber());
|
||||
createImageRequest.setSize(aiImage.getGenerateSize());
|
||||
createImageRequest.setResponseFormat(OPENAI_CREATE_IMAGE_RESP_FORMATS_URL);
|
||||
createImageRequest.setUser(user.getUuid());
|
||||
try {
|
||||
ImageResult imageResult = service.createImage(createImageRequest);
|
||||
log.info("createImage response:{}", imageResult);
|
||||
return imageResult.getData();
|
||||
} catch (Exception e) {
|
||||
log.error("create image error", e);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public List<Image> editImage(User user, AiImage aiImage) {
|
||||
File originalFile = new File(fileService.getImagePath(aiImage.getOriginalImage()));
|
||||
File maskFile = null;
|
||||
if (StringUtils.isNotBlank(aiImage.getMaskImage())) {
|
||||
maskFile = new File(fileService.getImagePath(aiImage.getMaskImage()));
|
||||
}
|
||||
//如果不是RGBA类型的图片,先转成RGBA
|
||||
File rgbaOriginalImage = ImageUtil.rgbConvertToRgba(originalFile, fileService.getTmpImagesPath(aiImage.getOriginalImage()));
|
||||
OpenAiService service = getOpenAiService(user);
|
||||
CreateImageEditRequest request = new CreateImageEditRequest();
|
||||
request.setPrompt(aiImage.getPrompt());
|
||||
request.setN(aiImage.getGenerateNumber());
|
||||
request.setSize(aiImage.getGenerateSize());
|
||||
request.setResponseFormat(OPENAI_CREATE_IMAGE_RESP_FORMATS_URL);
|
||||
request.setUser(user.getUuid());
|
||||
try {
|
||||
ImageResult imageResult = service.createImageEdit(request, rgbaOriginalImage, maskFile);
|
||||
log.info("editImage response:{}", imageResult);
|
||||
return imageResult.getData();
|
||||
} catch (Exception e) {
|
||||
log.error("edit image error", e);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public List<Image> createImageVariation(User user, AiImage aiImage) {
|
||||
File imagePath = new File(fileService.getImagePath(aiImage.getOriginalImage()));
|
||||
OpenAiService service = getOpenAiService(user);
|
||||
CreateImageVariationRequest request = new CreateImageVariationRequest();
|
||||
request.setN(aiImage.getGenerateNumber());
|
||||
request.setSize(aiImage.getGenerateSize());
|
||||
request.setResponseFormat(OPENAI_CREATE_IMAGE_RESP_FORMATS_URL);
|
||||
request.setUser(user.getUuid());
|
||||
try {
|
||||
ImageResult imageResult = service.createImageVariation(request, imagePath);
|
||||
log.info("createImageVariation response:{}", imageResult);
|
||||
return imageResult.getData();
|
||||
} catch (Exception e) {
|
||||
log.error("image variation error", e);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package com.moyz.adi.common.helper;
|
||||
|
||||
import com.moyz.adi.common.entity.User;
|
||||
import com.moyz.adi.common.enums.ErrorEnum;
|
||||
import com.moyz.adi.common.model.CostStat;
|
||||
import com.moyz.adi.common.service.UserDayCostService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class QuotaHelper {
|
||||
|
||||
@Resource
|
||||
private UserDayCostService userDayCostService;
|
||||
|
||||
public ErrorEnum checkTextQuota(User user) {
|
||||
if (StringUtils.isNotBlank(user.getSecretKey())) {
|
||||
log.info("Custom secret key,dont need to check text request quota,userId:{}", user.getId());
|
||||
return null;
|
||||
}
|
||||
int userQuotaByTokenDay = user.getQuotaByTokenDaily();
|
||||
int userQuotaByTokenMonth = user.getQuotaByTokenMonthly();
|
||||
int userQuotaByRequestDay = user.getQuotaByRequestDaily();
|
||||
int userQuotaByRequestMonth = user.getQuotaByRequestMonthly();
|
||||
CostStat costStat = userDayCostService.costStatByUser(user.getId());
|
||||
if (costStat.getTextTokenCostByDay() >= userQuotaByTokenDay || costStat.getTextRequestTimesByDay() >= userQuotaByRequestDay) {
|
||||
log.warn("Reach limit of a day,userId:{},token:{},request:{},used token:{}, used request:{}", user.getId(), userQuotaByRequestDay, userQuotaByRequestDay, userQuotaByTokenMonth, userQuotaByRequestMonth);
|
||||
return ErrorEnum.B_DAILY_QUOTA_USED;
|
||||
}
|
||||
if (costStat.getTextTokenCostByMonth() >= user.getQuotaByTokenMonthly() || costStat.getTextRequestTimesByMonth() >= user.getQuotaByRequestMonthly()) {
|
||||
log.warn("Reach limit of a month,userId:{},token:{},request:{},used token:{}, used request:{}", user.getId(), user.getQuotaByTokenMonthly(), user.getQuotaByRequestMonthly(), costStat.getTextTokenCostByMonth(), costStat.getTextRequestTimesByMonth());
|
||||
return ErrorEnum.B_MONTHLY_QUOTA_USED;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the generate image request if it can be accepted
|
||||
*
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
public ErrorEnum checkImageQuota(User user) {
|
||||
if (StringUtils.isNotBlank(user.getSecretKey())) {
|
||||
log.info("Custom secret key,dont need to check image quota,userId:{}", user.getId());
|
||||
return null;
|
||||
}
|
||||
int userDailyQuota = user.getQuotaByImageDaily();
|
||||
int userMonthlyQuota = user.getQuotaByImageMonthly();
|
||||
CostStat costStat = userDayCostService.costStatByUser(user.getId());
|
||||
if (costStat.getImageGeneratedNumberByDay() >= userDailyQuota) {
|
||||
log.warn("Generate image reach limit of a day,userId:{},request quota:{},used request times:{}", user.getId(), userDailyQuota, costStat.getImageGeneratedNumberByDay());
|
||||
return ErrorEnum.B_DAILY_QUOTA_USED;
|
||||
}
|
||||
if (costStat.getImageGeneratedNumberByMonth() >= userMonthlyQuota) {
|
||||
log.warn("Generate image reach limit of a month,userId:{},token:{},request quota:{},used request times:{}", user.getId(), user.getQuotaByImageMonthly(), costStat.getImageGeneratedNumberByMonth());
|
||||
return ErrorEnum.B_MONTHLY_QUOTA_USED;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package com.moyz.adi.common.helper;
|
||||
|
||||
import com.moyz.adi.common.model.RequestRateLimit;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service
|
||||
public class RateLimitHelper {
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
public boolean checkRequestTimes(String requestTimesKey, RequestRateLimit rateLimitConfig) {
|
||||
int requestCountInTimeWindow = 0;
|
||||
String rateLimitVal = stringRedisTemplate.opsForValue().get(requestTimesKey);
|
||||
if (StringUtils.isNotBlank(rateLimitVal)) {
|
||||
requestCountInTimeWindow = Integer.parseInt(rateLimitVal);
|
||||
}
|
||||
if (requestCountInTimeWindow >= rateLimitConfig.getTimes()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void increaseRequestTimes(String requestTimesKey, RequestRateLimit rateLimitConfig) {
|
||||
long expireTime = stringRedisTemplate.getExpire(requestTimesKey).longValue();
|
||||
if (expireTime == -1) {
|
||||
stringRedisTemplate.opsForValue().increment(requestTimesKey);
|
||||
stringRedisTemplate.opsForValue().set(requestTimesKey, String.valueOf(1), rateLimitConfig.getMinutes(), TimeUnit.MINUTES);
|
||||
} else if (expireTime > 3) {
|
||||
//If expireTime <= 3, too short to cache
|
||||
stringRedisTemplate.opsForValue().increment(requestTimesKey);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.moyz.adi.common.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.moyz.adi.common.entity.AiImage;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface AiImageMapper extends BaseMapper<AiImage> {
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.moyz.adi.common.mapper;
|
||||
|
||||
import com.moyz.adi.common.entity.AiModel;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface AiModelMapper extends BaseMapper<AiModel> {
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.moyz.adi.common.mapper;
|
||||
|
||||
import com.moyz.adi.common.entity.Conversation;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 会话表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author moyz
|
||||
* @since 2023-04-11
|
||||
*/
|
||||
@Mapper
|
||||
public interface ConversationMapper extends BaseMapper<Conversation> {
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.moyz.adi.common.mapper;
|
||||
|
||||
import com.moyz.adi.common.entity.ConversationMessage;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author moyz
|
||||
* @since 2023-04-11
|
||||
*/
|
||||
@Mapper
|
||||
public interface ConversationMessageMapper extends BaseMapper<ConversationMessage> {
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.moyz.adi.common.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.moyz.adi.common.entity.AdiFile;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface FileMapper extends BaseMapper<AdiFile> {
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.moyz.adi.common.mapper;
|
||||
|
||||
import com.moyz.adi.common.entity.Prompt;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface PromptMapper extends BaseMapper<Prompt> {
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.moyz.adi.common.mapper;
|
||||
|
||||
import com.moyz.adi.common.entity.SysConfig;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface SysConfigMapper extends BaseMapper<SysConfig> {
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.moyz.adi.common.mapper;
|
||||
|
||||
import com.moyz.adi.common.entity.UserDayCost;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface UserDayCostMapper extends BaseMapper<UserDayCost> {
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue