AI 项目详细开发步骤指南
一、环境搭建详解
1. JDK 17 安装与配置
Windows 系统安装步骤:
- 访问 Oracle 官网下载 JDK 17 安装包:https://www.oracle.com/java/technologies/downloads/#java17
- 下载 Windows x64 Installer 版本
- 双击安装包,按照向导完成安装
- 配置环境变量:
- 右键"此电脑" -> 属性 -> 高级系统设置 -> 环境变量
- 在系统变量中新建
JAVA_HOME
,值为 JDK 安装路径(如C:\Program Files\Java\jdk-17
) - 编辑系统变量
Path
,添加%JAVA_HOME%\bin
- 验证安装:
java -version javac -version
Linux 系统安装步骤:
- 使用包管理器安装:
# Ubuntu/Debian sudo apt update sudo apt install openjdk-17-jdk # CentOS/RHEL sudo yum install java-17-openjdk-devel
- 验证安装:
java -version javac -version
2. Maven 安装与配置
Windows 系统安装步骤:
- 访问 Maven 官网下载二进制包:https://maven.apache.org/download.cgi
- 下载
apache-maven-3.9.6-bin.zip
- 解压到指定目录(如
C:\Program Files\Apache\maven
) - 配置环境变量:
- 新建系统变量
MAVEN_HOME
,值为 Maven 解压路径 - 编辑系统变量
Path
,添加%MAVEN_HOME%\bin
- 新建系统变量
- 验证安装:
mvn -v
Linux 系统安装步骤:
- 下载 Maven 二进制包:
wget https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz
- 解压到指定目录:
sudo tar -xzf apache-maven-3.9.6-bin.tar.gz -C /opt
- 创建软链接:
sudo ln -s /opt/apache-maven-3.9.6 /opt/maven
- 配置环境变量,编辑
~/.bashrc
或~/.zshrc
:export MAVEN_HOME=/opt/maven export PATH=$PATH:$MAVEN_HOME/bin
- 使环境变量生效:
source ~/.bashrc # 或 source ~/.zshrc
- 验证安装:
mvn -v
3. MySQL 安装与配置
Windows 系统安装步骤:
- 访问 MySQL 官网下载安装包:https://dev.mysql.com/downloads/installer/
- 下载 MySQL Installer
- 运行安装程序,选择"Server only"或"Custom"安装类型
- 按照向导完成安装,设置 root 密码
- 将 MySQL 的 bin 目录添加到系统环境变量 Path 中
- 验证安装:
mysql --version mysql -u root -p
Linux 系统安装步骤:
- 使用包管理器安装:
# Ubuntu/Debian sudo apt update sudo apt install mysql-server # CentOS/RHEL sudo yum install mysql-server
- 启动 MySQL 服务:
# Ubuntu/Debian sudo systemctl start mysql sudo systemctl enable mysql # CentOS/RHEL sudo systemctl start mysqld sudo systemctl enable mysqld
- 设置 root 密码:
sudo mysql_secure_installation
- 验证安装:
mysql --version mysql -u root -p
创建项目数据库:
- 登录 MySQL:
mysql -u root -p
- 创建数据库:
CREATE DATABASE mt_ai CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- 创建项目用户并授权:
CREATE USER 'mt_ai_user'@'localhost' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON mt_ai.* TO 'mt_ai_user'@'localhost'; FLUSH PRIVILEGES;
4. IDE 安装与配置
IntelliJ IDEA 安装步骤:
- 访问 JetBrains 官网下载 IDEA:https://www.jetbrains.com/idea/download/
- 下载 Community 或 Ultimate 版本
- 运行安装程序,按照向导完成安装
- 首次启动配置:
- 选择 UI 主题
- 安装必要插件:
- Lombok
- Spring Boot
- MyBatisX
- Maven Helper
- Git Integration
配置 JDK:
- 打开 IDEA -> File -> Project Structure
- 在 Project 选项卡中设置 Project SDK 为 JDK 17
- 在 Modules 选项卡中确保 Language Level 为 17
配置 Maven:
- 打开 IDEA -> File -> Settings -> Build, Execution, Deployment -> Build Tools -> Maven
- 设置 Maven home directory 为 Maven 安装路径
- 设置 User settings file 为 Maven 的 settings.xml 路径
- 设置 Local repository 为 Maven 本地仓库路径
二、项目初始化详解
1. 创建 Spring Boot 项目
方法一:使用 Spring Initializr 网站
- 访问 https://start.spring.io/
- 填写项目信息:
- Project: Maven
- Language: Java
- Spring Boot: 3.4.3
- Project Metadata:
- Group: com.maoteng
- Artifact: maoteng-ai
- Name: maoteng-ai
- Description: MT-AI Project
- Package name: com.maoteng.ai
- Packaging: Jar
- Java: 17
- 添加依赖:
- Spring Web
- Spring Data JPA
- MySQL Driver
- Lombok
- Spring AI
- 点击"GENERATE"下载项目压缩包
- 解压到工作目录
方法二:使用 IDEA 创建
- 打开 IDEA -> File -> New -> Project
- 选择 Spring Initializr
- 填写项目信息(同上)
- 选择依赖(同上)
- 点击"Finish"完成创建
2. 配置 pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.maoteng</groupId>
<artifactId>maoteng-ai</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>maoteng-ai</name>
<description>MT-AI Project</description>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-M6</spring-ai.version>
<mybatis-plus.version>3.5.10.1</mybatis-plus.version>
<lombok.version>1.18.22</lombok.version>
</properties>
<dependencies>
<!-- Spring Boot Starters -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Spring AI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>
<!-- Database -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- Tools -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
3. 创建基础配置文件
application.yml
server:
port: 8080
servlet:
context-path: /api
spring:
application:
name: mt-ai
# 数据库配置
datasource:
url: jdbc:mysql://localhost:3306/mt_ai?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: mt_ai_user
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
minimum-idle: 5
maximum-pool-size: 15
idle-timeout: 30000
pool-name: HikariCP
max-lifetime: 1800000
connection-timeout: 30000
# JPA配置
jpa:
database-platform: org.hibernate.dialect.MySQL8Dialect
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
# AI配置
ai:
openai:
api-key: your-openai-api-key
model: gpt-3.5-turbo
temperature: 0.7
max-tokens: 2000
ollama:
base-url: http://localhost:11434
model: llama2
# MyBatis-Plus配置
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
type-aliases-package: com.maoteng.ai.entity
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
# 日志配置
logging:
level:
root: INFO
com.maoteng.ai: DEBUG
org.springframework.web: INFO
org.hibernate: INFO
file:
name: logs/mt-ai.log
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
# 监控配置
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
application-dev.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mt_ai_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: mt_ai_user
password: dev_password
jpa:
show-sql: true
hibernate:
ddl-auto: update
logging:
level:
com.maoteng.ai: DEBUG
application-prod.yml
spring:
datasource:
url: jdbc:mysql://prod-db-server:3306/mt_ai_prod?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
show-sql: false
hibernate:
ddl-auto: none
logging:
level:
com.maoteng.ai: INFO
4. 创建主应用类
package com.maoteng.ai;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = "com.maoteng.ai")
public class MtAiApplication {
public static void main(String[] args) {
SpringApplication.run(MtAiApplication.class, args);
}
}
5. 验证项目初始化
- 在 IDEA 中打开项目
- 等待 Maven 下载依赖
- 运行主应用类
MtAiApplication
- 查看控制台输出,确认应用启动成功
- 访问 http://localhost:8080/api/actuator/health 检查健康状态
三、项目结构搭建详解
1. 创建基础包结构
在 src/main/java/com/maoteng/ai
目录下创建以下包结构:
src/main/java/com/maoteng/ai/
├── config/ # 配置类
├── constants/ # 常量定义
├── controller/ # 控制器
├── entity/ # 实体类
├── mapper/ # MyBatis映射接口
├── model/ # 数据模型(DTO、VO等)
├── repository/ # 数据访问层
├── service/ # 业务逻辑层
│ └── impl/ # 服务实现类
├── tools/ # 工具类
└── utils/ # 工具类
2. 创建基础配置类
2.1 数据库配置
package com.maoteng.ai.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
2.2 Web配置
package com.maoteng.ai.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
2.3 AI配置
package com.maoteng.ai.config;
import org.springframework.ai.openai.OpenAiApi;
import org.springframework.ai.openai.client.OpenAiClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
@Configuration
public class OpenAiConfig {
@Bean
public OpenAiClient openAiClient() {
return OpenAiClient.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.build();
}
}
package com.maoteng.ai.config;
import org.springframework.ai.ollama.OllamaApi;
import org.springframework.ai.ollama.client.OllamaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
@Configuration
public class OllamaConfig {
@Bean
public OllamaClient ollamaClient() {
return OllamaClient.builder()
.baseUrl("http://localhost:11434")
.build();
}
}
3. 创建常量类
package com.maoteng.ai.constants;
public class CommonConstants {
// 系统常量
public static final String SYSTEM_NAME = "MT-AI";
public static final String VERSION = "1.0.0";
// 状态常量
public static final int STATUS_NORMAL = 0;
public static final int STATUS_DISABLED = 1;
// 分页常量
public static final int DEFAULT_PAGE_SIZE = 10;
public static final int DEFAULT_PAGE_NUM = 1;
// 时间格式
public static final String DATE_FORMAT = "yyyy-MM-dd";
public static final String TIME_FORMAT = "HH:mm:ss";
public static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
}
package com.maoteng.ai.constants;
public class ResponseConstants {
// 响应状态码
public static final int SUCCESS = 200;
public static final int ERROR = 500;
public static final int UNAUTHORIZED = 401;
public static final int FORBIDDEN = 403;
public static final int NOT_FOUND = 404;
// 响应消息
public static final String SUCCESS_MSG = "操作成功";
public static final String ERROR_MSG = "操作失败";
public static final String UNAUTHORIZED_MSG = "未授权";
public static final String FORBIDDEN_MSG = "禁止访问";
public static final String NOT_FOUND_MSG = "资源不存在";
}
四、核心功能开发详解
1. 实体类开发
1.1 基础实体类
package com.maoteng.ai.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import jakarta.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@Entity
@TableName("base_entity")
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@TableId(type = IdType.AUTO)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@CreatedDate
@Column(nullable = false, updatable = false)
private LocalDateTime createTime;
@LastModifiedDate
@Column(nullable = false)
private LocalDateTime updateTime;
@TableLogic
@Column(nullable = false)
private Integer deleted = 0;
}
1.2 用户实体类
package com.maoteng.ai.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
@Data
@Entity
@Table(name = "user")
@TableName("user")
@EqualsAndHashCode(callSuper = true)
public class User extends BaseEntity {
@Column(nullable = false, unique = true, length = 50)
private String username;
@Column(nullable = false, length = 100)
private String password;
@Column(length = 50)
private String nickname;
@Column(length = 100)
private String email;
@Column(length = 20)
private String phone;
@Column(nullable = false)
private Integer status = 0;
@Column(length = 500)
private String avatar;
}
1.3 聊天记录实体类
package com.maoteng.ai.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
@Data
@Entity
@Table(name = "chat_history")
@TableName("chat_history")
@EqualsAndHashCode(callSuper = true)
public class ChatHistory extends BaseEntity {
@Column(nullable = false)
private Long userId;
@Column(nullable = false, length = 50)
private String modelType; // openai, ollama
@Column(nullable = false, length = 50)
private String modelName; // gpt-3.5-turbo, llama2
@Column(nullable = false, columnDefinition = "TEXT")
private String userMessage;
@Column(nullable = false, columnDefinition = "TEXT")
private String aiResponse;
@Column
private Integer tokens;
@Column
private Long duration; // 响应时间(毫秒)
}
2. 数据访问层开发
2.1 用户Mapper
package com.maoteng.ai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.maoteng.ai.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT * FROM user WHERE username = #{username} AND deleted = 0")
User findByUsername(String username);
}
2.2 聊天记录Mapper
package com.maoteng.ai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.maoteng.ai.entity.ChatHistory;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface ChatHistoryMapper extends BaseMapper<ChatHistory> {
@Select("SELECT * FROM chat_history WHERE user_id = #{userId} AND deleted = 0 ORDER BY create_time DESC LIMIT #{limit}")
List<ChatHistory> findRecentByUserId(Long userId, int limit);
}
3. 服务层开发
3.1 基础服务接口
package com.maoteng.ai.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.maoteng.ai.entity.BaseEntity;
import java.util.List;
public interface BaseService<T extends BaseEntity> extends IService<T> {
/**
* 分页查询
*/
IPage<T> page(Page<T> page);
/**
* 查询所有
*/
List<T> list();
/**
* 根据ID查询
*/
T getById(Long id);
/**
* 保存
*/
boolean save(T entity);
/**
* 更新
*/
boolean updateById(T entity);
/**
* 删除
*/
boolean removeById(Long id);
}
3.2 基础服务实现
package com.maoteng.ai.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.maoteng.ai.entity.BaseEntity;
import com.maoteng.ai.mapper.BaseMapper;
import com.maoteng.ai.service.BaseService;
import java.util.List;
public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity>
extends ServiceImpl<M, T> implements BaseService<T> {
@Override
public IPage<T> page(Page<T> page) {
return this.page(page);
}
@Override
public List<T> list() {
return this.list();
}
@Override
public T getById(Long id) {
return this.getById(id);
}
@Override
public boolean save(T entity) {
return this.save(entity);
}
@Override
public boolean updateById(T entity) {
return this.updateById(entity);
}
@Override
public boolean removeById(Long id) {
return this.removeById(id);
}
}
3.3 用户服务接口
package com.maoteng.ai.service;
import com.maoteng.ai.entity.User;
public interface UserService extends BaseService<User> {
/**
* 根据用户名查询用户
*/
User findByUsername(String username);
/**
* 用户注册
*/
User register(User user);
/**
* 用户登录
*/
User login(String username, String password);
/**
* 修改密码
*/
boolean changePassword(Long userId, String oldPassword, String newPassword);
}
3.4 用户服务实现
package com.maoteng.ai.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.maoteng.ai.entity.User;
import com.maoteng.ai.mapper.UserMapper;
import com.maoteng.ai.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements UserService {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public User findByUsername(String username) {
return baseMapper.findByUsername(username);
}
@Override
@Transactional(rollbackFor = Exception.class)
public User register(User user) {
// 检查用户名是否已存在
User existingUser = findByUsername(user.getUsername());
if (existingUser != null) {
throw new RuntimeException("用户名已存在");
}
// 加密密码
user.setPassword(passwordEncoder.encode(user.getPassword()));
// 保存用户
save(user);
return user;
}
@Override
public User login(String username, String password) {
User user = findByUsername(username);
if (user == null) {
throw new RuntimeException("用户不存在");
}
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new RuntimeException("密码错误");
}
return user;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean changePassword(Long userId, String oldPassword, String newPassword) {
User user = getById(userId);
if (user == null) {
throw new RuntimeException("用户不存在");
}
if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
throw new RuntimeException("原密码错误");
}
user.setPassword(passwordEncoder.encode(newPassword));
return updateById(user);
}
}
3.5 聊天服务接口
package com.maoteng.ai.service;
import com.maoteng.ai.entity.ChatHistory;
import java.util.List;
public interface ChatService {
/**
* 发送消息到OpenAI
*/
ChatHistory sendMessageToOpenAI(Long userId, String message);
/**
* 发送消息到Ollama
*/
ChatHistory sendMessageToOllama(Long userId, String message);
/**
* 获取用户最近的聊天记录
*/
List<ChatHistory> getRecentChatHistory(Long userId, int limit);
/**
* 保存聊天记录
*/
ChatHistory saveChatHistory(ChatHistory chatHistory);
}
3.6 聊天服务实现
package com.maoteng.ai.service.impl;
import com.maoteng.ai.entity.ChatHistory;
import com.maoteng.ai.mapper.ChatHistoryMapper;
import com.maoteng.ai.service.ChatService;
import org.springframework.ai.openai.OpenAiClient;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.ollama.OllamaClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class ChatServiceImpl implements ChatService {
@Autowired
private OpenAiClient openAiClient;
@Autowired
private OllamaClient ollamaClient;
@Autowired
private ChatHistoryMapper chatHistoryMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public ChatHistory sendMessageToOpenAI(Long userId, String message) {
long startTime = System.currentTimeMillis();
// 调用OpenAI API
String response = openAiClient.generate(message);
long endTime = System.currentTimeMillis();
// 保存聊天记录
ChatHistory chatHistory = new ChatHistory();
chatHistory.setUserId(userId);
chatHistory.setModelType("openai");
chatHistory.setModelName("gpt-3.5-turbo");
chatHistory.setUserMessage(message);
chatHistory.setAiResponse(response);
chatHistory.setDuration(endTime - startTime);
return saveChatHistory(chatHistory);
}
@Override
@Transactional(rollbackFor = Exception.class)
public ChatHistory sendMessageToOllama(Long userId, String message) {
long startTime = System.currentTimeMillis();
// 调用Ollama API
String response = ollamaClient.generate(message);
long endTime = System.currentTimeMillis();
// 保存聊天记录
ChatHistory chatHistory = new ChatHistory();
chatHistory.setUserId(userId);
chatHistory.setModelType("ollama");
chatHistory.setModelName("llama2");
chatHistory.setUserMessage(message);
chatHistory.setAiResponse(response);
chatHistory.setDuration(endTime - startTime);
return saveChatHistory(chatHistory);
}
@Override
public List<ChatHistory> getRecentChatHistory(Long userId, int limit) {
return chatHistoryMapper.findRecentByUserId(userId, limit);
}
@Override
@Transactional(rollbackFor = Exception.class)
public ChatHistory saveChatHistory(ChatHistory chatHistory) {
chatHistoryMapper.insert(chatHistory);
return chatHistory;
}
}
4. 控制器开发
4.1 基础响应类
package com.maoteng.ai.model;
import lombok.Data;
import java.io.Serializable;
@Data
public class Response<T> implements Serializable {
private static final long serialVersionUID = 1L;
private int code;
private String message;
private T data;
public static <T> Response<T> success() {
return success(null);
}
public static <T> Response<T> success(T data) {
Response<T> response = new Response<>();
response.setCode(200);
response.setMessage("操作成功");
response.setData(data);
return response;
}
public static <T> Response<T> error(String message) {
return error(500, message);
}
public static <T> Response<T> error(int code, String message) {
Response<T> response = new Response<>();
response.setCode(code);
response.setMessage(message);
return response;
}
}
4.2 用户控制器
package com.maoteng.ai.controller;
import com.maoteng.ai.constants.ResponseConstants;
import com.maoteng.ai.entity.User;
import com.maoteng.ai.model.Response;
import com.maoteng.ai.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public Response<User> register(@RequestBody User user) {
try {
User registeredUser = userService.register(user);
return Response.success(registeredUser);
} catch (Exception e) {
return Response.error(e.getMessage());
}
}
@PostMapping("/login")
public Response<User> login(@RequestParam String username, @RequestParam String password) {
try {
User user = userService.login(username, password);
return Response.success(user);
} catch (Exception e) {
return Response.error(e.getMessage());
}
}
@GetMapping("/{id}")
public Response<User> getById(@PathVariable Long id) {
User user = userService.getById(id);
if (user == null) {
return Response.error(ResponseConstants.NOT_FOUND, ResponseConstants.NOT_FOUND_MSG);
}
return Response.success(user);
}
@PutMapping("/{id}/password")
public Response<Boolean> changePassword(@PathVariable Long id,
@RequestParam String oldPassword,
@RequestParam String newPassword) {
try {
boolean result = userService.changePassword(id, oldPassword, newPassword);
return Response.success(result);
} catch (Exception e) {
return Response.error(e.getMessage());
}
}
}
4.3 聊天控制器
package com.maoteng.ai.controller;
import com.maoteng.ai.entity.ChatHistory;
import com.maoteng.ai.model.Response;
import com.maoteng.ai.service.ChatService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/chat")
public class ChatController {
@Autowired
private ChatService chatService;
@PostMapping("/openai")
public Response<ChatHistory> sendMessageToOpenAI(@RequestParam Long userId, @RequestParam String message) {
try {
ChatHistory chatHistory = chatService.sendMessageToOpenAI(userId, message);
return Response.success(chatHistory);
} catch (Exception e) {
return Response.error(e.getMessage());
}
}
@PostMapping("/ollama")
public Response<ChatHistory> sendMessageToOllama(@RequestParam Long userId, @RequestParam String message) {
try {
ChatHistory chatHistory = chatService.sendMessageToOllama(userId, message);
return Response.success(chatHistory);
} catch (Exception e) {
return Response.error(e.getMessage());
}
}
@GetMapping("/history/{userId}")
public Response<List<ChatHistory>> getRecentChatHistory(@PathVariable Long userId,
@RequestParam(defaultValue = "10") int limit) {
List<ChatHistory> chatHistories = chatService.getRecentChatHistory(userId, limit);
return Response.success(chatHistories);
}
}
五、AI功能集成详解
1. OpenAI集成
1.1 OpenAI配置类
package com.maoteng.ai.config;
import org.springframework.ai.openai.OpenAiApi;
import org.springframework.ai.openai.client.OpenAiClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
@Configuration
public class OpenAiConfig {
@Bean
public OpenAiClient openAiClient() {
return OpenAiClient.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.build();
}
}
1.2 OpenAI服务类
package com.maoteng.ai.service;
import org.springframework.ai.openai.OpenAiClient;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OpenAiService {
@Autowired
private OpenAiClient openAiClient;
public String generateText(String prompt) {
return openAiClient.generate(prompt);
}
public String generateText(String prompt, String model, Double temperature, Integer maxTokens) {
return openAiClient.generate(prompt, model, temperature, maxTokens);
}
}
2. Ollama集成
2.1 Ollama配置类
package com.maoteng.ai.config;
import org.springframework.ai.ollama.OllamaApi;
import org.springframework.ai.ollama.client.OllamaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
@Configuration
public class OllamaConfig {
@Bean
public OllamaClient ollamaClient() {
return OllamaClient.builder()
.baseUrl("http://localhost:11434")
.build();
}
}
2.2 Ollama服务类
package com.maoteng.ai.service;
import org.springframework.ai.ollama.OllamaClient;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OllamaService {
@Autowired
private OllamaClient ollamaClient;
public String generateText(String prompt) {
return ollamaClient.generate(prompt);
}
public String generateText(String prompt, String model) {
return ollamaClient.generate(prompt, model);
}
}
六、测试开发详解
1. 单元测试
1.1 用户服务测试
package com.maoteng.ai.service;
import com.maoteng.ai.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@Transactional
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testRegister() {
User user = new User();
user.setUsername("testuser");
user.setPassword("password");
User registeredUser = userService.register(user);
assertNotNull(registeredUser.getId());
assertEquals("testuser", registeredUser.getUsername());
}
@Test
public void testLogin() {
// 先注册用户
User user = new User();
user.setUsername("testuser");
user.setPassword("password");
userService.register(user);
// 测试登录
User loggedInUser = userService.login("testuser", "password");
assertNotNull(loggedInUser);
assertEquals("testuser", loggedInUser.getUsername());
}
}
1.2 聊天服务测试
package com.maoteng.ai.service;
import com.maoteng.ai.entity.ChatHistory;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@Transactional
public class ChatServiceTest {
@Autowired
private ChatService chatService;
@Test
public void testSendMessageToOpenAI() {
Long userId = 1L;
String message = "Hello, OpenAI!";
ChatHistory chatHistory = chatService.sendMessageToOpenAI(userId, message);
assertNotNull(chatHistory.getId());
assertEquals(userId, chatHistory.getUserId());
assertEquals("openai", chatHistory.getModelType());
assertEquals("gpt-3.5-turbo", chatHistory.getModelName());
assertEquals(message, chatHistory.getUserMessage());
assertNotNull(chatHistory.getAiResponse());
}
@Test
public void testGetRecentChatHistory() {
Long userId = 1L;
int limit = 5;
List<ChatHistory> chatHistories = chatService.getRecentChatHistory(userId, limit);
assertNotNull(chatHistories);
}
}
2. 接口测试
使用Postman或其他API测试工具测试接口:
2.1 用户接口测试
-
注册用户
- 方法: POST
- URL: http://localhost:8080/api/users/register
- 请求体:
{ "username": "testuser", "password": "password", "email": "test@example.com" }
-
用户登录
- 方法: POST
- URL: http://localhost:8080/api/users/login?username=testuser&password=password
-
获取用户信息
- 方法: GET
- URL: http://localhost:8080/api/users/1
2.2 聊天接口测试
-
发送消息到OpenAI
- 方法: POST
- URL: http://localhost:8080/api/chat/openai?userId=1&message=Hello, OpenAI!
-
发送消息到Ollama
- 方法: POST
- URL: http://localhost:8080/api/chat/ollama?userId=1&message=Hello, Ollama!
-
获取聊天历史
- 方法: GET
- URL: http://localhost:8080/api/chat/history/1?limit=10
七、部署准备详解
1. 打包配置详解
1.1 Maven打包配置
在pom.xml
中添加或修改打包配置:
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
<mainClass>com.maoteng.ai.MtAiApplication</mainClass>
<layout>JAR</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
1.2 多环境配置
创建不同环境的配置文件:
application-dev.yml
server:
port: 8080
servlet:
context-path: /api
spring:
application:
name: mt-ai
# 数据库配置
datasource:
url: jdbc:mysql://localhost:3306/mt_ai_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: mt_ai_user
password: dev_password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
minimum-idle: 5
maximum-pool-size: 15
idle-timeout: 30000
pool-name: HikariCP
max-lifetime: 1800000
connection-timeout: 30000
# JPA配置
jpa:
database-platform: org.hibernate.dialect.MySQL8Dialect
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
# AI配置
ai:
openai:
api-key: your-openai-api-key-dev
model: gpt-3.5-turbo
temperature: 0.7
max-tokens: 2000
ollama:
base-url: http://localhost:11434
model: llama2
# MyBatis-Plus配置
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
type-aliases-package: com.maoteng.ai.entity
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
# 日志配置
logging:
level:
root: INFO
com.maoteng.ai: DEBUG
org.springframework.web: INFO
org.hibernate: INFO
file:
name: logs/mt-ai-dev.log
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
# 监控配置
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
application-test.yml
server:
port: 8080
servlet:
context-path: /api
spring:
application:
name: mt-ai
# 数据库配置
datasource:
url: jdbc:mysql://test-db-server:3306/mt_ai_test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
minimum-idle: 5
maximum-pool-size: 15
idle-timeout: 30000
pool-name: HikariCP
max-lifetime: 1800000
connection-timeout: 30000
# JPA配置
jpa:
database-platform: org.hibernate.dialect.MySQL8Dialect
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
# AI配置
ai:
openai:
api-key: ${OPENAI_API_KEY}
model: gpt-3.5-turbo
temperature: 0.7
max-tokens: 2000
ollama:
base-url: http://test-ollama-server:11434
model: llama2
# MyBatis-Plus配置
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
type-aliases-package: com.maoteng.ai.entity
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
# 日志配置
logging:
level:
root: INFO
com.maoteng.ai: DEBUG
org.springframework.web: INFO
org.hibernate: INFO
file:
name: logs/mt-ai-test.log
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
# 监控配置
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
application-prod.yml
server:
port: 8080
servlet:
context-path: /api
spring:
application:
name: mt-ai
# 数据库配置
datasource:
url: jdbc:mysql://prod-db-server:3306/mt_ai_prod?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
minimum-idle: 10
maximum-pool-size: 50
idle-timeout: 30000
pool-name: HikariCP
max-lifetime: 1800000
connection-timeout: 30000
# JPA配置
jpa:
database-platform: org.hibernate.dialect.MySQL8Dialect
show-sql: false
hibernate:
ddl-auto: none
properties:
hibernate:
format_sql: false
# AI配置
ai:
openai:
api-key: ${OPENAI_API_KEY}
model: gpt-3.5-turbo
temperature: 0.7
max-tokens: 2000
ollama:
base-url: http://prod-ollama-server:11434
model: llama2
# MyBatis-Plus配置
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
type-aliases-package: com.maoteng.ai.entity
configuration:
map-underscore-to-camel-case: true
cache-enabled: true
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
# 日志配置
logging:
level:
root: INFO
com.maoteng.ai: INFO
org.springframework.web: INFO
org.hibernate: INFO
file:
name: logs/mt-ai-prod.log
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
# 监控配置
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
2. 环境变量配置
2.1 开发环境
在本地开发环境中,可以创建一个.env
文件来存储环境变量:
DB_USERNAME=mt_ai_user
DB_PASSWORD=dev_password
OPENAI_API_KEY=your-openai-api-key
2.2 生产环境
在生产环境中,可以使用系统环境变量或Docker环境变量:
# Linux/Mac
export DB_USERNAME=mt_ai_user
export DB_PASSWORD=prod_password
export OPENAI_API_KEY=your-openai-api-key
# Windows
set DB_USERNAME=mt_ai_user
set DB_PASSWORD=prod_password
set OPENAI_API_KEY=your-openai-api-key
八、部署步骤详解
1. 开发环境部署
1.1 本地运行
- 在IDEA中打开项目
- 配置运行环境:
- 点击"Edit Configurations"
- 添加Spring Boot配置
- 设置Main class为
com.maoteng.ai.MtAiApplication
- 在VM options中添加
-Dspring.profiles.active=dev
- 点击运行按钮启动应用
1.2 命令行运行
# 编译项目
mvn clean package -DskipTests
# 运行项目(开发环境)
java -jar target/maoteng-ai.jar --spring.profiles.active=dev
2. 测试环境部署
2.1 服务器准备
-
安装JDK 17
# Ubuntu/Debian sudo apt update sudo apt install openjdk-17-jdk # CentOS/RHEL sudo yum install java-17-openjdk-devel
-
安装MySQL
# Ubuntu/Debian sudo apt install mysql-server # CentOS/RHEL sudo yum install mysql-server
-
配置MySQL
sudo mysql_secure_installation
-
创建数据库和用户
CREATE DATABASE mt_ai_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'mt_ai_user'@'localhost' IDENTIFIED BY 'test_password'; GRANT ALL PRIVILEGES ON mt_ai_test.* TO 'mt_ai_user'@'localhost'; FLUSH PRIVILEGES;
2.2 应用部署
-
上传应用包到服务器
scp target/maoteng-ai.jar user@test-server:/opt/mt-ai/
-
创建启动脚本
start.sh
#!/bin/bash # 设置环境变量 export DB_USERNAME=mt_ai_user export DB_PASSWORD=test_password export OPENAI_API_KEY=your-openai-api-key # 启动应用 nohup java -jar /opt/mt-ai/maoteng-ai.jar --spring.profiles.active=test > /opt/mt-ai/mt-ai.log 2>&1 & # 输出进程ID echo $! > /opt/mt-ai/mt-ai.pid
-
创建停止脚本
stop.sh
#!/bin/bash # 获取进程ID PID=$(cat /opt/mt-ai/mt-ai.pid) # 停止应用 kill $PID # 删除PID文件 rm /opt/mt-ai/mt-ai.pid
-
设置脚本权限
chmod +x /opt/mt-ai/start.sh chmod +x /opt/mt-ai/stop.sh
-
启动应用
cd /opt/mt-ai ./start.sh
3. 生产环境部署
3.1 服务器准备
-
安装JDK 17
# Ubuntu/Debian sudo apt update sudo apt install openjdk-17-jdk # CentOS/RHEL sudo yum install java-17-openjdk-devel
-
安装MySQL
# Ubuntu/Debian sudo apt install mysql-server # CentOS/RHEL sudo yum install mysql-server
-
配置MySQL
sudo mysql_secure_installation
-
创建数据库和用户
CREATE DATABASE mt_ai_prod CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'mt_ai_user'@'localhost' IDENTIFIED BY 'prod_password'; GRANT ALL PRIVILEGES ON mt_ai_prod.* TO 'mt_ai_user'@'localhost'; FLUSH PRIVILEGES;
3.2 应用部署
-
上传应用包到服务器
scp target/maoteng-ai.jar user@prod-server:/opt/mt-ai/
-
创建启动脚本
start.sh
#!/bin/bash # 设置环境变量 export DB_USERNAME=mt_ai_user export DB_PASSWORD=prod_password export OPENAI_API_KEY=your-openai-api-key # 启动应用 nohup java -Xms2g -Xmx4g -jar /opt/mt-ai/maoteng-ai.jar --spring.profiles.active=prod > /opt/mt-ai/mt-ai.log 2>&1 & # 输出进程ID echo $! > /opt/mt-ai/mt-ai.pid
-
创建停止脚本
stop.sh
#!/bin/bash # 获取进程ID PID=$(cat /opt/mt-ai/mt-ai.pid) # 停止应用 kill $PID # 删除PID文件 rm /opt/mt-ai/mt-ai.pid
-
设置脚本权限
chmod +x /opt/mt-ai/start.sh chmod +x /opt/mt-ai/stop.sh
-
启动应用
cd /opt/mt-ai ./start.sh
4. Docker部署
4.1 创建Dockerfile
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/maoteng-ai.jar /app/mt-ai.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "mt-ai.jar"]
4.2 创建docker-compose.yml
version: '3'
services:
mt-ai:
build: .
container_name: mt-ai
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_USERNAME=mt_ai_user
- DB_PASSWORD=prod_password
- OPENAI_API_KEY=your-openai-api-key
volumes:
- ./logs:/app/logs
restart: always
4.3 构建和运行Docker容器
# 构建镜像
docker-compose build
# 启动容器
docker-compose up -d
# 查看日志
docker-compose logs -f
九、维护和更新详解
1. 日志管理
1.1 日志配置
在application.yml
中配置日志:
logging:
level:
root: INFO
com.maoteng.ai: DEBUG
org.springframework.web: INFO
org.hibernate: INFO
file:
name: logs/mt-ai.log
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
logback:
rollingpolicy:
max-file-size: 10MB
max-history: 30
1.2 日志轮转
创建logback-spring.xml
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_PATH" value="logs"/>
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/mt-ai.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/mt-ai.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 根日志级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
<!-- 应用日志级别 -->
<logger name="com.maoteng.ai" level="DEBUG"/>
</configuration>
1.3 日志查看
# 查看实时日志
tail -f logs/mt-ai.log
# 查看特定日期的日志
cat logs/mt-ai.2023-04-11.log
# 使用grep搜索日志
grep "ERROR" logs/mt-ai.log
2. 监控配置
2.1 添加监控依赖
在pom.xml
中添加监控依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
2.2 配置监控端点
在application.yml
中配置监控端点:
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
tags:
application: ${spring.application.name}
2.3 自定义健康检查
package com.maoteng.ai.config;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class CustomHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// 自定义健康检查逻辑
return Health.up().withDetail("custom", "Everything is OK").build();
}
}
3. 备份策略
3.1 数据库备份
创建数据库备份脚本 backup-db.sh
:
#!/bin/bash
# 设置变量
DB_NAME="mt_ai_prod"
DB_USER="mt_ai_user"
DB_PASS="prod_password"
BACKUP_DIR="/opt/backups/db"
DATE=$(date +%Y%m%d_%H%M%S)
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份数据库
mysqldump -u$DB_USER -p$DB_PASS $DB_NAME > $BACKUP_DIR/${DB_NAME}_${DATE}.sql
# 压缩备份
gzip $BACKUP_DIR/${DB_NAME}_${DATE}.sql
# 删除30天前的备份
find $BACKUP_DIR -name "${DB_NAME}_*.sql.gz" -mtime +30 -delete
3.2 配置文件备份
创建配置文件备份脚本 backup-config.sh
:
#!/bin/bash
# 设置变量
CONFIG_DIR="/opt/mt-ai"
BACKUP_DIR="/opt/backups/config"
DATE=$(date +%Y%m%d_%H%M%S)
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份配置文件
tar -czf $BACKUP_DIR/config_${DATE}.tar.gz $CONFIG_DIR/application*.yml
# 删除30天前的备份
find $BACKUP_DIR -name "config_*.tar.gz" -mtime +30 -delete
3.3 设置定时备份
编辑crontab:
crontab -e
添加以下内容:
# 每天凌晨2点备份数据库
0 2 * * * /opt/mt-ai/backup-db.sh
# 每周日凌晨3点备份配置文件
0 3 * * 0 /opt/mt-ai/backup-config.sh
4. 版本更新
4.1 版本号管理
在pom.xml
中管理版本号:
<properties>
<java.version>17</java.version>
<spring-boot.version>3.4.3</spring-boot.version>
<spring-ai.version>1.0.0-M6</spring-ai.version>
<mybatis-plus.version>3.5.10.1</mybatis-plus.version>
<lombok.version>1.18.22</lombok.version>
<project.version>1.0.0</project.version>
</properties>
4.2 更新流程
-
更新代码
git pull origin main
-
更新版本号
# 修改pom.xml中的版本号 mvn versions:set -DnewVersion=1.0.1
-
编译项目
mvn clean package -DskipTests
-
部署新版本
# 停止旧版本 ./stop.sh # 备份旧版本 cp maoteng-ai.jar maoteng-ai.jar.bak # 部署新版本 cp target/maoteng-ai.jar . # 启动新版本 ./start.sh
-
验证新版本
# 检查应用状态 curl http://localhost:8080/api/actuator/health
-
回滚(如果需要)
# 停止新版本 ./stop.sh # 恢复旧版本 mv maoteng-ai.jar.bak maoteng-ai.jar # 启动旧版本 ./start.sh
十、性能优化详解
1. JVM优化
1.1 JVM参数配置
在启动脚本中添加JVM参数:
java -Xms2g -Xmx4g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar maoteng-ai.jar
参数说明:
-Xms2g
: 初始堆大小-Xmx4g
: 最大堆大小-XX:MetaspaceSize=128m
: 初始元空间大小-XX:MaxMetaspaceSize=256m
: 最大元空间大小-XX:+UseG1GC
: 使用G1垃圾收集器-XX:MaxGCPauseMillis=200
: 最大GC暂停时间
1.2 GC日志配置
添加GC日志参数:
java -Xms2g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar maoteng-ai.jar
2. 数据库优化
2.1 连接池优化
在application.yml
中优化数据库连接池配置:
spring:
datasource:
hikari:
minimum-idle: 10
maximum-pool-size: 50
idle-timeout: 30000
pool-name: HikariCP
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
2.2 SQL优化
-
使用索引
CREATE INDEX idx_user_username ON user(username); CREATE INDEX idx_chat_history_user_id ON chat_history(user_id);
-
优化查询
// 使用分页查询 Page<User> page = new Page<>(1, 10); IPage<User> userPage = userService.page(page); // 使用条件查询 LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(User::getStatus, 0); List<User> users = userService.list(queryWrapper);
3. 缓存优化
3.1 添加缓存依赖
在pom.xml
中添加缓存依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3.2 配置缓存
在application.yml
中配置缓存:
spring:
cache:
type: redis
redis:
time-to-live: 3600000
cache-null-values: true
redis:
host: localhost
port: 6379
password:
database: 0
3.3 使用缓存
@Service
@CacheConfig(cacheNames = "users")
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements UserService {
@Override
@Cacheable(key = "#id")
public User getById(Long id) {
return super.getById(id);
}
@Override
@CachePut(key = "#user.id")
public boolean updateById(User user) {
return super.updateById(user);
}
@Override
@CacheEvict(key = "#id")
public boolean removeById(Long id) {
return super.removeById(id);
}
}
十一、安全配置详解
1. 添加安全依赖
在pom.xml
中添加安全依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
2. 配置安全
2.1 安全配置类
package com.maoteng.ai.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests()
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/actuator/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.httpBasic();
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
2.2 JWT工具类
package com.maoteng.ai.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Component
public class JwtTokenUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
private Key getSigningKey() {
byte[] keyBytes = secret.getBytes();
return Keys.hmacShaKeyFor(keyBytes);
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
}
3. 配置JWT属性
在application.yml
中添加JWT配置:
jwt:
secret: your-secret-key-should-be-very-long-and-secure
expiration: 86400 # 24小时
十二、常见问题解决
1. 环境配置问题
1.1 JDK版本不匹配
问题:应用启动时报错 Unsupported class file major version 61
解决方案:
- 确保使用JDK 17
- 检查JAVA_HOME环境变量是否正确设置
- 检查IDE中的JDK配置
1.2 数据库连接失败
问题:应用启动时报错 Communications link failure
解决方案:
- 检查数据库服务是否启动
- 验证数据库连接信息是否正确
- 检查数据库用户权限
- 检查防火墙设置
2. 运行错误处理
2.1 内存不足
问题:应用启动时报错 OutOfMemoryError: Java heap space
解决方案:
- 增加JVM堆内存:
-Xmx4g
- 检查内存泄漏
- 优化代码中的内存使用
2.2 端口占用
问题:应用启动时报错 Web server failed to start. Port 8080 was already in use
解决方案:
- 更改应用端口:
server.port=8081
- 查找并关闭占用端口的进程:
# Linux/Mac lsof -i :8080 kill -9 <PID> # Windows netstat -ano | findstr :8080 taskkill /PID <PID> /F
3. 性能优化建议
3.1 数据库查询慢
解决方案:
- 添加适当的索引
- 优化SQL查询
- 使用分页查询
- 配置数据库连接池
3.2 应用响应慢
解决方案:
- 使用缓存
- 优化代码逻辑
- 增加服务器资源
- 使用异步处理
十三、项目扩展建议
1. 功能扩展
-
用户管理
- 角色权限管理
- 用户组管理
- 第三方登录集成
-
AI功能
- 更多AI模型集成
- 自定义模型训练
- 图像识别功能
- 语音识别功能
-
数据分析
- 用户行为分析
- 使用统计报表
- 性能监控面板
2. 架构扩展
-
微服务架构
- 服务拆分
- 服务注册与发现
- 负载均衡
- 服务熔断与降级
-
高可用架构
- 集群部署
- 数据库主从复制
- 缓存集群
-
容器化部署
- Kubernetes集群
- 服务网格
- 自动化部署
十四、总结
AI项目是一个基于Spring Boot的智能AI应用系统,集成了多种AI模型,提供了智能对话、文档处理等功能。通过本开发指南,您可以从零开始搭建和开发这个项目,包括环境搭建、项目初始化、核心功能开发、AI功能集成、测试开发、部署准备、部署步骤、维护和更新等方面。