diff --git a/README.md b/README.md index 8caf41c..70d5d1e 100644 --- a/README.md +++ b/README.md @@ -41,28 +41,29 @@ novel 是一套基于时下**最新** Java 技术栈 Spring Boot 3 + Vue 3 开 ## 后端技术选型 -| 技术 | 版本 | 说明 | 官网 | 学习 | -|---------------------|:--------------:|---------------------| --------------------------------------- |:-----------------------------------------------------------------------------------------------------------------------------:| -| Spring Boot | 3.0.0 | 容器 + MVC 框架 | [进入](https://spring.io/projects/spring-boot) | [进入](https://docs.spring.io/spring-boot/docs/3.0.0/reference/html) | -| MyBatis | 3.5.9 | ORM 框架 | [进入](http://www.mybatis.org) | [进入](https://mybatis.org/mybatis-3/zh/index.html) | -| MyBatis-Plus | 3.5.3 | MyBatis 增强工具 | [进入](https://baomidou.com/) | [进入](https://baomidou.com/pages/24112f/) | -| JJWT | 0.11.5 | JWT 登录支持 | [进入](https://github.com/jwtk/jjwt) | - | -| Lombok | 1.18.24 | 简化对象封装工具 | [进入](https://github.com/projectlombok/lombok) | [进入](https://projectlombok.org/features/all) | -| Caffeine | 3.1.0 | 本地缓存支持 | [进入](https://github.com/ben-manes/caffeine) | [进入](https://github.com/ben-manes/caffeine/wiki/Home-zh-CN) | -| Redis | 7.0 | 分布式缓存支持 | [进入](https://redis.io) | [进入](https://redis.io/docs) | -| Redisson | 3.17.4 | 分布式锁实现 | [进入](https://github.com/redisson/redisson) | [进入](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | -| MySQL | 8.0 | 数据库服务 | [进入](https://www.mysql.com) | [进入](https://docs.oracle.com/en-us/iaas/mysql-database/doc/getting-started.html) | -| ShardingSphere-JDBC | 5.1.1 | 数据库分库分表支持 | [进入](https://shardingsphere.apache.org) | [进入](https://shardingsphere.apache.org/document/5.1.1/cn/overview) | -| Elasticsearch | 8.2.0 | 搜索引擎服务 | [进入](https://www.elastic.co) | [进入](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) | -| RabbitMQ | 3.10.2 | 开源消息中间件 | [进入](https://www.rabbitmq.com) | [进入](https://www.rabbitmq.com/tutorials/tutorial-one-java.html) | -| XXL-JOB | 2.3.1 | 分布式任务调度平台 | [进入](https://www.xuxueli.com/xxl-job) | [进入](https://www.xuxueli.com/xxl-job) | -| Sentinel | 1.8.4 | 流量控制组件 | [进入](https://github.com/alibaba/Sentinel) | [进入](https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5) | -| Springdoc-openapi | 2.0.0 | Swagger 3 接口文档自动生成 | [进入](https://github.com/springdoc/springdoc-openapi) | [进入](https://springdoc.org/) | -| Spring Boot Admin | 3.0.0-M1 | 应用管理和监控 | [进入](https://github.com/codecentric/spring-boot-admin) | [进入](https://codecentric.github.io/spring-boot-admin/3.0.0-M1) | -| Undertow | 2.2.17.Final | Java 开发的高性能 Web 服务器 | [进入](https://undertow.io) | [进入](https://undertow.io/documentation.html) | -| Docker | - | 应用容器引擎 | [进入](https://www.docker.com/) | - | -| Jenkins | - | 自动化部署工具 | [进入](https://github.com/jenkinsci/jenkins) | - | -| Sonarqube | - | 代码质量控制 | [进入](https://www.sonarqube.org/) | - | +| 技术 | 版本 | 说明 | 官网 | 学习 | +|---------------------|:------------:|---------------------| ------------------------------------- |:------------------------------------------------------------------------------------------------------------------------:| +| Spring Boot | 3.3.0 | 容器 + MVC 框架 | [进入](https://spring.io/projects/spring-boot) | [进入](https://docs.spring.io/spring-boot/docs/3.0.0/reference/html) | +| Spring AI | 1.0.0-SNAPSHOT | Spring 官方 AI 框架 | [进入](https://spring.io/projects/spring-ai) | [进入](https://docs.spring.io/spring-ai/reference/) | +| MyBatis | 3.5.9 | ORM 框架 | [进入](http://www.mybatis.org) | [进入](https://mybatis.org/mybatis-3/zh/index.html) | +| MyBatis-Plus | 3.5.3 | MyBatis 增强工具 | [进入](https://baomidou.com/) | [进入](https://baomidou.com/pages/24112f/) | +| JJWT | 0.11.5 | JWT 登录支持 | [进入](https://github.com/jwtk/jjwt) | - | +| Lombok | 1.18.24 | 简化对象封装工具 | [进入](https://github.com/projectlombok/lombok) | [进入](https://projectlombok.org/features/all) | +| Caffeine | 3.1.0 | 本地缓存支持 | [进入](https://github.com/ben-manes/caffeine) | [进入](https://github.com/ben-manes/caffeine/wiki/Home-zh-CN) | +| Redis | 7.0 | 分布式缓存支持 | [进入](https://redis.io) | [进入](https://redis.io/docs) | +| Redisson | 3.17.4 | 分布式锁实现 | [进入](https://github.com/redisson/redisson) | [进入](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | +| MySQL | 8.0 | 数据库服务 | [进入](https://www.mysql.com) | [进入](https://docs.oracle.com/en-us/iaas/mysql-database/doc/getting-started.html) | +| ShardingSphere-JDBC | 5.1.1 | 数据库分库分表支持 | [进入](https://shardingsphere.apache.org) | [进入](https://shardingsphere.apache.org/document/5.1.1/cn/overview) | +| Elasticsearch | 8.2.0 | 搜索引擎服务 | [进入](https://www.elastic.co) | [进入](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) | +| RabbitMQ | 3.10.2 | 开源消息中间件 | [进入](https://www.rabbitmq.com) | [进入](https://www.rabbitmq.com/tutorials/tutorial-one-java.html) | +| XXL-JOB | 2.3.1 | 分布式任务调度平台 | [进入](https://www.xuxueli.com/xxl-job) | [进入](https://www.xuxueli.com/xxl-job) | +| Sentinel | 1.8.4 | 流量控制组件 | [进入](https://github.com/alibaba/Sentinel) | [进入](https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5) | +| Springdoc-openapi | 2.0.0 | Swagger 3 接口文档自动生成 | [进入](https://github.com/springdoc/springdoc-openapi) | [进入](https://springdoc.org/) | +| Spring Boot Admin | 3.0.0-M1 | 应用管理和监控 | [进入](https://github.com/codecentric/spring-boot-admin) | [进入](https://codecentric.github.io/spring-boot-admin/3.0.0-M1) | +| Undertow | 2.2.17.Final | Java 开发的高性能 Web 服务器 | [进入](https://undertow.io) | [进入](https://undertow.io/documentation.html) | +| Docker | - | 应用容器引擎 | [进入](https://www.docker.com/) | - | +| Jenkins | - | 自动化部署工具 | [进入](https://github.com/jenkinsci/jenkins) | - | +| Sonarqube | - | 代码质量控制 | [进入](https://www.sonarqube.org/) | - | **注:更多热门新技术待集成。** diff --git a/pom.xml b/pom.xml index 3af661b..9426b2c 100644 --- a/pom.xml +++ b/pom.xml @@ -177,6 +177,12 @@ spring-boot-starter-mail + + + org.springframework.ai + spring-ai-openai-spring-boot-starter + + com.mysql mysql-connector-j @@ -212,6 +218,18 @@ + + + + org.springframework.ai + spring-ai-bom + 1.0.0-SNAPSHOT + pom + import + + + + @@ -248,6 +266,22 @@ false + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + diff --git a/src/main/java/io/github/xxyopen/novel/controller/author/AuthorAiController.java b/src/main/java/io/github/xxyopen/novel/controller/author/AuthorAiController.java new file mode 100644 index 0000000..799804d --- /dev/null +++ b/src/main/java/io/github/xxyopen/novel/controller/author/AuthorAiController.java @@ -0,0 +1,83 @@ +package io.github.xxyopen.novel.controller.author; + +import io.github.xxyopen.novel.core.common.resp.RestResp; +import io.github.xxyopen.novel.core.constant.ApiRouterConsts; +import io.github.xxyopen.novel.core.constant.SystemConfigConsts; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 作家后台-AI模块API控制器 + * + * @author xiongxiaoyang + * @date 2025/2/19 + */ +@Tag(name = "AiController", description = "作家后台-AI模块") +@SecurityRequirement(name = SystemConfigConsts.HTTP_AUTH_HEADER_NAME) +@RestController +@RequestMapping(ApiRouterConsts.API_AUTHOR_AI_URL_PREFIX) +@RequiredArgsConstructor +public class AuthorAiController { + + private final ChatClient chatClient; + + /** + * AI扩写 + */ + @Operation(summary = "AI扩写接口") + @PostMapping("/expand") + public RestResp expandText(@RequestParam("text") String text, @RequestParam("ratio") Double ratio) { + String prompt = "请将以下文本扩写为原长度的" + ratio/100 + "倍:" + text; + return RestResp.ok(chatClient.prompt() + .user(prompt) + .call() + .content()); + } + + /** + * AI缩写 + */ + @Operation(summary = "AI缩写接口") + @PostMapping("/condense") + public RestResp condenseText(@RequestParam("text") String text, @RequestParam("ratio") Integer ratio) { + String prompt = "请将以下文本缩写为原长度的" + 100/ratio + "分之一:" + text; + return RestResp.ok(chatClient.prompt() + .user(prompt) + .call() + .content()); + } + + /** + * AI续写 + */ + @Operation(summary = "AI续写接口") + @PostMapping("/continue") + public RestResp continueText(@RequestParam("text") String text, @RequestParam("length") Integer length) { + String prompt = "请续写以下文本,续写长度约为" + length + "字:" + text; + return RestResp.ok(chatClient.prompt() + .user(prompt) + .call() + .content()); + } + + /** + * AI润色 + */ + @Operation(summary = "AI润色接口") + @PostMapping("/polish") + public RestResp polishText(@RequestParam("text") String text) { + String prompt = "请润色优化以下文本,保持原意:" + text; + return RestResp.ok(chatClient.prompt() + .user(prompt) + .call() + .content()); + } + +} diff --git a/src/main/java/io/github/xxyopen/novel/core/config/AiConfig.java b/src/main/java/io/github/xxyopen/novel/core/config/AiConfig.java new file mode 100644 index 0000000..b433ab0 --- /dev/null +++ b/src/main/java/io/github/xxyopen/novel/core/config/AiConfig.java @@ -0,0 +1,47 @@ +package io.github.xxyopen.novel.core.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestClient; + +/** + * Ai 相关配置 + * + * @author xiongxiaoyang + * @date 2025/2/19 + */ +@Configuration +@Slf4j +public class AiConfig { + + /** + * 目的:配置自定义的 RestClientBuilder 对象 + *

