diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..549e00a
--- /dev/null
+++ b/.gitignore
@@ -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/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3b6f5a0
--- /dev/null
+++ b/README.md
@@ -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
+```
diff --git a/adi-admin/README.md b/adi-admin/README.md
new file mode 100644
index 0000000..319a1fa
--- /dev/null
+++ b/adi-admin/README.md
@@ -0,0 +1,4 @@
+Admin site
+
+
+TODO...
\ No newline at end of file
diff --git a/adi-admin/pom.xml b/adi-admin/pom.xml
new file mode 100644
index 0000000..6ac206e
--- /dev/null
+++ b/adi-admin/pom.xml
@@ -0,0 +1,32 @@
+
+
+ 4.0.0
+
+ com.moyz
+ aideepin
+ 0.0.1-SNAPSHOT
+
+
+ adi-admin
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ com.moyz
+ adi-bootstrap
+ 0.0.1-SNAPSHOT
+
+
+ com.moyz
+ adi-common
+ 0.0.1-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/adi-bootstrap/pom.xml b/adi-bootstrap/pom.xml
new file mode 100644
index 0000000..b4e9695
--- /dev/null
+++ b/adi-bootstrap/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ com.moyz
+ aideepin
+ 0.0.1-SNAPSHOT
+
+
+ adi-bootstrap
+
+
+
+
+ src/main/resources
+
+ **/*
+
+ true
+
+
+
+
\ No newline at end of file
diff --git a/adi-bootstrap/src/main/java/com/moyz/adi/BootstrapApplication.java b/adi-bootstrap/src/main/java/com/moyz/adi/BootstrapApplication.java
new file mode 100644
index 0000000..6008ece
--- /dev/null
+++ b/adi-bootstrap/src/main/java/com/moyz/adi/BootstrapApplication.java
@@ -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);
+ }
+
+}
diff --git a/adi-bootstrap/src/main/resources/application-dev.yml b/adi-bootstrap/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..a61b950
--- /dev/null
+++ b/adi-bootstrap/src/main/resources/application-dev.yml
@@ -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/
\ No newline at end of file
diff --git a/adi-bootstrap/src/main/resources/application-prod.yml b/adi-bootstrap/src/main/resources/application-prod.yml
new file mode 100644
index 0000000..3eaba77
--- /dev/null
+++ b/adi-bootstrap/src/main/resources/application-prod.yml
@@ -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
\ No newline at end of file
diff --git a/adi-bootstrap/src/main/resources/application.yml b/adi-bootstrap/src/main/resources/application.yml
new file mode 100644
index 0000000..7a65237
--- /dev/null
+++ b/adi-bootstrap/src/main/resources/application.yml
@@ -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/
\ No newline at end of file
diff --git a/adi-bootstrap/src/main/resources/logback.xml b/adi-bootstrap/src/main/resources/logback.xml
new file mode 100644
index 0000000..8db09b7
--- /dev/null
+++ b/adi-bootstrap/src/main/resources/logback.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${CONSOLE_LOG_PATTERN}
+ UTF-8
+
+
+
+
+ /data/logs/adi.log
+
+ ${CONSOLE_LOG_PATTERN_NO_COLOR}
+ UTF-8
+
+
+
+ /data/logs/%d{yyyy-MM-dd}.%i.log
+
+ ${LOG_FILEMAXDAY}
+
+ ${LOG_MAXFILESIZE}
+
+
+
+ INFO
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/adi-chat/Dockerfile b/adi-chat/Dockerfile
new file mode 100644
index 0000000..eed310a
--- /dev/null
+++ b/adi-chat/Dockerfile
@@ -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
\ No newline at end of file
diff --git a/adi-chat/README.md b/adi-chat/README.md
new file mode 100644
index 0000000..d19fc5c
--- /dev/null
+++ b/adi-chat/README.md
@@ -0,0 +1 @@
+User side
\ No newline at end of file
diff --git a/adi-chat/pom.xml b/adi-chat/pom.xml
new file mode 100644
index 0000000..8fe63ac
--- /dev/null
+++ b/adi-chat/pom.xml
@@ -0,0 +1,64 @@
+
+
+ 4.0.0
+
+ com.moyz
+ aideepin
+ 0.0.1-SNAPSHOT
+
+
+ adi-chat
+
+
+
+ com.moyz
+ adi-bootstrap
+ 0.0.1-SNAPSHOT
+
+
+ com.moyz
+ adi-common
+ 0.0.1-SNAPSHOT
+
+
+
+
+ com.ramostear
+ Happy-Captcha
+ system
+ 1.0.1
+ ${project.basedir}/src/lib/Happy-Captcha-1.0.1.jar
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ com.moyz.adi.BootstrapApplication
+ true
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+ build-info
+ repackage
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/adi-chat/src/lib/Happy-Captcha-1.0.1.jar b/adi-chat/src/lib/Happy-Captcha-1.0.1.jar
new file mode 100644
index 0000000..2c85cf9
Binary files /dev/null and b/adi-chat/src/lib/Happy-Captcha-1.0.1.jar differ
diff --git a/adi-chat/src/main/java/com/moyz/adi/chat/controller/AiImageController.java b/adi-chat/src/main/java/com/moyz/adi/chat/controller/AiImageController.java
new file mode 100644
index 0000000..8171435
--- /dev/null
+++ b/adi-chat/src/main/java/com/moyz/adi/chat/controller/AiImageController.java
@@ -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 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 edit(@RequestBody EditImageReq editImageReq) {
+ String uuid = imageService.editByOriginalImage(editImageReq);
+ return Map.of("uuid", uuid);
+ }
+
+ @Operation(summary = "Image variation")
+ @PostMapping("/variation")
+ public Map 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);
+ }
+}
diff --git a/adi-chat/src/main/java/com/moyz/adi/chat/controller/AuthController.java b/adi-chat/src/main/java/com/moyz/adi/chat/controller/AuthController.java
new file mode 100644
index 0000000..7e5b2e6
--- /dev/null
+++ b/adi-chat/src/main/java/com/moyz/adi/chat/controller/AuthController.java
@@ -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();
+ }
+
+}
diff --git a/adi-chat/src/main/java/com/moyz/adi/chat/controller/ConversationController.java b/adi-chat/src/main/java/com/moyz/adi/chat/controller/ConversationController.java
new file mode 100644
index 0000000..617ac1f
--- /dev/null
+++ b/adi-chat/src/main/java/com/moyz/adi/chat/controller/ConversationController.java
@@ -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 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);
+ }
+}
diff --git a/adi-chat/src/main/java/com/moyz/adi/chat/controller/ConversationMessageController.java b/adi-chat/src/main/java/com/moyz/adi/chat/controller/ConversationMessageController.java
new file mode 100644
index 0000000..429121b
--- /dev/null
+++ b/adi-chat/src/main/java/com/moyz/adi/chat/controller/ConversationMessageController.java
@@ -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);
+ }
+
+}
diff --git a/adi-chat/src/main/java/com/moyz/adi/chat/controller/FileController.java b/adi-chat/src/main/java/com/moyz/adi/chat/controller/FileController.java
new file mode 100644
index 0000000..d148e6e
--- /dev/null
+++ b/adi-chat/src/main/java/com/moyz/adi/chat/controller/FileController.java
@@ -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 upload(@RequestPart(value = "file") MultipartFile file) {
+ Map 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);
+ }
+}
diff --git a/adi-chat/src/main/java/com/moyz/adi/chat/controller/PromptController.java b/adi-chat/src/main/java/com/moyz/adi/chat/controller/PromptController.java
new file mode 100644
index 0000000..2a1adfa
--- /dev/null
+++ b/adi-chat/src/main/java/com/moyz/adi/chat/controller/PromptController.java
@@ -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 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 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 autocomplete(String keyword) {
+ return promptService.autocomplete(keyword);
+ }
+
+ @Operation(summary = "保存列表")
+ @PostMapping(value = "/save")
+ public Map 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 search(@Validated SearchReq searchReq) {
+ return promptService.search(searchReq.getKeyword());
+ }
+}
diff --git a/adi-chat/src/main/java/com/moyz/adi/chat/controller/UserController.java b/adi-chat/src/main/java/com/moyz/adi/chat/controller/UserController.java
new file mode 100644
index 0000000..0a3ab06
--- /dev/null
+++ b/adi-chat/src/main/java/com/moyz/adi/chat/controller/UserController.java
@@ -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);
+ }
+ }
+}
diff --git a/adi-common/pom.xml b/adi-common/pom.xml
new file mode 100644
index 0000000..4db32c1
--- /dev/null
+++ b/adi-common/pom.xml
@@ -0,0 +1,14 @@
+
+
+ 4.0.0
+
+ com.moyz
+ aideepin
+ 0.0.1-SNAPSHOT
+
+
+ adi-common
+
+
\ No newline at end of file
diff --git a/adi-common/src/main/java/com/moyz/adi/common/CodeGenerator.java b/adi-common/src/main/java/com/moyz/adi/common/CodeGenerator.java
new file mode 100644
index 0000000..8dccb39
--- /dev/null
+++ b/adi-common/src/main/java/com/moyz/adi/common/CodeGenerator.java
@@ -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();
+ }
+}
diff --git a/adi-common/src/main/java/com/moyz/adi/common/annotation/AskReqCheck.java b/adi-common/src/main/java/com/moyz/adi/common/annotation/AskReqCheck.java
new file mode 100644
index 0000000..c16fab7
--- /dev/null
+++ b/adi-common/src/main/java/com/moyz/adi/common/annotation/AskReqCheck.java
@@ -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 {};
+}
diff --git a/adi-common/src/main/java/com/moyz/adi/common/annotation/CreateImageReqCheck.java b/adi-common/src/main/java/com/moyz/adi/common/annotation/CreateImageReqCheck.java
new file mode 100644
index 0000000..117db02
--- /dev/null
+++ b/adi-common/src/main/java/com/moyz/adi/common/annotation/CreateImageReqCheck.java
@@ -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 {};
+}
diff --git a/adi-common/src/main/java/com/moyz/adi/common/annotation/DistributeLock.java b/adi-common/src/main/java/com/moyz/adi/common/annotation/DistributeLock.java
new file mode 100644
index 0000000..e4e0612
--- /dev/null
+++ b/adi-common/src/main/java/com/moyz/adi/common/annotation/DistributeLock.java
@@ -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;
+
+}
diff --git a/adi-common/src/main/java/com/moyz/adi/common/annotation/NotAllFieldsEmptyCheck.java b/adi-common/src/main/java/com/moyz/adi/common/annotation/NotAllFieldsEmptyCheck.java
new file mode 100644
index 0000000..e3d7f04
--- /dev/null
+++ b/adi-common/src/main/java/com/moyz/adi/common/annotation/NotAllFieldsEmptyCheck.java
@@ -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 {};
+}
diff --git a/adi-common/src/main/java/com/moyz/adi/common/annotation/ParamsLog.java b/adi-common/src/main/java/com/moyz/adi/common/annotation/ParamsLog.java
new file mode 100644
index 0000000..24662f7
--- /dev/null
+++ b/adi-common/src/main/java/com/moyz/adi/common/annotation/ParamsLog.java
@@ -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 {
+}
diff --git a/adi-common/src/main/java/com/moyz/adi/common/aop/ControllerParamsLogAspect.java b/adi-common/src/main/java/com/moyz/adi/common/aop/ControllerParamsLogAspect.java
new file mode 100644
index 0000000..63365bd
--- /dev/null
+++ b/adi-common/src/main/java/com/moyz/adi/common/aop/ControllerParamsLogAspect.java
@@ -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);
+ }
+
+
+}
diff --git a/adi-common/src/main/java/com/moyz/adi/common/aop/DistributeLockAspect.java b/adi-common/src/main/java/com/moyz/adi/common/aop/DistributeLockAspect.java
new file mode 100644
index 0000000..1870c2c
--- /dev/null
+++ b/adi-common/src/main/java/com/moyz/adi/common/aop/DistributeLockAspect.java
@@ -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;
+ }
+}
diff --git a/adi-common/src/main/java/com/moyz/adi/common/aop/ParamsLogAspect.java b/adi-common/src/main/java/com/moyz/adi/common/aop/ParamsLogAspect.java
new file mode 100644
index 0000000..0baf7a6
--- /dev/null
+++ b/adi-common/src/main/java/com/moyz/adi/common/aop/ParamsLogAspect.java
@@ -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));
+ }
+}
diff --git a/adi-common/src/main/java/com/moyz/adi/common/base/BaseResponse.java b/adi-common/src/main/java/com/moyz/adi/common/base/BaseResponse.java
new file mode 100644
index 0000000..e9a2508
--- /dev/null
+++ b/adi-common/src/main/java/com/moyz/adi/common/base/BaseResponse.java
@@ -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 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, "");
+ }
+}
diff --git a/adi-common/src/main/java/com/moyz/adi/common/base/ResponseWrapper.java b/adi-common/src/main/java/com/moyz/adi/common/base/ResponseWrapper.java
new file mode 100644
index 0000000..9db597a
--- /dev/null
+++ b/adi-common/src/main/java/com/moyz/adi/common/base/ResponseWrapper.java
@@ -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