AI 项目详细开发步骤指南

AI 项目详细开发步骤指南

一、环境搭建详解

1. JDK 17 安装与配置

Windows 系统安装步骤:
  1. 访问 Oracle 官网下载 JDK 17 安装包:https://www.oracle.com/java/technologies/downloads/#java17
  2. 下载 Windows x64 Installer 版本
  3. 双击安装包,按照向导完成安装
  4. 配置环境变量:
    • 右键"此电脑" -> 属性 -> 高级系统设置 -> 环境变量
    • 在系统变量中新建 JAVA_HOME,值为 JDK 安装路径(如 C:\Program Files\Java\jdk-17
    • 编辑系统变量 Path,添加 %JAVA_HOME%\bin
  5. 验证安装:
    java -version
    javac -version
    
Linux 系统安装步骤:
  1. 使用包管理器安装:
    # Ubuntu/Debian
    sudo apt update
    sudo apt install openjdk-17-jdk
    
    # CentOS/RHEL
    sudo yum install java-17-openjdk-devel
    
  2. 验证安装:
    java -version
    javac -version
    

2. Maven 安装与配置

Windows 系统安装步骤:
  1. 访问 Maven 官网下载二进制包:https://maven.apache.org/download.cgi
  2. 下载 apache-maven-3.9.6-bin.zip
  3. 解压到指定目录(如 C:\Program Files\Apache\maven
  4. 配置环境变量:
    • 新建系统变量 MAVEN_HOME,值为 Maven 解压路径
    • 编辑系统变量 Path,添加 %MAVEN_HOME%\bin
  5. 验证安装:
    mvn -v
    
Linux 系统安装步骤:
  1. 下载 Maven 二进制包:
    wget https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz
    
  2. 解压到指定目录:
    sudo tar -xzf apache-maven-3.9.6-bin.tar.gz -C /opt
    
  3. 创建软链接:
    sudo ln -s /opt/apache-maven-3.9.6 /opt/maven
    
  4. 配置环境变量,编辑 ~/.bashrc~/.zshrc
    export MAVEN_HOME=/opt/maven
    export PATH=$PATH:$MAVEN_HOME/bin
    
  5. 使环境变量生效:
    source ~/.bashrc  # 或 source ~/.zshrc
    
  6. 验证安装:
    mvn -v
    

3. MySQL 安装与配置

Windows 系统安装步骤:
  1. 访问 MySQL 官网下载安装包:https://dev.mysql.com/downloads/installer/
  2. 下载 MySQL Installer
  3. 运行安装程序,选择"Server only"或"Custom"安装类型
  4. 按照向导完成安装,设置 root 密码
  5. 将 MySQL 的 bin 目录添加到系统环境变量 Path 中
  6. 验证安装:
    mysql --version
    mysql -u root -p
    
Linux 系统安装步骤:
  1. 使用包管理器安装:
    # Ubuntu/Debian
    sudo apt update
    sudo apt install mysql-server
    
    # CentOS/RHEL
    sudo yum install mysql-server
    
  2. 启动 MySQL 服务:
    # Ubuntu/Debian
    sudo systemctl start mysql
    sudo systemctl enable mysql
    
    # CentOS/RHEL
    sudo systemctl start mysqld
    sudo systemctl enable mysqld
    
  3. 设置 root 密码:
    sudo mysql_secure_installation
    
  4. 验证安装:
    mysql --version
    mysql -u root -p
    
创建项目数据库:
  1. 登录 MySQL:
    mysql -u root -p
    
  2. 创建数据库:
    CREATE DATABASE mt_ai CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    
  3. 创建项目用户并授权:
    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 安装步骤:
  1. 访问 JetBrains 官网下载 IDEA:https://www.jetbrains.com/idea/download/
  2. 下载 Community 或 Ultimate 版本
  3. 运行安装程序,按照向导完成安装
  4. 首次启动配置:
    • 选择 UI 主题
    • 安装必要插件:
      • Lombok
      • Spring Boot
      • MyBatisX
      • Maven Helper
      • Git Integration
配置 JDK:
  1. 打开 IDEA -> File -> Project Structure
  2. 在 Project 选项卡中设置 Project SDK 为 JDK 17
  3. 在 Modules 选项卡中确保 Language Level 为 17
配置 Maven:
  1. 打开 IDEA -> File -> Settings -> Build, Execution, Deployment -> Build Tools -> Maven
  2. 设置 Maven home directory 为 Maven 安装路径
  3. 设置 User settings file 为 Maven 的 settings.xml 路径
  4. 设置 Local repository 为 Maven 本地仓库路径

二、项目初始化详解

1. 创建 Spring Boot 项目

方法一:使用 Spring Initializr 网站
  1. 访问 https://start.spring.io/
  2. 填写项目信息:
    • 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
  3. 添加依赖:
    • Spring Web
    • Spring Data JPA
    • MySQL Driver
    • Lombok
    • Spring AI
  4. 点击"GENERATE"下载项目压缩包
  5. 解压到工作目录
方法二:使用 IDEA 创建
  1. 打开 IDEA -> File -> New -> Project
  2. 选择 Spring Initializr
  3. 填写项目信息(同上)
  4. 选择依赖(同上)
  5. 点击"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. 验证项目初始化

  1. 在 IDEA 中打开项目
  2. 等待 Maven 下载依赖
  3. 运行主应用类 MtAiApplication
  4. 查看控制台输出,确认应用启动成功
  5. 访问 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 用户接口测试
  1. 注册用户

    • 方法: POST
    • URL: http://localhost:8080/api/users/register
    • 请求体:
      {
        "username": "testuser",
        "password": "password",
        "email": "test@example.com"
      }
      
  2. 用户登录

    • 方法: POST
    • URL: http://localhost:8080/api/users/login?username=testuser&password=password
  3. 获取用户信息

    • 方法: GET
    • URL: http://localhost:8080/api/users/1
2.2 聊天接口测试
  1. 发送消息到OpenAI

    • 方法: POST
    • URL: http://localhost:8080/api/chat/openai?userId=1&message=Hello, OpenAI!
  2. 发送消息到Ollama

    • 方法: POST
    • URL: http://localhost:8080/api/chat/ollama?userId=1&message=Hello, Ollama!
  3. 获取聊天历史

    • 方法: 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 本地运行
  1. 在IDEA中打开项目
  2. 配置运行环境:
    • 点击"Edit Configurations"
    • 添加Spring Boot配置
    • 设置Main class为com.maoteng.ai.MtAiApplication
    • 在VM options中添加-Dspring.profiles.active=dev
  3. 点击运行按钮启动应用
1.2 命令行运行
# 编译项目
mvn clean package -DskipTests

# 运行项目(开发环境)
java -jar target/maoteng-ai.jar --spring.profiles.active=dev

2. 测试环境部署

2.1 服务器准备
  1. 安装JDK 17

    # Ubuntu/Debian
    sudo apt update
    sudo apt install openjdk-17-jdk
    
    # CentOS/RHEL
    sudo yum install java-17-openjdk-devel
    
  2. 安装MySQL

    # Ubuntu/Debian
    sudo apt install mysql-server
    
    # CentOS/RHEL
    sudo yum install mysql-server
    
  3. 配置MySQL

    sudo mysql_secure_installation
    
  4. 创建数据库和用户

    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 应用部署
  1. 上传应用包到服务器

    scp target/maoteng-ai.jar user@test-server:/opt/mt-ai/
    
  2. 创建启动脚本 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
    
  3. 创建停止脚本 stop.sh

    #!/bin/bash
    
    # 获取进程ID
    PID=$(cat /opt/mt-ai/mt-ai.pid)
    
    # 停止应用
    kill $PID
    
    # 删除PID文件
    rm /opt/mt-ai/mt-ai.pid
    
  4. 设置脚本权限

    chmod +x /opt/mt-ai/start.sh
    chmod +x /opt/mt-ai/stop.sh
    
  5. 启动应用

    cd /opt/mt-ai
    ./start.sh
    

3. 生产环境部署

3.1 服务器准备
  1. 安装JDK 17

    # Ubuntu/Debian
    sudo apt update
    sudo apt install openjdk-17-jdk
    
    # CentOS/RHEL
    sudo yum install java-17-openjdk-devel
    
  2. 安装MySQL

    # Ubuntu/Debian
    sudo apt install mysql-server
    
    # CentOS/RHEL
    sudo yum install mysql-server
    
  3. 配置MySQL

    sudo mysql_secure_installation
    
  4. 创建数据库和用户

    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 应用部署
  1. 上传应用包到服务器

    scp target/maoteng-ai.jar user@prod-server:/opt/mt-ai/
    
  2. 创建启动脚本 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
    
  3. 创建停止脚本 stop.sh

    #!/bin/bash
    
    # 获取进程ID
    PID=$(cat /opt/mt-ai/mt-ai.pid)
    
    # 停止应用
    kill $PID
    
    # 删除PID文件
    rm /opt/mt-ai/mt-ai.pid
    
  4. 设置脚本权限

    chmod +x /opt/mt-ai/start.sh
    chmod +x /opt/mt-ai/stop.sh
    
  5. 启动应用

    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 更新流程
  1. 更新代码

    git pull origin main
    
  2. 更新版本号

    # 修改pom.xml中的版本号
    mvn versions:set -DnewVersion=1.0.1
    
  3. 编译项目

    mvn clean package -DskipTests
    
  4. 部署新版本

    # 停止旧版本
    ./stop.sh
    
    # 备份旧版本
    cp maoteng-ai.jar maoteng-ai.jar.bak
    
    # 部署新版本
    cp target/maoteng-ai.jar .
    
    # 启动新版本
    ./start.sh
    
  5. 验证新版本

    # 检查应用状态
    curl http://localhost:8080/api/actuator/health
    
  6. 回滚(如果需要)

    # 停止新版本
    ./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优化
  1. 使用索引

    CREATE INDEX idx_user_username ON user(username);
    CREATE INDEX idx_chat_history_user_id ON chat_history(user_id);
    
  2. 优化查询

    // 使用分页查询
    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. 功能扩展

  1. 用户管理

    • 角色权限管理
    • 用户组管理
    • 第三方登录集成
  2. AI功能

    • 更多AI模型集成
    • 自定义模型训练
    • 图像识别功能
    • 语音识别功能
  3. 数据分析

    • 用户行为分析
    • 使用统计报表
    • 性能监控面板

2. 架构扩展

  1. 微服务架构

    • 服务拆分
    • 服务注册与发现
    • 负载均衡
    • 服务熔断与降级
  2. 高可用架构

    • 集群部署
    • 数据库主从复制
    • 缓存集群
  3. 容器化部署

    • Kubernetes集群
    • 服务网格
    • 自动化部署

十四、总结

AI项目是一个基于Spring Boot的智能AI应用系统,集成了多种AI模型,提供了智能对话、文档处理等功能。通过本开发指南,您可以从零开始搭建和开发这个项目,包括环境搭建、项目初始化、核心功能开发、AI功能集成、测试开发、部署准备、部署步骤、维护和更新等方面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值