+ * 原因:Spring AI 框架的 ChatClient 内部通过 RestClient(Spring Framework 6 和 Spring Boot 3 中引入) 发起 HTTP REST 请求与远程的大模型服务进行通信, + * 如果项目中没有配置自定义的 RestClientBuilder 对象, 那么在 RestClient 的自动配置类 org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration + * 中配置的 RestClientBuilder 对象会使用 Spring 容器中提供的 HttpMessageConverters, 由于本项目中配置了 spring.jackson.generator.write-numbers-as-strings + * = true, 所以 Spring 容器中的 HttpMessageConverters 在 RestClient 发起 HTTP REST 请求转换 Java 对象为 JSON 字符串时会自动将 Number 类型的 + * Java 对象属性转换为字符串而导致请求参数错误 + *

+ * 示例:"temperature": 0.7 =》"temperature": "0.7" + * {"code":20015,"message":"The parameter is invalid. Please check again.","data":null} + */ + @Bean + public RestClient.Builder restClientBuilder() { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + // 连接超时时间 + factory.setConnectTimeout(5000); + // 读取超时时间 + factory.setReadTimeout(60000); + return RestClient.builder().requestFactory(factory); + } + + @Bean + public ChatClient chatClient(ChatClient.Builder chatClientBuilder) { + return chatClientBuilder.build(); + } + +} diff --git a/src/main/java/io/github/xxyopen/novel/core/constant/ApiRouterConsts.java b/src/main/java/io/github/xxyopen/novel/core/constant/ApiRouterConsts.java index 4a62610..8dbefd3 100644 --- a/src/main/java/io/github/xxyopen/novel/core/constant/ApiRouterConsts.java +++ b/src/main/java/io/github/xxyopen/novel/core/constant/ApiRouterConsts.java @@ -62,6 +62,11 @@ public class ApiRouterConsts { */ public static final String SEARCH_URL_PREFIX = "/search"; + /** + * AI模块请求路径前缀 + */ + public static final String AI_URL_PREFIX = "/ai"; + /** * 前台门户首页API请求路径前缀 */ @@ -94,4 +99,10 @@ public class ApiRouterConsts { public static final String API_FRONT_SEARCH_URL_PREFIX = API_FRONT_URL_PREFIX + SEARCH_URL_PREFIX; + /** + * 作家后台AI相关API请求路径前缀 + */ + public static final String API_AUTHOR_AI_URL_PREFIX = API_AUTHOR_URL_PREFIX + AI_URL_PREFIX; + + } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2d4c302..9c8d536 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -34,6 +34,18 @@ server: # 端口号 port: 8888 +--- #--------------------- Spring AI 配置---------------------- +spring: + ai: + openai: + api-key: + base-url: https://api.siliconflow.cn + chat: + options: + model: deepseek-ai/DeepSeek-R1-Distill-Llama-8B + + + --- #---------------------数据库配置--------------------------- spring: datasource: