Compare commits
10 Commits
eb5c54e4a7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3896c4deec | |||
| 3118df026c | |||
| 8b9418fe02 | |||
| e77e1cac49 | |||
|
|
094d110f30 | ||
|
|
a28bec2eec | ||
|
|
7a84d710f2 | ||
|
|
5e34e15809 | ||
|
|
a1b0d4e807 | ||
|
|
f4b54e7dc0 |
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
Binary file not shown.
18
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
18
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.1/apache-maven-3.6.1-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"java.compile.nullAnalysis.mode": "automatic"
|
||||
}
|
||||
@@ -17,8 +17,8 @@ COPY src ./src
|
||||
# Build the application
|
||||
RUN ./mvnw clean package -DskipTests
|
||||
|
||||
# Expose port 8080
|
||||
EXPOSE 8080
|
||||
# Expose port 9090
|
||||
EXPOSE 9090
|
||||
|
||||
# Run the jar file
|
||||
CMD ["java", "-jar", "target/chat-0.0.1-SNAPSHOT.jar"]
|
||||
756
README.md
756
README.md
@@ -1,54 +1,728 @@
|
||||
# 验证码认证实现说明
|
||||
# 智能问答聊天系统 (Chat Backend Server)
|
||||
|
||||
本项目实现了基于验证码的用户认证机制,不再使用密码进行登录和注册。
|
||||
## 项目简介
|
||||
|
||||
## 主要变更
|
||||
这是一个基于 **Spring Boot 3.2.4** 和 **Java 17** 开发的智能问答聊天系统后端服务。项目集成了多种AI模型服务,支持普通聊天和深度研究两种问答模式,提供完整的用户认证、会话管理、邮件服务等功能。
|
||||
|
||||
1. **验证码请求和验证DTO**
|
||||
- `VerificationCodeRequest`: 请求发送验证码的DTO
|
||||
- `VerifyCodeRequest`: 验证验证码的DTO
|
||||
- 修改了`LoginRequest`和`RegisterRequest`,移除密码字段,改为使用验证码
|
||||
**项目类型**: RESTful API 服务
|
||||
**编程语言**: Java 17
|
||||
**框架**: Spring Boot 3.2.4
|
||||
**数据库**: MySQL 8.4.5
|
||||
**ORM框架**: MyBatis-Flex 1.11.0
|
||||
**认证方式**: JWT + Spring Security
|
||||
**AI集成**: Spring AI + OpenAI API
|
||||
|
||||
2. **认证控制器**
|
||||
- 新增了`/api/auth/send-code`接口用于发送验证码
|
||||
- 修改了登录和注册接口以使用验证码认证
|
||||
## 核心功能特性
|
||||
|
||||
3. **用户服务**
|
||||
- 实现了验证码生成、存储和验证逻辑
|
||||
- 在开发模式下,验证码会在控制台输出
|
||||
- 支持使用万能验证码(默认为"123456")用于测试
|
||||
### 🤖 智能问答系统
|
||||
- **普通聊天模式**: 支持日常对话和简单问答
|
||||
- **深度研究模式**: 提供专业研究分析,包含引用资料和报告生成
|
||||
- **多模型支持**: 集成 OpenAI GPT-4o-mini 和自定义 LLM 服务
|
||||
- **流式响应**: 支持实时流式对话体验
|
||||
|
||||
4. **配置**
|
||||
- 在`application.yml`中添加了验证码相关的配置
|
||||
- 支持配置验证码过期时间、万能验证码和开发模式
|
||||
### 👤 用户管理系统
|
||||
- **用户注册/登录**: 支持邮箱注册和JWT认证
|
||||
- **密码重置**: 邮件验证码重置密码功能
|
||||
- **用户资料管理**: 个人信息修改和头像上传
|
||||
- **多种用户类型**: 个人用户、企业用户、管理员
|
||||
|
||||
5. **安全配置**
|
||||
- 更新了`SecurityConfig`以支持跨域请求
|
||||
- 优化了安全配置结构
|
||||
### 💬 会话管理
|
||||
- **会话持久化**: 保存用户对话历史
|
||||
- **上下文关联**: 支持多轮对话上下文
|
||||
- **会话分类**: 区分普通聊天和研究模式会话
|
||||
- **消息序列**: 完整的消息时序管理
|
||||
|
||||
## 使用说明
|
||||
### 🔐 安全认证
|
||||
- **JWT Token**: 无状态身份认证
|
||||
- **Spring Security**: 完整的安全框架集成
|
||||
- **密码加密**: BCrypt 密码哈希存储
|
||||
- **API权限控制**: 基于角色的访问控制
|
||||
|
||||
1. 用户注册/登录流程:
|
||||
- 用户输入邮箱或手机号,请求发送验证码
|
||||
- 系统生成验证码,在开发模式下控制台会输出验证码
|
||||
- 用户输入收到的验证码进行注册或登录
|
||||
### 📧 邮件服务
|
||||
- **验证码发送**: 注册和密码重置验证
|
||||
- **邮件模板**: 支持HTML邮件模板
|
||||
- **开发模式**: 控制台输出验证码便于调试
|
||||
|
||||
2. 万能验证码:
|
||||
- 在开发或测试环境中,可以使用配置的万能验证码(默认为"123456")
|
||||
- 无需等待验证码发送,直接使用万能验证码即可
|
||||
## 技术架构
|
||||
|
||||
3. 配置说明:
|
||||
```yaml
|
||||
app:
|
||||
verification-code:
|
||||
expiration: 300 # 验证码有效期(秒)
|
||||
master-code: "123456" # 万能验证码
|
||||
development-mode: true # 开发模式(控制台输出验证码)
|
||||
```
|
||||
### 后端技术栈
|
||||
```
|
||||
Spring Boot 3.2.4
|
||||
├── Spring Web (RESTful API)
|
||||
├── Spring Security (安全认证)
|
||||
├── Spring AI (AI模型集成)
|
||||
├── Spring Mail (邮件服务)
|
||||
├── MyBatis-Flex (ORM框架)
|
||||
├── HikariCP (数据库连接池)
|
||||
├── JWT (JSON Web Token)
|
||||
├── Lombok (代码简化)
|
||||
└── SpringDoc OpenAPI (API文档)
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
### 数据库设计
|
||||
```
|
||||
MySQL 8.4.5
|
||||
├── users (用户主表)
|
||||
├── conversations (会话表)
|
||||
├── messages (消息表)
|
||||
├── conversation_research_meta (研究元数据)
|
||||
├── membership_levels (会员等级)
|
||||
└── user_auth_accounts (第三方登录)
|
||||
```
|
||||
|
||||
1. 实际生产环境中,应使用Redis等缓存存储验证码,而非内存存储
|
||||
2. 需实现真实的短信和邮件发送服务来发送验证码
|
||||
3. 生产环境应禁用万能验证码和开发模式
|
||||
4. 当前实现为开发测试版本,注重功能实现,生产环境应加强安全性
|
||||
### 项目目录结构
|
||||
|
||||
```
|
||||
backserver/
|
||||
├── src/main/java/com/yundage/chat/
|
||||
│ ├── ChatApplication.java # 应用程序主入口
|
||||
│ ├── config/ # 配置类目录
|
||||
│ │ ├── SecurityConfig.java # Spring Security配置
|
||||
│ │ ├── JwtAuthenticationFilter.java # JWT认证过滤器
|
||||
│ │ ├── LlmApiProperties.java # LLM API配置属性
|
||||
│ │ ├── LlmWebClientConfig.java # LLM WebClient配置
|
||||
│ │ ├── OpenApiConfig.java # OpenAPI文档配置
|
||||
│ │ └── UserTypeHandler.java # 用户类型处理器
|
||||
│ ├── controller/ # 控制器层
|
||||
│ │ ├── AuthController.java # 认证相关API
|
||||
│ │ ├── ChatController.java # 聊天相关API
|
||||
│ │ └── UserController.java # 用户管理API
|
||||
│ ├── dto/ # 数据传输对象
|
||||
│ │ ├── AuthResponse.java # 认证响应DTO
|
||||
│ │ ├── ChatRequest.java # 聊天请求DTO
|
||||
│ │ ├── ChatResponse.java # 聊天响应DTO
|
||||
│ │ ├── LoginRequest.java # 登录请求DTO
|
||||
│ │ ├── RegisterRequest.java # 注册请求DTO
|
||||
│ │ ├── PasswordResetRequest.java # 密码重置请求DTO
|
||||
│ │ ├── ResetPasswordRequest.java # 重置密码请求DTO
|
||||
│ │ ├── StreamResponse.java # 流式响应DTO
|
||||
│ │ ├── UserDTO.java # 用户信息DTO
|
||||
│ │ ├── UserProfileUpdateRequest.java # 用户资料更新DTO
|
||||
│ │ ├── VerificationCodeRequest.java # 验证码请求DTO
|
||||
│ │ └── VerifyCodeRequest.java # 验证码验证DTO
|
||||
│ ├── entity/ # 实体类
|
||||
│ │ ├── User.java # 用户实体
|
||||
│ │ ├── Conversation.java # 会话实体
|
||||
│ │ ├── Message.java # 消息实体
|
||||
│ │ └── PasswordResetToken.java # 密码重置令牌实体
|
||||
│ ├── enums/ # 枚举类
|
||||
│ ├── mapper/ # MyBatis映射器
|
||||
│ ├── service/ # 服务层接口
|
||||
│ │ ├── ChatService.java # 聊天服务接口
|
||||
│ │ ├── UserService.java # 用户服务接口
|
||||
│ │ ├── EmailService.java # 邮件服务接口
|
||||
│ │ ├── LLMService.java # LLM服务接口
|
||||
│ │ └── CustomUserDetailsService.java # 用户详情服务
|
||||
│ ├── service/impl/ # 服务层实现
|
||||
│ │ ├── ChatServiceImpl.java # 聊天服务实现
|
||||
│ │ ├── UserServiceImpl.java # 用户服务实现
|
||||
│ │ ├── EmailServiceImpl.java # 邮件服务实现
|
||||
│ │ ├── SpringAIModelService.java # Spring AI模型服务
|
||||
│ │ └── CustomAPIModelService.java # 自定义API模型服务
|
||||
│ └── util/ # 工具类
|
||||
├── src/main/resources/
|
||||
│ ├── application.yml # 应用配置文件
|
||||
│ ├── schema.sql # 数据库表结构
|
||||
│ └── mapper/ # MyBatis XML映射文件
|
||||
│ ├── UserMapper.xml # 用户映射文件
|
||||
│ └── PasswordResetTokenMapper.xml # 密码重置令牌映射
|
||||
├── docs/ # 项目文档
|
||||
│ ├── chat.md # 聊天API设计文档
|
||||
│ └── 忘记密码和重置密码.md # 密码重置功能文档
|
||||
├── target/ # Maven构建输出目录
|
||||
├── Dockerfile # Docker镜像构建文件
|
||||
├── docker-compose.yml # Docker Compose配置
|
||||
├── deploy.sh # 部署脚本
|
||||
├── pom.xml # Maven项目配置
|
||||
├── 更新.md # 更新日志
|
||||
└── README.md # 项目说明文档
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 环境要求
|
||||
- **Java**: 17+
|
||||
- **Maven**: 3.6+
|
||||
- **MySQL**: 8.0+
|
||||
- **Docker**: 20.10+ (可选)
|
||||
|
||||
### 本地开发环境搭建
|
||||
|
||||
1. **克隆项目**
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd backserver
|
||||
```
|
||||
|
||||
2. **配置数据库**
|
||||
```sql
|
||||
-- 创建数据库
|
||||
CREATE DATABASE yunda_qa CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||||
|
||||
-- 导入表结构
|
||||
mysql -u root -p yunda_qa < src/main/resources/schema.sql
|
||||
```
|
||||
|
||||
3. **配置应用参数**
|
||||
编辑 `src/main/resources/application.yml`:
|
||||
```yaml
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://localhost:3306/yunda_qa
|
||||
username: root
|
||||
password: Zjj7574166
|
||||
mail:
|
||||
host: smtp.gmail.com
|
||||
username: your-email@gmail.com
|
||||
password: your-app-password
|
||||
ai:
|
||||
openai:
|
||||
api-key: your-openai-api-key
|
||||
base-url: https://api.openai.com
|
||||
```
|
||||
|
||||
4. **启动应用**
|
||||
```bash
|
||||
# 使用Maven启动
|
||||
mvn spring-boot:run
|
||||
|
||||
# 或者编译后启动
|
||||
mvn clean package
|
||||
java -jar target/chat-0.0.1-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
5. **访问应用**
|
||||
- **API服务**: http://localhost:8080
|
||||
- **API文档**: http://localhost:8080/swagger-ui.html
|
||||
- **健康检查**: http://localhost:8080/actuator/health
|
||||
|
||||
### Docker部署
|
||||
|
||||
1. **构建镜像**
|
||||
```bash
|
||||
docker build -t backserver-app .
|
||||
```
|
||||
|
||||
2. **使用Docker Compose启动**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## API接口文档
|
||||
|
||||
### 认证相关 API
|
||||
|
||||
#### 用户注册
|
||||
```http
|
||||
POST /api/auth/register
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "用户名",
|
||||
"email": "user@example.com",
|
||||
"password": "password123"
|
||||
}
|
||||
```
|
||||
|
||||
#### 用户登录
|
||||
```http
|
||||
POST /api/auth/login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"password": "password123"
|
||||
}
|
||||
```
|
||||
|
||||
#### 密码重置
|
||||
```http
|
||||
POST /api/auth/forgot-password
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "user@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
### 聊天相关 API
|
||||
|
||||
#### 发送消息
|
||||
```http
|
||||
POST /api/chat/send
|
||||
Authorization: Bearer <jwt-token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"mode": "chat", // chat | research
|
||||
"conversation_id": "12345", // 可选,会话ID
|
||||
"message": "你好,什么是AI?" // 用户消息
|
||||
}
|
||||
```
|
||||
|
||||
#### 流式聊天
|
||||
```http
|
||||
POST /api/chat/stream
|
||||
Authorization: Bearer <jwt-token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"mode": "chat",
|
||||
"conversation_id": "12345",
|
||||
"message": "请详细解释机器学习"
|
||||
}
|
||||
```
|
||||
|
||||
### 用户管理 API
|
||||
|
||||
#### 获取用户信息
|
||||
```http
|
||||
GET /api/users/profile
|
||||
Authorization: Bearer <jwt-token>
|
||||
```
|
||||
|
||||
#### 更新用户资料
|
||||
```http
|
||||
PUT /api/users/profile
|
||||
Authorization: Bearer <jwt-token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "新用户名",
|
||||
"avatarUrl": "头像URL"
|
||||
}
|
||||
```
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 应用配置 (application.yml)
|
||||
|
||||
#### 数据库配置
|
||||
```yaml
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/yunda_qa
|
||||
username: root
|
||||
password: your_password
|
||||
```
|
||||
|
||||
#### JWT配置
|
||||
```yaml
|
||||
jwt:
|
||||
secret: your-jwt-secret-key
|
||||
expiration: 86400000 # 24小时
|
||||
```
|
||||
|
||||
#### AI模型配置
|
||||
```yaml
|
||||
spring:
|
||||
ai:
|
||||
openai:
|
||||
api-key: your-openai-api-key
|
||||
base-url: https://api.openai.com
|
||||
chat:
|
||||
options:
|
||||
model: gpt-4o-mini
|
||||
|
||||
llm:
|
||||
base-url: http://127.0.0.1:8000 # 自定义LLM服务地址
|
||||
```
|
||||
|
||||
#### 邮件配置
|
||||
```yaml
|
||||
spring:
|
||||
mail:
|
||||
host: smtp.gmail.com
|
||||
port: 587
|
||||
username: your-email@gmail.com
|
||||
password: your-app-password
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
starttls:
|
||||
enable: true
|
||||
```
|
||||
|
||||
## 开发指南
|
||||
|
||||
### 代码结构说明
|
||||
|
||||
#### 控制器层 (Controller)
|
||||
- **AuthController**: 处理用户认证相关请求(注册、登录、密码重置)
|
||||
- **ChatController**: 处理聊天相关请求(发送消息、流式聊天)
|
||||
- **UserController**: 处理用户管理相关请求(资料查看、更新)
|
||||
|
||||
#### 服务层 (Service)
|
||||
- **UserService**: 用户管理业务逻辑
|
||||
- **ChatService**: 聊天业务逻辑
|
||||
- **EmailService**: 邮件发送业务逻辑
|
||||
- **LLMService**: AI模型调用业务逻辑
|
||||
|
||||
#### 数据访问层 (Mapper)
|
||||
- 使用 MyBatis-Flex 进行数据库操作
|
||||
- 支持注解和XML两种映射方式
|
||||
- 提供BaseMapper基础CRUD操作
|
||||
|
||||
#### 配置层 (Config)
|
||||
- **SecurityConfig**: Spring Security安全配置
|
||||
- **JwtAuthenticationFilter**: JWT认证过滤器
|
||||
- **OpenApiConfig**: Swagger API文档配置
|
||||
|
||||
### 开发规范
|
||||
|
||||
#### 代码注释规范
|
||||
每个类和方法都应包含完整的JavaDoc注释:
|
||||
|
||||
```java
|
||||
/**
|
||||
* 用户认证控制器
|
||||
*
|
||||
* 提供用户注册、登录、密码重置等认证相关功能
|
||||
*
|
||||
* @author YourName
|
||||
* @version 1.0
|
||||
* @since 2025-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
public class AuthController {
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*
|
||||
* @param request 注册请求参数
|
||||
* @return 注册结果响应
|
||||
* @throws UserAlreadyExistsException 用户已存在异常
|
||||
*/
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<AuthResponse> register(@Valid @RequestBody RegisterRequest request) {
|
||||
// 实现逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 异常处理
|
||||
使用统一的异常处理机制:
|
||||
|
||||
```java
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(UserNotFoundException.class)
|
||||
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
||||
.body(new ErrorResponse("USER_NOT_FOUND", ex.getMessage()));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 数据验证
|
||||
使用Bean Validation进行参数验证:
|
||||
|
||||
```java
|
||||
public class RegisterRequest {
|
||||
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 2, max = 50, message = "用户名长度必须在2-50个字符之间")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 100, message = "密码长度必须在6-100个字符之间")
|
||||
private String password;
|
||||
}
|
||||
```
|
||||
|
||||
## 测试指南
|
||||
|
||||
### 单元测试
|
||||
```bash
|
||||
# 运行所有测试
|
||||
mvn test
|
||||
|
||||
# 运行特定测试类
|
||||
mvn test -Dtest=UserServiceTest
|
||||
|
||||
# 运行测试并生成覆盖率报告
|
||||
mvn test jacoco:report
|
||||
```
|
||||
|
||||
### 集成测试
|
||||
```bash
|
||||
# 运行集成测试
|
||||
mvn verify
|
||||
|
||||
# 使用测试配置文件
|
||||
mvn test -Dspring.profiles.active=test
|
||||
```
|
||||
|
||||
### API测试
|
||||
使用Swagger UI进行API测试:
|
||||
1. 启动应用
|
||||
2. 访问 http://localhost:8080/swagger-ui.html
|
||||
3. 选择相应的API端点进行测试
|
||||
|
||||
## 部署指南
|
||||
|
||||
### 生产环境部署
|
||||
|
||||
#### 1. 环境变量配置
|
||||
```bash
|
||||
export SPRING_PROFILES_ACTIVE=prod
|
||||
export SPRING_DATASOURCE_URL=jdbc:mysql://prod-db:3306/yunda_qa
|
||||
export SPRING_DATASOURCE_USERNAME=prod_user
|
||||
export SPRING_DATASOURCE_PASSWORD=prod_password
|
||||
export JWT_SECRET=your-production-jwt-secret
|
||||
export OPENAI_API_KEY=your-production-openai-key
|
||||
```
|
||||
|
||||
#### 2. 使用Docker部署
|
||||
```bash
|
||||
# 构建生产镜像
|
||||
docker build -t backserver-app:latest .
|
||||
|
||||
# 启动服务
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
#### 3. 使用脚本部署
|
||||
```bash
|
||||
# 执行部署脚本
|
||||
chmod +x deploy.sh
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
### 监控和日志
|
||||
|
||||
#### 应用监控
|
||||
- **健康检查**: `/actuator/health`
|
||||
- **应用信息**: `/actuator/info`
|
||||
- **性能指标**: `/actuator/metrics`
|
||||
|
||||
#### 日志配置
|
||||
```yaml
|
||||
logging:
|
||||
level:
|
||||
com.yundage.chat: INFO
|
||||
org.springframework.security: WARN
|
||||
pattern:
|
||||
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
|
||||
file:
|
||||
name: logs/application.log
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
#### 1. 数据库连接失败
|
||||
```
|
||||
检查数据库配置和网络连接
|
||||
确认数据库服务是否正常运行
|
||||
验证用户名密码是否正确
|
||||
```
|
||||
|
||||
#### 2. JWT认证失败
|
||||
```
|
||||
检查JWT密钥配置
|
||||
确认token是否过期
|
||||
验证请求头格式是否正确
|
||||
```
|
||||
|
||||
#### 3. AI模型调用失败
|
||||
```
|
||||
检查API密钥是否有效
|
||||
确认网络连接是否正常
|
||||
验证模型配置是否正确
|
||||
```
|
||||
|
||||
#### 4. 邮件发送失败
|
||||
```
|
||||
检查SMTP服务器配置
|
||||
确认邮箱密码是否正确
|
||||
验证网络防火墙设置
|
||||
```
|
||||
|
||||
### 调试技巧
|
||||
|
||||
#### 启用调试日志
|
||||
```yaml
|
||||
logging:
|
||||
level:
|
||||
com.yundage.chat: DEBUG
|
||||
org.springframework.security: DEBUG
|
||||
```
|
||||
|
||||
#### 使用开发工具
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## 贡献指南
|
||||
|
||||
### 开发流程
|
||||
1. Fork项目到个人仓库
|
||||
2. 创建功能分支 (`git checkout -b feature/new-feature`)
|
||||
3. 提交代码 (`git commit -am 'Add new feature'`)
|
||||
4. 推送分支 (`git push origin feature/new-feature`)
|
||||
5. 创建Pull Request
|
||||
|
||||
### 代码规范
|
||||
- 遵循Java编码规范
|
||||
- 使用统一的代码格式化配置
|
||||
- 编写完整的单元测试
|
||||
- 添加必要的文档注释
|
||||
|
||||
## 版本历史
|
||||
|
||||
### v0.0.1-SNAPSHOT (当前版本)
|
||||
- ✅ 基础用户认证系统
|
||||
- ✅ JWT token认证
|
||||
- ✅ 普通聊天功能
|
||||
- ✅ 深度研究模式
|
||||
- ✅ 邮件服务集成
|
||||
- ✅ OpenAI API集成
|
||||
- ✅ 数据库设计和实现
|
||||
- ✅ Docker容器化支持
|
||||
- ✅ API文档生成
|
||||
|
||||
### 计划功能
|
||||
- 🔄 多模型支持扩展
|
||||
- 🔄 实时WebSocket聊天
|
||||
- 🔄 文件上传和处理
|
||||
- 🔄 会话导出功能
|
||||
- 🔄 用户权限管理
|
||||
- 🔄 API限流和监控
|
||||
- 🔄 缓存优化
|
||||
- 🔄 国际化支持
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
|
||||
|
||||
## 联系方式
|
||||
|
||||
- **项目维护者**: YunDaGe Team
|
||||
- **邮箱**: support@yundage.com
|
||||
- **项目地址**: https://github.com/yundage/backserver
|
||||
|
||||
---
|
||||
|
||||
**注意**: 本项目目前处于开发阶段,部分功能可能还在完善中。如有问题或建议,欢迎提交Issue或Pull Request。
|
||||
|
||||
## 项目架构分析和文档完善总结
|
||||
|
||||
### 已完成的工作
|
||||
|
||||
#### 📋 项目分析
|
||||
- ✅ 完整分析了项目目录结构和技术架构
|
||||
- ✅ 识别了所有主要模块和组件
|
||||
- ✅ 理解了数据库设计和业务逻辑
|
||||
- ✅ 分析了API接口设计和功能特性
|
||||
|
||||
#### 📚 文档创建和完善
|
||||
- ✅ **主README.md**: 创建了详细的项目说明文档,包含:
|
||||
- 项目简介和技术栈
|
||||
- 完整的目录结构说明
|
||||
- 快速开始指南
|
||||
- API接口文档
|
||||
- 配置说明和部署指南
|
||||
- 开发规范和测试指南
|
||||
- 故障排除和版本历史
|
||||
|
||||
- ✅ **src/README.md**: 创建了源代码目录说明文档
|
||||
- ✅ **docs/README.md**: 创建了文档目录说明文档
|
||||
|
||||
#### 💻 代码注释完善
|
||||
- ✅ **ChatApplication.java**: 添加了详细的类和方法注释
|
||||
- ✅ **AuthController.java**: 添加了完整的控制器注释
|
||||
- ✅ **User.java**: 添加了详细的实体类注释
|
||||
- ✅ **ChatService.java**: 添加了完整的服务接口注释
|
||||
|
||||
### 项目特点总结
|
||||
|
||||
#### 🏗️ 技术架构
|
||||
- **后端框架**: Spring Boot 3.2.4 + Java 17
|
||||
- **数据库**: MySQL 8.4.5 + MyBatis-Flex 1.11.0
|
||||
- **安全认证**: Spring Security + JWT
|
||||
- **AI集成**: Spring AI + OpenAI API
|
||||
- **容器化**: Docker + Docker Compose
|
||||
|
||||
#### 🚀 核心功能
|
||||
- **智能问答**: 支持普通聊天和深度研究两种模式
|
||||
- **用户管理**: 完整的注册、登录、密码重置功能
|
||||
- **会话管理**: 持久化对话历史,支持多轮对话
|
||||
- **流式响应**: 实时流式对话体验
|
||||
- **邮件服务**: 验证码发送和密码重置
|
||||
|
||||
#### 📊 数据库设计
|
||||
- **用户系统**: users, user_auth_accounts, membership_levels
|
||||
- **会话系统**: conversations, messages, conversation_research_meta
|
||||
- **安全机制**: 密码重置令牌、验证码管理
|
||||
|
||||
### 代码质量改进
|
||||
|
||||
#### 📝 注释规范
|
||||
- 所有主要类都添加了详细的JavaDoc注释
|
||||
- 方法注释包含参数说明、返回值、异常处理
|
||||
- 代码逻辑注释解释了关键业务流程
|
||||
- API注释包含使用示例和注意事项
|
||||
|
||||
#### 🔧 开发规范
|
||||
- 统一的代码注释格式
|
||||
- 清晰的异常处理机制
|
||||
- 完整的参数验证
|
||||
- 安全的权限控制
|
||||
|
||||
### 项目优势
|
||||
|
||||
#### 💪 技术优势
|
||||
- 现代化的技术栈,性能优异
|
||||
- 完整的安全认证体系
|
||||
- 灵活的AI模型集成
|
||||
- 标准的RESTful API设计
|
||||
|
||||
#### 📖 文档优势
|
||||
- 详细的项目说明文档
|
||||
- 完整的API接口文档
|
||||
- 清晰的开发指南
|
||||
- 实用的部署说明
|
||||
|
||||
#### 🛡️ 安全优势
|
||||
- JWT无状态认证
|
||||
- 密码加密存储
|
||||
- 用户数据隔离
|
||||
- 完善的权限控制
|
||||
|
||||
### 后续改进建议
|
||||
|
||||
#### 🔄 功能扩展
|
||||
- 实时WebSocket聊天
|
||||
- 文件上传和处理
|
||||
- 多模型支持扩展
|
||||
- 用户权限管理优化
|
||||
|
||||
#### 📈 性能优化
|
||||
- 缓存机制集成
|
||||
- 数据库查询优化
|
||||
- API限流和监控
|
||||
- 异步处理优化
|
||||
|
||||
#### 🧪 测试完善
|
||||
- 单元测试覆盖
|
||||
- 集成测试编写
|
||||
- API自动化测试
|
||||
- 性能测试实施
|
||||
|
||||
---
|
||||
|
||||
**总结**: 这是一个架构清晰、功能完整的智能问答聊天系统。通过详细的文档和代码注释,为后续的开发和维护提供了良好的基础。项目采用了现代化的技术栈,具有良好的扩展性和可维护性。
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
build: .
|
||||
container_name: backserver-app
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "9090:8080"
|
||||
environment:
|
||||
- SPRING_PROFILES_ACTIVE=prod
|
||||
- SPRING_DATASOURCE_URL=jdbc:mysql://101.200.154.78:3306/yunda_qa?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
||||
|
||||
163
docs/README.md
Normal file
163
docs/README.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# 项目文档目录 (docs/)
|
||||
|
||||
## 模块简介
|
||||
|
||||
`docs/` 目录包含项目的所有技术文档、API设计文档、功能说明文档等。这些文档为开发者提供详细的功能说明、使用指南和设计思路。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
docs/
|
||||
├── README.md # 文档目录说明
|
||||
├── chat.md # 聊天API设计文档
|
||||
└── 忘记密码和重置密码.md # 密码重置功能文档
|
||||
```
|
||||
|
||||
## 主要文件说明
|
||||
|
||||
### chat.md - 聊天API设计文档
|
||||
**功能描述**: 详细描述智能问答接口的设计规范和使用方法
|
||||
|
||||
**主要内容**:
|
||||
- 🤖 **问答模式**: 普通聊天(chat)和深度研究(research)两种模式
|
||||
- 📥 **请求格式**: POST /api/qa/ask 接口的请求参数说明
|
||||
- 📤 **响应格式**: 返回数据的结构和字段说明
|
||||
- 💡 **使用示例**: 包含完整的请求和响应示例
|
||||
- 🔧 **字段说明**: 每个参数的类型、必填性和用途
|
||||
|
||||
**核心接口**:
|
||||
```http
|
||||
POST /api/qa/ask
|
||||
{
|
||||
"mode": "chat|research",
|
||||
"conversation_id": "可选会话ID",
|
||||
"message": "用户问题"
|
||||
}
|
||||
```
|
||||
|
||||
**适用场景**:
|
||||
- API接口开发参考
|
||||
- 前端对接指南
|
||||
- 测试用例编写
|
||||
- 产品功能说明
|
||||
|
||||
### 忘记密码和重置密码.md - 密码重置功能文档
|
||||
**功能描述**: 详细说明用户密码重置功能的实现流程和技术细节
|
||||
|
||||
**主要内容**:
|
||||
- 🔐 **安全机制**: 验证码生成和验证流程
|
||||
- 📧 **邮件服务**: 邮件发送配置和模板设计
|
||||
- ⏰ **时效控制**: 验证码过期时间和重试机制
|
||||
- 🛡️ **安全防护**: 防止暴力破解和恶意攻击
|
||||
- 🔄 **完整流程**: 从申请重置到密码更新的全流程
|
||||
|
||||
**核心流程**:
|
||||
1. 用户申请密码重置
|
||||
2. 系统发送验证码邮件
|
||||
3. 用户输入验证码
|
||||
4. 验证通过后设置新密码
|
||||
|
||||
**适用场景**:
|
||||
- 密码重置功能开发
|
||||
- 安全机制设计参考
|
||||
- 邮件服务配置指南
|
||||
- 用户体验优化
|
||||
|
||||
## 如何使用文档
|
||||
|
||||
### 开发者使用指南
|
||||
|
||||
#### 1. API开发参考
|
||||
```bash
|
||||
# 查看聊天API设计
|
||||
cat docs/chat.md
|
||||
|
||||
# 了解密码重置流程
|
||||
cat docs/忘记密码和重置密码.md
|
||||
```
|
||||
|
||||
#### 2. 接口测试
|
||||
- 参考`chat.md`中的示例进行API测试
|
||||
- 使用Swagger UI: http://localhost:8080/swagger-ui.html
|
||||
- 使用Postman导入API文档进行测试
|
||||
|
||||
#### 3. 功能实现
|
||||
- 按照文档中的设计规范实现功能
|
||||
- 遵循文档中的数据格式和错误处理机制
|
||||
- 参考安全机制设计相关功能
|
||||
|
||||
### 文档维护规范
|
||||
|
||||
#### 1. 文档更新
|
||||
- 功能变更时及时更新相关文档
|
||||
- 保持文档与代码实现的一致性
|
||||
- 添加版本号和更新日期
|
||||
|
||||
#### 2. 文档格式
|
||||
- 使用Markdown格式编写
|
||||
- 包含清晰的标题和目录结构
|
||||
- 提供完整的示例代码
|
||||
|
||||
#### 3. 文档审查
|
||||
- 新增文档需要代码审查
|
||||
- 定期检查文档的准确性
|
||||
- 收集用户反馈并改进文档
|
||||
|
||||
## 文档扩展计划
|
||||
|
||||
### 待添加文档
|
||||
|
||||
#### 技术文档
|
||||
- **数据库设计文档**: 详细的表结构和关系说明
|
||||
- **架构设计文档**: 系统整体架构和模块关系
|
||||
- **部署指南**: 详细的生产环境部署步骤
|
||||
- **性能优化指南**: 系统性能调优建议
|
||||
|
||||
#### API文档
|
||||
- **用户管理API**: 用户注册、登录、资料管理接口
|
||||
- **会话管理API**: 会话创建、查询、删除接口
|
||||
- **系统管理API**: 系统配置、监控、日志接口
|
||||
|
||||
#### 开发文档
|
||||
- **开发环境搭建**: 详细的本地开发环境配置
|
||||
- **代码规范**: Java代码编写规范和最佳实践
|
||||
- **测试指南**: 单元测试、集成测试编写指南
|
||||
- **故障排除**: 常见问题和解决方案
|
||||
|
||||
#### 用户文档
|
||||
- **用户使用手册**: 面向最终用户的功能说明
|
||||
- **管理员指南**: 系统管理和配置说明
|
||||
- **FAQ**: 常见问题解答
|
||||
|
||||
### 文档工具
|
||||
|
||||
#### 文档生成
|
||||
- **Swagger/OpenAPI**: 自动生成API文档
|
||||
- **JavaDoc**: 生成代码文档
|
||||
- **GitBook**: 在线文档平台
|
||||
|
||||
#### 文档管理
|
||||
- **版本控制**: 使用Git管理文档版本
|
||||
- **协作编辑**: 支持多人协作编辑
|
||||
- **自动发布**: 文档自动部署和更新
|
||||
|
||||
## 重要注意事项
|
||||
|
||||
### 文档质量
|
||||
- 确保文档内容准确无误
|
||||
- 提供完整的示例和说明
|
||||
- 保持文档结构清晰易读
|
||||
|
||||
### 安全考虑
|
||||
- 不在文档中暴露敏感信息
|
||||
- 密钥和密码使用占位符
|
||||
- 生产环境配置单独管理
|
||||
|
||||
### 维护更新
|
||||
- 定期检查文档的时效性
|
||||
- 及时更新过时的信息
|
||||
- 收集用户反馈并改进
|
||||
|
||||
---
|
||||
|
||||
**提示**: 如需查看具体文档内容,请直接打开对应的.md文件。所有文档都使用Markdown格式编写,支持在GitHub、GitLab等平台上直接预览。
|
||||
4
git_test.txt
Normal file
4
git_test.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
11
|
||||
22
|
||||
|
||||
ssdasda
|
||||
308
mvnw
vendored
Normal file
308
mvnw
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /usr/local/etc/mavenrc ] ; then
|
||||
. /usr/local/etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "$(uname)" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
|
||||
else
|
||||
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=$(java-config --jre-home)
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="$(which javac)"
|
||||
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=$(which readlink)
|
||||
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="$(dirname "\"$javaExecutable\"")"
|
||||
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
|
||||
else
|
||||
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
|
||||
fi
|
||||
javaHome="$(dirname "\"$javaExecutable\"")"
|
||||
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=$(cd "$wdir/.." || exit 1; pwd)
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
# Remove \r in case we run on Windows within Git Bash
|
||||
# and check out the repository with auto CRLF management
|
||||
# enabled. Otherwise, we may read lines that are delimited with
|
||||
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
|
||||
# splitting rules.
|
||||
tr -s '\r\n' ' ' < "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
log() {
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
printf '%s\n' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
|
||||
log "$MAVEN_PROJECTBASEDIR"
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if [ -r "$wrapperJarPath" ]; then
|
||||
log "Found $wrapperJarPath"
|
||||
else
|
||||
log "Couldn't find $wrapperJarPath, downloading it ..."
|
||||
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
else
|
||||
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
fi
|
||||
while IFS="=" read -r key value; do
|
||||
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
|
||||
safeValue=$(echo "$value" | tr -d '\r')
|
||||
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
|
||||
esac
|
||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
log "Downloading from: $wrapperUrl"
|
||||
|
||||
if $cygwin; then
|
||||
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
log "Found wget ... using wget"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
else
|
||||
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
log "Found curl ... using curl"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
else
|
||||
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
else
|
||||
log "Falling back to using Java to download"
|
||||
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaSource=$(cygpath --path --windows "$javaSource")
|
||||
javaClass=$(cygpath --path --windows "$javaClass")
|
||||
fi
|
||||
if [ -e "$javaSource" ]; then
|
||||
if [ ! -e "$javaClass" ]; then
|
||||
log " - Compiling MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/javac" "$javaSource")
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
log " - Running MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
wrapperSha256Sum=""
|
||||
while IFS="=" read -r key value; do
|
||||
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
|
||||
esac
|
||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ -n "$wrapperSha256Sum" ]; then
|
||||
wrapperSha256Result=false
|
||||
if command -v sha256sum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
elif command -v shasum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
|
||||
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
|
||||
exit 1
|
||||
fi
|
||||
if [ $wrapperSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
|
||||
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
# shellcheck disable=SC2086 # safe args
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
$MAVEN_DEBUG_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||
205
mvnw.cmd
vendored
Normal file
205
mvnw.cmd
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
|
||||
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Found %WRAPPER_JAR%
|
||||
)
|
||||
) else (
|
||||
if not "%MVNW_REPOURL%" == "" (
|
||||
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %WRAPPER_URL%
|
||||
)
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
SET WRAPPER_SHA_256_SUM=""
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
|
||||
)
|
||||
IF NOT %WRAPPER_SHA_256_SUM%=="" (
|
||||
powershell -Command "&{"^
|
||||
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
|
||||
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
|
||||
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
|
||||
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
|
||||
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
|
||||
" exit 1;"^
|
||||
"}"^
|
||||
"}"
|
||||
if ERRORLEVEL 1 goto error
|
||||
)
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
%MAVEN_JAVA_EXE% ^
|
||||
%JVM_CONFIG_MAVEN_PROPS% ^
|
||||
%MAVEN_OPTS% ^
|
||||
%MAVEN_DEBUG_OPTS% ^
|
||||
-classpath %WRAPPER_JAR% ^
|
||||
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
||||
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
||||
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
||||
|
||||
cmd /C exit /B %ERROR_CODE%
|
||||
13
pom.xml
13
pom.xml
@@ -120,7 +120,11 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.netty</groupId>
|
||||
<artifactId>reactor-netty-http</artifactId>
|
||||
<version>1.1.15</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -130,6 +134,13 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<parameters>true</parameters>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
119
src/README.md
Normal file
119
src/README.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# 源代码目录 (src/)
|
||||
|
||||
## 模块简介
|
||||
|
||||
`src/` 目录包含项目的所有源代码,采用标准的Maven项目结构。主要分为主代码(`main/`)和测试代码(`test/`)两个部分。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
src/
|
||||
├── main/ # 主要源代码
|
||||
│ ├── java/ # Java源代码
|
||||
│ │ └── com/yundage/chat/ # 主包路径
|
||||
│ │ ├── ChatApplication.java # 应用程序入口
|
||||
│ │ ├── config/ # 配置类
|
||||
│ │ ├── controller/ # 控制器层
|
||||
│ │ ├── dto/ # 数据传输对象
|
||||
│ │ ├── entity/ # 实体类
|
||||
│ │ ├── enums/ # 枚举类
|
||||
│ │ ├── mapper/ # 数据访问层
|
||||
│ │ ├── service/ # 服务层
|
||||
│ │ └── util/ # 工具类
|
||||
│ └── resources/ # 资源文件
|
||||
│ ├── application.yml # 应用配置
|
||||
│ ├── schema.sql # 数据库表结构
|
||||
│ └── mapper/ # MyBatis映射文件
|
||||
└── test/ # 测试代码 (待创建)
|
||||
├── java/ # Java测试代码
|
||||
└── resources/ # 测试资源文件
|
||||
```
|
||||
|
||||
## 主要文件说明
|
||||
|
||||
### 应用程序入口
|
||||
- **ChatApplication.java**: Spring Boot应用程序主入口,包含main方法和基础配置注解
|
||||
|
||||
### 配置类 (config/)
|
||||
- **SecurityConfig.java**: Spring Security安全配置,定义认证和授权规则
|
||||
- **JwtAuthenticationFilter.java**: JWT认证过滤器,处理token验证
|
||||
- **LlmApiProperties.java**: LLM API配置属性类
|
||||
- **LlmWebClientConfig.java**: LLM服务WebClient配置
|
||||
- **OpenApiConfig.java**: Swagger/OpenAPI文档配置
|
||||
- **UserTypeHandler.java**: 用户类型枚举处理器
|
||||
|
||||
### 控制器层 (controller/)
|
||||
- **AuthController.java**: 认证相关API端点(注册、登录、密码重置)
|
||||
- **ChatController.java**: 聊天相关API端点(发送消息、流式聊天)
|
||||
- **UserController.java**: 用户管理API端点(资料查看、更新)
|
||||
|
||||
### 数据传输对象 (dto/)
|
||||
- **AuthResponse.java**: 认证响应数据结构
|
||||
- **ChatRequest.java**: 聊天请求数据结构
|
||||
- **ChatResponse.java**: 聊天响应数据结构
|
||||
- **LoginRequest.java**: 登录请求数据结构
|
||||
- **RegisterRequest.java**: 注册请求数据结构
|
||||
- **StreamResponse.java**: 流式响应数据结构
|
||||
- **UserDTO.java**: 用户信息数据传输对象
|
||||
|
||||
### 实体类 (entity/)
|
||||
- **User.java**: 用户实体,对应users表
|
||||
- **Conversation.java**: 会话实体,对应conversations表
|
||||
- **Message.java**: 消息实体,对应messages表
|
||||
- **PasswordResetToken.java**: 密码重置令牌实体
|
||||
|
||||
### 服务层 (service/)
|
||||
- **ChatService.java**: 聊天业务逻辑接口
|
||||
- **UserService.java**: 用户管理业务逻辑接口
|
||||
- **EmailService.java**: 邮件服务接口
|
||||
- **LLMService.java**: LLM模型调用接口
|
||||
- **CustomUserDetailsService.java**: Spring Security用户详情服务
|
||||
|
||||
### 服务实现 (service/impl/)
|
||||
- **ChatServiceImpl.java**: 聊天服务具体实现
|
||||
- **UserServiceImpl.java**: 用户服务具体实现
|
||||
- **EmailServiceImpl.java**: 邮件服务具体实现
|
||||
- **SpringAIModelService.java**: Spring AI模型服务实现
|
||||
- **CustomAPIModelService.java**: 自定义API模型服务实现
|
||||
|
||||
## 如何使用
|
||||
|
||||
### 开发新功能
|
||||
1. **创建实体类**: 在`entity/`目录下定义数据模型
|
||||
2. **创建DTO**: 在`dto/`目录下定义API数据传输对象
|
||||
3. **创建Mapper**: 在`mapper/`目录下定义数据访问接口
|
||||
4. **创建Service**: 在`service/`目录下定义业务逻辑接口和实现
|
||||
5. **创建Controller**: 在`controller/`目录下定义API端点
|
||||
|
||||
### 配置管理
|
||||
- 修改`application.yml`进行应用配置
|
||||
- 在`config/`目录下添加新的配置类
|
||||
- 使用`@ConfigurationProperties`绑定配置属性
|
||||
|
||||
### 数据库操作
|
||||
- 使用MyBatis-Flex进行数据库操作
|
||||
- 在`mapper/`目录下定义接口
|
||||
- 在`resources/mapper/`目录下编写XML映射文件
|
||||
|
||||
## 重要注意事项
|
||||
|
||||
### 代码规范
|
||||
- 所有类都应包含完整的JavaDoc注释
|
||||
- 遵循Spring Boot最佳实践
|
||||
- 使用Lombok简化代码
|
||||
- 统一异常处理机制
|
||||
|
||||
### 安全考虑
|
||||
- 敏感信息不要硬编码在代码中
|
||||
- 使用配置文件或环境变量管理密钥
|
||||
- 所有API都应进行适当的权限验证
|
||||
|
||||
### 性能优化
|
||||
- 合理使用缓存机制
|
||||
- 数据库查询优化
|
||||
- 异步处理长时间操作
|
||||
|
||||
### 依赖管理
|
||||
- 通过Maven管理项目依赖
|
||||
- 避免直接修改pom.xml中的版本号
|
||||
- 使用Spring Boot的依赖管理机制
|
||||
@@ -1,16 +1,71 @@
|
||||
package com.yundage.chat;
|
||||
|
||||
import com.yundage.chat.config.LlmApiProperties;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
@SpringBootApplication(exclude = {HibernateJpaAutoConfiguration.class})
|
||||
|
||||
@MapperScan("com.yundage.chat.mapper")
|
||||
/**
|
||||
* 智能问答聊天系统应用程序主入口
|
||||
*
|
||||
* 这是一个基于Spring Boot 3.2.4的智能问答聊天系统后端服务,
|
||||
* 集成了多种AI模型服务,支持普通聊天和深度研究两种问答模式。
|
||||
*
|
||||
* 主要功能特性:
|
||||
* - 🤖 智能问答系统:支持普通聊天和深度研究模式
|
||||
* - 👤 用户管理系统:注册、登录、密码重置等完整用户管理
|
||||
* - 💬 会话管理:持久化对话历史,支持多轮对话上下文
|
||||
* - 🔐 安全认证:JWT token认证,Spring Security安全框架
|
||||
* - 📧 邮件服务:验证码发送,密码重置等邮件功能
|
||||
* - 🚀 AI集成:Spring AI + OpenAI API,支持多种LLM模型
|
||||
*
|
||||
* 技术栈:
|
||||
* - Spring Boot 3.2.4 (Web框架)
|
||||
* - Spring Security (安全认证)
|
||||
* - Spring AI (AI模型集成)
|
||||
* - MyBatis-Flex 1.11.0 (ORM框架)
|
||||
* - MySQL 8.4.5 (数据库)
|
||||
* - JWT (身份认证)
|
||||
* - HikariCP (数据库连接池)
|
||||
*
|
||||
* @author YunDaGe Team
|
||||
* @version 0.0.1-SNAPSHOT
|
||||
* @since 2025-01-01
|
||||
*/
|
||||
@SpringBootApplication // Spring Boot自动配置注解
|
||||
@MapperScan("com.yundage.chat.mapper") // MyBatis Mapper接口扫描路径
|
||||
@EnableConfigurationProperties(LlmApiProperties.class) // 启用LLM API配置属性
|
||||
public class ChatApplication {
|
||||
|
||||
/**
|
||||
* 应用程序主入口方法
|
||||
*
|
||||
* 启动Spring Boot应用程序,初始化所有必要的组件和服务:
|
||||
* - Web服务器 (默认Tomcat,端口8080)
|
||||
* - 数据库连接池
|
||||
* - Spring Security安全配置
|
||||
* - MyBatis数据访问层
|
||||
* - AI模型服务
|
||||
* - 邮件服务
|
||||
*
|
||||
* 启动后可访问:
|
||||
* - API服务: http://localhost:8080
|
||||
* - API文档: http://localhost:8080/swagger-ui.html
|
||||
* - 健康检查: http://localhost:8080/actuator/health
|
||||
*
|
||||
* @param args 命令行参数,支持Spring Boot标准参数
|
||||
* 如:--server.port=8081 修改端口
|
||||
* --spring.profiles.active=prod 指定环境
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// 启动Spring Boot应用程序
|
||||
// SpringApplication.run()方法会:
|
||||
// 1. 创建ApplicationContext应用上下文
|
||||
// 2. 加载所有配置类和组件
|
||||
// 3. 启动嵌入式Web服务器
|
||||
// 4. 初始化数据库连接
|
||||
// 5. 启动所有自动配置的服务
|
||||
SpringApplication.run(ChatApplication.class, args);
|
||||
}
|
||||
}
|
||||
28
src/main/java/com/yundage/chat/config/LlmApiProperties.java
Normal file
28
src/main/java/com/yundage/chat/config/LlmApiProperties.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.yundage.chat.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* LLM API配置属性类
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "llm")
|
||||
public class LlmApiProperties {
|
||||
private String baseUrl;
|
||||
private String apiKey; // 可选,预留未来认证使用
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.yundage.chat.config;
|
||||
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.netty.transport.logging.AdvancedByteBufFormat;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Configuration
|
||||
public class LlmWebClientConfig {
|
||||
@Value(
|
||||
"${llm.base-url:}"
|
||||
)
|
||||
private String baseUrl;
|
||||
@Bean("llmWebClient")
|
||||
public WebClient llmWebClient(
|
||||
WebClient.Builder builder) {
|
||||
|
||||
return builder
|
||||
.baseUrl(baseUrl) // ← 确认是 FastAPI 的实际端口
|
||||
.clientConnector(new ReactorClientHttpConnector(
|
||||
HttpClient.create()
|
||||
.responseTimeout(Duration.ofSeconds(60))
|
||||
.wiretap("reactor.netty.http.client.HttpClient",
|
||||
LogLevel.DEBUG,
|
||||
AdvancedByteBufFormat.TEXTUAL)))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,75 @@ import org.springframework.web.bind.annotation.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
@Tag(name = "认证管理", description = "用户认证相关接口")
|
||||
/**
|
||||
* 用户认证控制器
|
||||
*
|
||||
* 提供完整的用户认证相关功能,包括:
|
||||
* - 📱 验证码发送和验证
|
||||
* - 👤 用户注册和登录
|
||||
* - 🔐 密码重置和找回
|
||||
* - 🚪 用户登出
|
||||
*
|
||||
* 安全特性:
|
||||
* - JWT Token认证机制
|
||||
* - 验证码防刷机制
|
||||
* - 密码加密存储
|
||||
* - 邮件验证码重置密码
|
||||
*
|
||||
* API路径前缀: /api/auth
|
||||
*
|
||||
* @author YunDaGe Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-01
|
||||
*/
|
||||
@RestController // 标识为REST控制器,自动序列化返回值为JSON
|
||||
@RequestMapping("/api/auth") // 设置控制器的基础路径
|
||||
@Tag(name = "认证管理", description = "用户认证相关接口") // Swagger文档标签
|
||||
public class AuthController {
|
||||
|
||||
/**
|
||||
* 用户服务接口
|
||||
*
|
||||
* 注入UserService来处理具体的业务逻辑:
|
||||
* - 用户注册和登录验证
|
||||
* - 验证码生成和验证
|
||||
* - 密码重置流程
|
||||
* - JWT Token生成和管理
|
||||
*/
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* 发送验证码接口
|
||||
*
|
||||
* 向用户的手机号或邮箱发送验证码,用于注册、登录或密码重置。
|
||||
*
|
||||
* 功能特性:
|
||||
* - 📱 支持手机短信验证码(预留功能)
|
||||
* - 📧 支持邮箱验证码
|
||||
* - ⏰ 验证码有效期5分钟
|
||||
* - 🛡️ 防刷机制:同一联系方式1分钟内只能发送一次
|
||||
* - 🔧 开发模式:控制台输出验证码便于调试
|
||||
*
|
||||
* @param request 验证码请求参数,包含联系方式(手机号或邮箱)
|
||||
* @return ApiResponse 包含发送结果,开发模式下返回验证码
|
||||
*
|
||||
* @apiNote 请求示例:
|
||||
* POST /api/auth/send-code
|
||||
* {
|
||||
* "contact": "user@example.com"
|
||||
* }
|
||||
*
|
||||
* @apiNote 响应示例:
|
||||
* {
|
||||
* "success": true,
|
||||
* "message": "验证码发送成功",
|
||||
* "data": {
|
||||
* "message": "验证码已发送",
|
||||
* "code": "123456" // 仅开发模式返回
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@PostMapping("/send-code")
|
||||
@Operation(summary = "发送验证码", description = "向手机或邮箱发送验证码")
|
||||
@ApiResponses(value = {
|
||||
@@ -30,19 +91,72 @@ public class AuthController {
|
||||
})
|
||||
public ApiResponse<?> sendVerificationCode(@Valid @RequestBody VerificationCodeRequest request) {
|
||||
try {
|
||||
// 调用用户服务发送验证码
|
||||
// 返回值:开发模式下返回验证码,生产模式返回null
|
||||
String code = userService.sendVerificationCode(request.getContact());
|
||||
|
||||
// 构建响应数据
|
||||
Map<String, String> data = new HashMap<>();
|
||||
data.put("message", "验证码已发送");
|
||||
// 仅在开发模式下返回验证码
|
||||
|
||||
// 仅在开发模式下返回验证码,便于测试
|
||||
// 生产环境中不会暴露验证码
|
||||
if (code != null) {
|
||||
data.put("code", code);
|
||||
}
|
||||
|
||||
return ApiResponse.success("验证码发送成功", data);
|
||||
} catch (Exception e) {
|
||||
// 捕获所有异常,返回统一错误响应
|
||||
// 常见异常:邮件发送失败、联系方式格式错误、发送频率限制等
|
||||
return ApiResponse.error(ErrorCode.VERIFICATION_CODE_SEND_FAILED, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册接口
|
||||
*
|
||||
* 创建新用户账号,需要验证码验证。注册成功后自动登录并返回JWT token。
|
||||
*
|
||||
* 注册流程:
|
||||
* 1. 📧 先调用发送验证码接口获取验证码
|
||||
* 2. 📝 填写注册信息(用户名、邮箱、密码、验证码)
|
||||
* 3. ✅ 系统验证验证码有效性
|
||||
* 4. 🔐 密码使用BCrypt加密存储
|
||||
* 5. 🎫 生成JWT token并返回用户信息
|
||||
*
|
||||
* 安全机制:
|
||||
* - 邮箱唯一性检查
|
||||
* - 手机号唯一性检查(预留)
|
||||
* - 验证码时效性验证
|
||||
* - 密码强度要求(6-100字符)
|
||||
*
|
||||
* @param request 注册请求参数,包含用户名、邮箱、密码、验证码
|
||||
* @return ApiResponse<AuthResponse> 包含JWT token和用户基本信息
|
||||
*
|
||||
* @apiNote 请求示例:
|
||||
* POST /api/auth/register
|
||||
* {
|
||||
* "username": "张三",
|
||||
* "email": "zhangsan@example.com",
|
||||
* "password": "password123",
|
||||
* "verificationCode": "123456"
|
||||
* }
|
||||
*
|
||||
* @apiNote 响应示例:
|
||||
* {
|
||||
* "success": true,
|
||||
* "message": "注册成功",
|
||||
* "data": {
|
||||
* "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
* "user": {
|
||||
* "id": 1,
|
||||
* "username": "张三",
|
||||
* "email": "zhangsan@example.com"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@PostMapping("/register")
|
||||
@Operation(summary = "用户注册", description = "注册新用户账号")
|
||||
@ApiResponses(value = {
|
||||
@@ -51,9 +165,12 @@ public class AuthController {
|
||||
})
|
||||
public ApiResponse<AuthResponse> register(@Valid @RequestBody RegisterRequest request) {
|
||||
try {
|
||||
// 调用用户服务进行注册
|
||||
// 内部会验证验证码、检查邮箱唯一性、加密密码、生成JWT token
|
||||
AuthResponse response = userService.register(request);
|
||||
return ApiResponse.success("注册成功", response);
|
||||
} catch (RuntimeException e) {
|
||||
// 根据异常信息返回具体的错误码和消息
|
||||
String message = e.getMessage();
|
||||
if (message.contains("邮箱已被注册")) {
|
||||
return ApiResponse.error(ErrorCode.EMAIL_ALREADY_REGISTERED, message);
|
||||
@@ -65,6 +182,7 @@ public class AuthController {
|
||||
return ApiResponse.error(ErrorCode.REGISTER_FAILED, message);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 捕获其他未预期的异常
|
||||
return ApiResponse.error(ErrorCode.REGISTER_FAILED, "注册失败");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,80 +13,277 @@ import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
@Table("users")
|
||||
public class User implements UserDetails {
|
||||
/**
|
||||
* 用户实体类
|
||||
*
|
||||
* 对应数据库中的users表,存储用户的基本信息和认证相关数据。
|
||||
* 实现了Spring Security的UserDetails接口,用于身份认证和授权。
|
||||
*
|
||||
* 数据库表结构:
|
||||
* - 主键:id (自增长)
|
||||
* - 基本信息:username, email, phone, avatar_url
|
||||
* - 认证信息:password_hash
|
||||
* - 用户类型:user_type (personal/enterprise/admin)
|
||||
* - 会员等级:membership_level_id
|
||||
* - 状态管理:status (1=正常, 0=封禁)
|
||||
* - 时间戳:created_at, updated_at, last_login_at
|
||||
*
|
||||
* 安全特性:
|
||||
* - 密码使用BCrypt加密存储
|
||||
* - 支持邮箱和手机号登录
|
||||
* - 集成Spring Security权限管理
|
||||
* - 账户状态控制(启用/禁用)
|
||||
*
|
||||
* @author YunDaGe Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-01
|
||||
*/
|
||||
@Table("users") // MyBatis-Flex注解,指定对应的数据库表名
|
||||
public class User implements UserDetails { // 实现Spring Security的UserDetails接口
|
||||
|
||||
@Id(keyType = KeyType.Auto)
|
||||
/**
|
||||
* 用户唯一标识ID
|
||||
*
|
||||
* 数据库主键,自动递增。
|
||||
* 用于唯一标识每个用户,在关联表中作为外键使用。
|
||||
*/
|
||||
@Id(keyType = KeyType.Auto) // 主键,自动生成
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户显示名称
|
||||
*
|
||||
* 用户的昵称或显示名,用于界面展示。
|
||||
* 可以重复,不作为登录凭证使用。
|
||||
* 长度限制:2-50个字符
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码哈希值
|
||||
*
|
||||
* 使用BCrypt算法加密后的密码,不存储明文密码。
|
||||
* 对应数据库字段:password_hash
|
||||
*
|
||||
* 安全要求:
|
||||
* - 原始密码长度:6-100个字符
|
||||
* - 使用BCrypt加密,强度为10
|
||||
* - 支持密码重置功能
|
||||
*/
|
||||
@Column("password_hash")
|
||||
private String passwordHash;
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
*
|
||||
* 用户的手机号,可作为登录凭证(预留功能)。
|
||||
* 在数据库中具有唯一性约束。
|
||||
* 格式:支持国内11位手机号
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 邮箱地址
|
||||
*
|
||||
* 用户的邮箱地址,主要登录凭证。
|
||||
* 在数据库中具有唯一性约束。
|
||||
* 用途:登录认证、密码重置、系统通知
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 用户头像URL
|
||||
*
|
||||
* 用户头像图片的存储地址。
|
||||
* 对应数据库字段:avatar_url
|
||||
* 支持:本地存储、云存储(OSS/CDN)
|
||||
*/
|
||||
@Column("avatar_url")
|
||||
private String avatarUrl;
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*
|
||||
* 定义用户的类型和权限级别:
|
||||
* - PERSONAL: 个人用户(默认)
|
||||
* - ENTERPRISE: 企业用户
|
||||
* - ADMIN: 管理员用户
|
||||
*
|
||||
* 使用自定义TypeHandler处理枚举与数据库的映射。
|
||||
*/
|
||||
@Column(value = "user_type", typeHandler = com.yundage.chat.config.UserTypeHandler.class)
|
||||
private UserType userType;
|
||||
|
||||
/**
|
||||
* 会员等级ID
|
||||
*
|
||||
* 关联membership_levels表的外键。
|
||||
* 用于控制用户的功能权限和使用限制:
|
||||
* - 每日消息数量限制
|
||||
* - 可用功能范围
|
||||
* - 优先级等级
|
||||
*
|
||||
* 默认值:1(基础会员)
|
||||
*/
|
||||
@Column("membership_level_id")
|
||||
private Integer membershipLevelId;
|
||||
|
||||
/**
|
||||
* 用户状态
|
||||
*
|
||||
* 控制用户账户的可用性:
|
||||
* - 1: 正常状态,可以正常使用所有功能
|
||||
* - 0: 封禁状态,禁止登录和使用系统
|
||||
*
|
||||
* 默认值:1(正常状态)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*
|
||||
* 用户账户的创建时间戳。
|
||||
* 数据库字段:created_at
|
||||
* 自动设置为当前时间(CURRENT_TIMESTAMP)
|
||||
*/
|
||||
@Column("created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*
|
||||
* 用户信息的最后更新时间戳。
|
||||
* 数据库字段:updated_at
|
||||
* 自动更新为当前时间(ON UPDATE CURRENT_TIMESTAMP)
|
||||
*/
|
||||
@Column("updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 最后登录时间
|
||||
*
|
||||
* 用户最后一次成功登录的时间戳。
|
||||
* 数据库字段:last_login_at
|
||||
* 用于统计用户活跃度和安全监控
|
||||
*/
|
||||
@Column("last_login_at")
|
||||
private LocalDateTime lastLoginAt;
|
||||
|
||||
/**
|
||||
* 默认构造函数
|
||||
*
|
||||
* 初始化用户对象的默认值:
|
||||
* - 用户类型:个人用户(PERSONAL)
|
||||
* - 会员等级:基础会员(ID=1)
|
||||
* - 用户状态:正常状态(1)
|
||||
*
|
||||
* 这些默认值确保新用户具有基本的系统访问权限。
|
||||
*/
|
||||
public User() {
|
||||
this.userType = UserType.PERSONAL;
|
||||
this.membershipLevelId = 1;
|
||||
this.status = 1;
|
||||
this.userType = UserType.PERSONAL; // 默认为个人用户
|
||||
this.membershipLevelId = 1; // 默认为基础会员
|
||||
this.status = 1; // 默认为正常状态
|
||||
}
|
||||
|
||||
// UserDetails implementation
|
||||
// ==================== Spring Security UserDetails 接口实现 ====================
|
||||
|
||||
/**
|
||||
* 获取用户权限列表
|
||||
*
|
||||
* Spring Security权限管理方法。
|
||||
* 根据用户类型返回对应的角色权限:
|
||||
* - PERSONAL -> ROLE_PERSONAL
|
||||
* - ENTERPRISE -> ROLE_ENTERPRISE
|
||||
* - ADMIN -> ROLE_ADMIN
|
||||
*
|
||||
* @return Collection<GrantedAuthority> 用户权限集合
|
||||
*/
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
// 将用户类型转换为Spring Security的权限格式
|
||||
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + userType.name()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户密码
|
||||
*
|
||||
* Spring Security认证方法。
|
||||
* 返回加密后的密码哈希值,用于密码验证。
|
||||
*
|
||||
* @return String 加密后的密码哈希值
|
||||
*/
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return passwordHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户名(登录凭证)
|
||||
*
|
||||
* Spring Security认证方法。
|
||||
* 优先使用邮箱作为用户名,邮箱为空时使用手机号。
|
||||
*
|
||||
* @return String 用户登录凭证(邮箱或手机号)
|
||||
*/
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return email != null ? email : phone;
|
||||
}
|
||||
|
||||
/**
|
||||
* 账户是否未过期
|
||||
*
|
||||
* Spring Security账户状态检查方法。
|
||||
* 当前实现:账户永不过期。
|
||||
*
|
||||
* @return boolean true表示账户未过期
|
||||
*/
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
return true; // 账户永不过期
|
||||
}
|
||||
|
||||
/**
|
||||
* 账户是否未锁定
|
||||
*
|
||||
* Spring Security账户状态检查方法。
|
||||
* 根据用户状态判断账户是否被锁定:
|
||||
* - status = 1: 账户正常,未锁定
|
||||
* - status = 0: 账户被封禁,已锁定
|
||||
*
|
||||
* @return boolean true表示账户未锁定
|
||||
*/
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return status == 1;
|
||||
return status == 1; // 状态为1表示正常,非1表示被锁定
|
||||
}
|
||||
|
||||
/**
|
||||
* 凭证是否未过期
|
||||
*
|
||||
* Spring Security凭证状态检查方法。
|
||||
* 当前实现:凭证永不过期。
|
||||
*
|
||||
* @return boolean true表示凭证未过期
|
||||
*/
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
return true; // 凭证永不过期
|
||||
}
|
||||
|
||||
/**
|
||||
* 账户是否启用
|
||||
*
|
||||
* Spring Security账户状态检查方法。
|
||||
* 根据用户状态判断账户是否启用:
|
||||
* - status = 1: 账户启用
|
||||
* - status = 0: 账户禁用
|
||||
*
|
||||
* @return boolean true表示账户已启用
|
||||
*/
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return status == 1;
|
||||
return status == 1; // 状态为1表示启用
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
|
||||
@@ -11,45 +11,202 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 聊天服务接口
|
||||
*
|
||||
* 提供智能问答聊天系统的核心业务逻辑接口定义。
|
||||
* 支持两种问答模式和完整的会话管理功能。
|
||||
*
|
||||
* 核心功能:
|
||||
* - 🤖 智能问答处理:普通聊天和深度研究模式
|
||||
* - 🔄 流式响应:实时流式对话体验
|
||||
* - 💬 会话管理:会话创建、查询、删除
|
||||
* - 📝 消息历史:完整的对话记录管理
|
||||
* - 🔐 权限控制:用户级别的数据隔离
|
||||
*
|
||||
* 支持的问答模式:
|
||||
* - chat: 普通聊天模式,适用于日常对话和简单问答
|
||||
* - research: 深度研究模式,提供专业分析和引用资料
|
||||
*
|
||||
* 技术特性:
|
||||
* - 集成多种AI模型(OpenAI GPT、自定义LLM)
|
||||
* - 支持上下文关联的多轮对话
|
||||
* - 实时流式响应(Server-Sent Events)
|
||||
* - 消息持久化存储
|
||||
* - 用户会话隔离
|
||||
*
|
||||
* @author YunDaGe Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-01
|
||||
*/
|
||||
public interface ChatService {
|
||||
|
||||
/**
|
||||
* 处理流式聊天请求
|
||||
* @param request 聊天请求
|
||||
* @param user 当前用户
|
||||
* @return SSE发射器
|
||||
*
|
||||
* 使用Server-Sent Events (SSE) 技术提供实时流式对话体验。
|
||||
* AI模型的响应会以流的形式逐步返回,用户可以实时看到回答内容。
|
||||
*
|
||||
* 处理流程:
|
||||
* 1. 🔍 验证用户权限和请求参数
|
||||
* 2. 📝 创建或获取会话上下文
|
||||
* 3. 🤖 调用AI模型进行流式生成
|
||||
* 4. 📡 通过SSE实时推送响应内容
|
||||
* 5. 💾 保存用户消息和AI回复到数据库
|
||||
* 6. 📊 记录token使用量和响应时间
|
||||
*
|
||||
* 支持的模式:
|
||||
* - chat: 普通聊天,快速响应
|
||||
* - research: 深度研究,包含引用和分析
|
||||
*
|
||||
* @param request 聊天请求参数,包含消息内容、模式、会话ID等
|
||||
* @param user 当前登录用户,用于权限验证和数据隔离
|
||||
* @return SseEmitter SSE发射器,用于推送流式响应数据
|
||||
*
|
||||
* @throws IllegalArgumentException 请求参数无效
|
||||
* @throws SecurityException 用户权限不足
|
||||
* @throws RuntimeException AI模型调用失败或其他系统错误
|
||||
*
|
||||
* @apiNote 使用示例:
|
||||
* ChatRequest request = new ChatRequest();
|
||||
* request.setMode("chat");
|
||||
* request.setMessage("什么是人工智能?");
|
||||
* SseEmitter emitter = chatService.processChatStream(request, currentUser);
|
||||
*/
|
||||
SseEmitter processChatStream(ChatRequest request, User user);
|
||||
|
||||
/**
|
||||
* 处理普通聊天请求
|
||||
* @param request 聊天请求
|
||||
* @param user 当前用户
|
||||
* @return 聊天响应
|
||||
*
|
||||
* 传统的请求-响应模式,等待AI模型完成回答后一次性返回完整结果。
|
||||
* 适用于不需要实时反馈的场景或客户端不支持SSE的情况。
|
||||
*
|
||||
* 处理流程:
|
||||
* 1. 🔍 验证用户权限和请求参数
|
||||
* 2. 📝 创建或获取会话上下文
|
||||
* 3. 🤖 调用AI模型生成完整回答
|
||||
* 4. 💾 保存用户消息和AI回复到数据库
|
||||
* 5. 📊 记录token使用量和响应时间
|
||||
* 6. 📤 返回完整的聊天响应
|
||||
*
|
||||
* 响应内容:
|
||||
* - 完整的AI回答文本
|
||||
* - 会话ID和消息ID
|
||||
* - token使用统计
|
||||
* - 响应时间统计
|
||||
* - 引用资料(research模式)
|
||||
*
|
||||
* @param request 聊天请求参数,包含消息内容、模式、会话ID等
|
||||
* @param user 当前登录用户,用于权限验证和数据隔离
|
||||
* @return ChatResponse 完整的聊天响应,包含AI回答和元数据
|
||||
*
|
||||
* @throws IllegalArgumentException 请求参数无效
|
||||
* @throws SecurityException 用户权限不足
|
||||
* @throws RuntimeException AI模型调用失败或其他系统错误
|
||||
*
|
||||
* @apiNote 使用示例:
|
||||
* ChatRequest request = new ChatRequest();
|
||||
* request.setMode("research");
|
||||
* request.setMessage("分析区块链技术的发展趋势");
|
||||
* ChatResponse response = chatService.processChat(request, currentUser);
|
||||
*/
|
||||
ChatResponse processChat(ChatRequest request, User user);
|
||||
|
||||
/**
|
||||
* 获取用户的会话列表
|
||||
* @param userId 用户ID
|
||||
* @return 会话列表
|
||||
*
|
||||
* 查询指定用户的所有会话记录,按最后更新时间倒序排列。
|
||||
* 支持数据隔离,用户只能查看自己的会话。
|
||||
*
|
||||
* 返回信息:
|
||||
* - 会话ID和标题
|
||||
* - 会话模式(chat/research)
|
||||
* - 创建和更新时间
|
||||
* - 会话状态(活跃/归档)
|
||||
* - 消息数量统计
|
||||
*
|
||||
* 数据安全:
|
||||
* - 严格的用户权限验证
|
||||
* - 只返回当前用户的会话
|
||||
* - 过滤已删除的会话
|
||||
*
|
||||
* @param userId 用户ID,必须是有效的用户标识
|
||||
* @return List<Conversation> 用户的会话列表,按更新时间倒序
|
||||
*
|
||||
* @throws IllegalArgumentException 用户ID无效
|
||||
* @throws SecurityException 用户权限不足
|
||||
*
|
||||
* @apiNote 使用示例:
|
||||
* List<Conversation> conversations = chatService.getUserConversations(userId);
|
||||
* conversations.forEach(conv -> System.out.println(conv.getTitle()));
|
||||
*/
|
||||
List<Conversation> getUserConversations(Long userId);
|
||||
|
||||
/**
|
||||
* 获取会话消息历史
|
||||
* @param conversationId 会话ID
|
||||
* @param userId 用户ID
|
||||
* @return 消息列表
|
||||
*
|
||||
* 查询指定会话的所有消息记录,按时间顺序排列。
|
||||
* 包含用户消息和AI回复的完整对话历史。
|
||||
*
|
||||
* 返回信息:
|
||||
* - 消息ID和序号
|
||||
* - 消息角色(user/assistant/system)
|
||||
* - 消息内容和时间戳
|
||||
* - token使用量和响应时间
|
||||
* - 消息状态和元数据
|
||||
*
|
||||
* 权限控制:
|
||||
* - 验证会话归属权
|
||||
* - 只允许查看自己的会话消息
|
||||
* - 过滤已删除的消息
|
||||
*
|
||||
* @param conversationId 会话ID,必须是有效的会话标识
|
||||
* @param userId 用户ID,用于权限验证
|
||||
* @return List<Message> 会话的消息列表,按时间顺序排列
|
||||
*
|
||||
* @throws IllegalArgumentException 会话ID或用户ID无效
|
||||
* @throws SecurityException 用户无权访问该会话
|
||||
* @throws RuntimeException 数据库查询失败
|
||||
*
|
||||
* @apiNote 使用示例:
|
||||
* List<Message> messages = chatService.getConversationMessages(conversationId, userId);
|
||||
* messages.forEach(msg -> System.out.println(msg.getRole() + ": " + msg.getContent()));
|
||||
*/
|
||||
List<Message> getConversationMessages(Long conversationId, Long userId);
|
||||
|
||||
/**
|
||||
* 删除用户会话
|
||||
* @param conversationId 会话ID
|
||||
* @param userId 用户ID
|
||||
* @return 是否删除成功
|
||||
*
|
||||
* 软删除指定的会话及其所有相关消息。
|
||||
* 实际上是将会话状态标记为非活跃,而不是物理删除数据。
|
||||
*
|
||||
* 删除操作:
|
||||
* - 🗑️ 标记会话为非活跃状态
|
||||
* - 📝 保留消息数据用于审计
|
||||
* - 🔒 立即生效,用户无法再访问
|
||||
* - 📊 更新用户统计信息
|
||||
*
|
||||
* 权限验证:
|
||||
* - 验证会话归属权
|
||||
* - 只允许删除自己的会话
|
||||
* - 管理员可删除任意会话(预留)
|
||||
*
|
||||
* 数据安全:
|
||||
* - 软删除机制,数据可恢复
|
||||
* - 操作日志记录
|
||||
* - 事务性操作,确保数据一致性
|
||||
*
|
||||
* @param conversationId 会话ID,必须是有效的会话标识
|
||||
* @param userId 用户ID,用于权限验证
|
||||
* @return boolean 删除是否成功,true表示删除成功
|
||||
*
|
||||
* @throws IllegalArgumentException 会话ID或用户ID无效
|
||||
* @throws SecurityException 用户无权删除该会话
|
||||
* @throws RuntimeException 数据库操作失败
|
||||
*
|
||||
* @apiNote 使用示例:
|
||||
* boolean success = chatService.deleteConversation(conversationId, userId);
|
||||
* if (success) {
|
||||
* System.out.println("会话删除成功");
|
||||
* }
|
||||
*/
|
||||
boolean deleteConversation(Long conversationId, Long userId);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.yundage.chat.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.yundage.chat.dto.ChatRequest;
|
||||
import com.yundage.chat.dto.ChatResponse;
|
||||
import com.yundage.chat.dto.StreamResponse;
|
||||
@@ -12,6 +13,7 @@ import com.yundage.chat.mapper.MessageMapper;
|
||||
import com.yundage.chat.service.ChatService;
|
||||
import com.yundage.chat.service.LLMService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
@@ -43,7 +45,7 @@ public class ChatServiceImpl implements ChatService {
|
||||
private final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
|
||||
@Autowired
|
||||
public ChatServiceImpl(LLMService llmService,
|
||||
public ChatServiceImpl(@Qualifier("customLLM") LLMService llmService,
|
||||
ConversationMapper conversationMapper,
|
||||
MessageMapper messageMapper) {
|
||||
this.llmService = llmService;
|
||||
@@ -73,13 +75,9 @@ public class ChatServiceImpl implements ChatService {
|
||||
// 准备额外参数
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
// 可以根据需要添加特定参数
|
||||
if ("research".equals(request.getMode())) {
|
||||
parameters.put("systemPrompt", "你是一个专注于研究和深度分析的AI助手。提供详细、准确和有参考价值的信息。");
|
||||
parameters.put("temperature", 0.3); // 更低的温度,提高回答的确定性
|
||||
} else {
|
||||
parameters.put("systemPrompt", "你是一个友好、有帮助的AI助手。");
|
||||
parameters.put("temperature", 0.7); // 默认温度
|
||||
}
|
||||
parameters.put("debug", false);
|
||||
parameters.put("show_thinking", false);
|
||||
parameters.put("agent_type","graph_agent");
|
||||
|
||||
// 3️⃣ 使用封装的LLM服务接口进行流式生成
|
||||
llmService.streamGenerateText(
|
||||
@@ -88,7 +86,10 @@ public class ChatServiceImpl implements ChatService {
|
||||
// 处理每个文本块
|
||||
chunk -> {
|
||||
try {
|
||||
full.append(chunk);
|
||||
String actualContent = extractActualContent(chunk);
|
||||
if (actualContent != null) {
|
||||
full.append(actualContent);
|
||||
}
|
||||
StreamResponse resp = StreamResponse.content(chunk);
|
||||
emitter.send(SseEmitter.event()
|
||||
.name("message")
|
||||
@@ -100,7 +101,10 @@ public class ChatServiceImpl implements ChatService {
|
||||
// 处理完成事件
|
||||
() -> {
|
||||
try {
|
||||
Message assistantMsg = saveAssistantMessage(convo.getId(), full.toString());
|
||||
// 清理最终的内容,移除任何残留的JSON或状态标记
|
||||
String finalContent = cleanupFinalContent(full.toString());
|
||||
|
||||
Message assistantMsg = saveAssistantMessage(convo.getId(), finalContent);
|
||||
convo.setUpdatedAt(LocalDateTime.now());
|
||||
conversationMapper.update(convo);
|
||||
|
||||
@@ -112,7 +116,7 @@ public class ChatServiceImpl implements ChatService {
|
||||
assistantMsg.getId().toString(),
|
||||
refs,
|
||||
null,
|
||||
estimateTokens(request.getMessage(), full.toString()),
|
||||
estimateTokens(request.getMessage(), finalContent),
|
||||
System.currentTimeMillis() - start
|
||||
);
|
||||
emitter.send(SseEmitter.event()
|
||||
@@ -141,6 +145,82 @@ public class ChatServiceImpl implements ChatService {
|
||||
return emitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从嵌套的JSON中提取实际内容
|
||||
* @param jsonStr JSON字符串
|
||||
* @return 提取的实际内容,或者在无法解析时返回原字符串
|
||||
*/
|
||||
private String extractActualContent(String jsonStr) {
|
||||
try {
|
||||
// 尝试解析外层JSON
|
||||
JsonNode node = mapper.readTree(jsonStr);
|
||||
|
||||
// 检查是否有状态字段,以及是否为token
|
||||
if (node.has("status") && "token".equals(node.get("status").asText()) && node.has("content")) {
|
||||
// 解析内层content字段的JSON
|
||||
String contentText = node.get("content").asText();
|
||||
if (contentText == null || contentText.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
JsonNode contentNode = mapper.readTree(contentText);
|
||||
|
||||
// 从内层JSON中获取实际内容
|
||||
if (contentNode.has("content")) {
|
||||
return contentNode.get("content").asText();
|
||||
} else if (contentNode.has("status") &&
|
||||
("done".equals(contentNode.get("status").asText()) ||
|
||||
"start".equals(contentNode.get("status").asText()))) {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 内层JSON解析失败,可能不是JSON格式
|
||||
return contentText;
|
||||
}
|
||||
} else if (node.has("status") &&
|
||||
("done".equals(node.get("status").asText()) ||
|
||||
"start".equals(node.get("status").asText()))) {
|
||||
// 处理开始或结束信号,不需要添加内容
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果无法按预期解析,返回null避免添加不必要的内容
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
// 解析错误时返回null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理最终内容,移除任何残留的JSON或状态标记
|
||||
* @param content 原始内容
|
||||
* @return 清理后的内容
|
||||
*/
|
||||
private String cleanupFinalContent(String content) {
|
||||
if (content == null || content.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 移除任何结尾的JSON格式内容
|
||||
int jsonStart = content.lastIndexOf("{\"status\":");
|
||||
if (jsonStart >= 0) {
|
||||
content = content.substring(0, jsonStart);
|
||||
}
|
||||
|
||||
// 移除"**正在分析问题**..."等提示文本
|
||||
content = content.replaceAll("\\*\\*正在分析问题\\*\\*\\.\\.\\.", "")
|
||||
.replaceAll("\\*\\*正在检索相关信息\\*\\*\\.\\.\\.", "")
|
||||
.replaceAll("\\*\\*正在生成回答\\*\\*\\.\\.\\.", "");
|
||||
|
||||
// 清理多余的空行
|
||||
content = content.replaceAll("\\n\\s*\\n\\s*\\n", "\n\n")
|
||||
.trim();
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public ChatResponse processChat(ChatRequest request, User user) {
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.yundage.chat.service.impl;
|
||||
|
||||
import com.yundage.chat.service.LLMService;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 自定义协议的LLM微服务调用实现
|
||||
*/
|
||||
@Service
|
||||
@Qualifier("customLLM")
|
||||
public class CustomAPIModelService implements LLMService {
|
||||
|
||||
private final WebClient llmClient;
|
||||
|
||||
public CustomAPIModelService(@Qualifier("llmWebClient") WebClient llmClient) {
|
||||
this.llmClient = llmClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻塞式调用,返回完整文本
|
||||
*/
|
||||
@Override
|
||||
public String generateText(String userMessage, Map<String, Object> params) {
|
||||
// 构建请求体
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("message", userMessage);
|
||||
body.put("debug", false);
|
||||
body.put("show_thinking", false);
|
||||
body.put("agent_type", "graph_agent");
|
||||
|
||||
// 打印调试信息
|
||||
System.out.println("发送请求体: " + body);
|
||||
|
||||
return llmClient.post()
|
||||
.uri("/chat")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(body)) // 使用BodyInserters确保正确发送
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.block();
|
||||
}
|
||||
|
||||
/**
|
||||
* 流式调用,通过回调函数处理响应片段
|
||||
*/
|
||||
@Override
|
||||
public void streamGenerateText(String userMessage,
|
||||
Map<String, Object> params,
|
||||
Consumer<String> onChunk,
|
||||
Runnable onComplete,
|
||||
Consumer<Throwable> onError) {
|
||||
|
||||
// --------- 1. 组装 JSON ---------- //
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("message", userMessage);
|
||||
body.put("session_id", params.getOrDefault("session_id",
|
||||
UUID.randomUUID().toString())); // 必要字段
|
||||
body.put("debug", false);
|
||||
body.put("show_thinking", false);
|
||||
body.put("agent_type", "graph_agent");
|
||||
|
||||
System.out.println("[SSE‑REQ] " + body); // 调试输出
|
||||
|
||||
// --------- 2. 发起 SSE 请求 ---------- //
|
||||
llmClient.post()
|
||||
.uri("/chat/stream")
|
||||
.contentType(MediaType.APPLICATION_JSON) // 告诉后端:我发 JSON
|
||||
.accept(MediaType.TEXT_EVENT_STREAM) // 我要拿到 SSE
|
||||
.bodyValue(body) // **一定** 用 bodyValue
|
||||
.retrieve()
|
||||
.bodyToFlux(String.class) // 每行都是一条 event data
|
||||
.doOnNext(chunk -> {
|
||||
System.out.println("[SSE‑RESP] " + chunk);
|
||||
onChunk.accept(chunk);
|
||||
})
|
||||
.doOnComplete(onComplete)
|
||||
.doOnError(e -> {
|
||||
System.err.println("[SSE‑ERR] " + e.getMessage());
|
||||
onError.accept(e);
|
||||
})
|
||||
.subscribe(); // 订阅并开始请求
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话ID,如果参数中有则使用参数中的,否则生成一个新的
|
||||
*/
|
||||
private String getSessionId(Map<String, Object> params) {
|
||||
if (params != null && params.containsKey("sessionId")) {
|
||||
return params.get("sessionId").toString();
|
||||
}
|
||||
return "debug-session-" + UUID.randomUUID().toString().substring(0, 8);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +1,107 @@
|
||||
# Spring Boot 主配置文件
|
||||
spring:
|
||||
# 数据源配置 - 用于连接MySQL数据库
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver # MySQL 8.0 驱动类
|
||||
# 数据库连接URL - 包含服务器地址、端口、数据库名和连接参数
|
||||
# useUnicode=true&characterEncoding=utf8: 启用Unicode并设置字符编码为UTF-8
|
||||
# serverTimezone=Asia/Shanghai: 设置服务器时区为上海(东八区)
|
||||
url: jdbc:mysql://101.200.154.78:3306/yunda_qa?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: mysql_Jt3yzh
|
||||
username: root # 数据库用户名
|
||||
password: mysql_Jt3yzh # 数据库密码
|
||||
|
||||
# Mail configuration
|
||||
# 邮件服务配置 - 用于发送验证码和密码重置邮件
|
||||
mail:
|
||||
host: smtp.gmail.com
|
||||
port: 587
|
||||
username: your-email@gmail.com
|
||||
password: your-app-password
|
||||
host: smtp.gmail.com # SMTP服务器地址(注意:当前配置不匹配,QQ邮箱应使用smtp.qq.com)
|
||||
port: 587 # SMTP端口号(587是TLS加密端口)
|
||||
username: 487594186@qq.com # 发送邮件的邮箱地址
|
||||
password: rqpbeqtirspsbhaa # 邮箱授权码(不是邮箱登录密码,需要在邮箱设置中开启并获取)
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
auth: true # 启用SMTP身份验证
|
||||
starttls:
|
||||
enable: true
|
||||
enable: true # 启用STARTTLS安全传输(邮件加密)
|
||||
|
||||
# AI人工智能服务配置 - 用于聊天机器人功能
|
||||
ai:
|
||||
openai:
|
||||
api-key: sk-5gAxnpuwJ9KIsea51cD05e21F1Ac4b6a99C8A97278C1F837
|
||||
base-url: https://www.jzhengda-api.com
|
||||
# OpenAI API密钥 - 用于访问AI模型服务
|
||||
api-key: sk-VXQjKSH5ZTwfGjJl58Af3f5dCa3746A99200D365D7Dc31Ff
|
||||
# API基础URL - 使用代理服务而非官方OpenAI地址
|
||||
base-url: https://jzd.itya.xyz
|
||||
chat:
|
||||
options:
|
||||
model: gpt-4o-mini
|
||||
model: qwen-plus # 使用的AI模型:通义千问Plus
|
||||
|
||||
# Spring Security 安全框架配置
|
||||
security:
|
||||
debug: true
|
||||
# MyBatis-Flex configuration
|
||||
debug: true # 开启安全调试模式(生产环境建议关闭)
|
||||
|
||||
# 自定义大语言模型服务配置 - 本地部署的LLM服务
|
||||
llm:
|
||||
base-url: http://127.0.0.1:8000 # 本地LLM服务地址(localhost:8000)
|
||||
# api-key: sk-xxxx # 预留的API认证密钥字段(暂未启用)
|
||||
|
||||
# MyBatis-Flex 数据库框架配置 - 用于数据库操作和SQL映射
|
||||
mybatis-flex:
|
||||
# Enable SQL logging
|
||||
configuration:
|
||||
# 启用SQL日志输出到控制台(开发环境用于调试SQL语句)
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
# Mapper XML location (optional)
|
||||
|
||||
# Mapper XML文件位置配置(可选)
|
||||
# classpath*:mapper/*.xml 表示扫描所有jar包中mapper目录下的xml文件
|
||||
mapper-locations: classpath*:mapper/*.xml
|
||||
# JWT configuration
|
||||
|
||||
# JWT令牌配置 - 用于用户身份认证和授权
|
||||
jwt:
|
||||
# JWT签名密钥 - 用于生成和验证JWT令牌的安全密钥
|
||||
secret: mySecretKeyForJWTTokenGenerationAndValidation
|
||||
expiration: 86400000 # 24 hours in milliseconds
|
||||
# JWT令牌过期时间(毫秒)- 86400000毫秒 = 24小时
|
||||
expiration: 86400000
|
||||
|
||||
# App configuration
|
||||
# 应用程序自定义配置
|
||||
app:
|
||||
# 密码重置页面URL - 前端重置密码页面地址
|
||||
reset-password-url: http://localhost:3000/reset-password
|
||||
# 验证码相关配置
|
||||
verification-code:
|
||||
expiration: 300 # 5 minutes in seconds
|
||||
master-code: "123456" # Master code for testing
|
||||
development-mode: true # Enable printing code to console in development mode
|
||||
expiration: 300 # 验证码过期时间(秒)- 300秒 = 5分钟
|
||||
master-code: "123456" # 万能验证码(仅用于测试环境)
|
||||
development-mode: true # 开发模式 - 启用后会在控制台打印验证码
|
||||
|
||||
# Server configuration
|
||||
# 服务器配置
|
||||
server:
|
||||
port: 8080
|
||||
port: 8080 # 应用程序运行端口号
|
||||
|
||||
# SpringDoc OpenAPI configuration
|
||||
# SpringDoc OpenAPI 文档配置 - 用于生成和展示API文档
|
||||
springdoc:
|
||||
api-docs:
|
||||
path: /api-docs
|
||||
path: /api-docs # API文档JSON格式访问路径
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html
|
||||
try-it-out-enabled: true
|
||||
operationsSorter: method
|
||||
tagsSorter: alpha
|
||||
path: /swagger-ui.html # Swagger UI界面访问路径
|
||||
try-it-out-enabled: true # 启用"试用"功能,可直接在文档中测试API
|
||||
operationsSorter: method # API操作按HTTP方法排序
|
||||
tagsSorter: alpha # API标签按字母顺序排序
|
||||
|
||||
# Logging configuration
|
||||
# 日志配置 - 控制不同组件的日志输出级别
|
||||
logging:
|
||||
level:
|
||||
com.yundage.chat: debug
|
||||
com.mybatisflex: info
|
||||
org.springframework.security: DEBUG
|
||||
# org.springframework: DEBUG
|
||||
# MyBatis相关日志级别
|
||||
org.mybatis: warn # MyBatis框架日志级别设为警告
|
||||
org.apache.ibatis: warn # Apache iBatis日志级别设为警告
|
||||
org.apache.ibatis.logging: off # 关闭iBatis详细日志输出
|
||||
org.mybatis.spring.SqlSessionUtils: warn # MyBatis Spring集成日志级别
|
||||
|
||||
# 应用程序自定义日志
|
||||
com.yundage.chat: debug # 聊天模块日志级别设为调试(显示详细信息)
|
||||
com.mybatisflex: warn # MyBatis-Flex框架日志级别
|
||||
|
||||
# Spring框架相关日志
|
||||
org.springframework.security: DEBUG # Spring Security安全框架调试日志
|
||||
reactor.netty.http.client: DEBUG # Reactor Netty HTTP客户端调试日志
|
||||
|
||||
# 可选的全局调试配置(已注释)
|
||||
# org.springframework: DEBUG # 整个Spring框架调试日志(慎用,日志量大)
|
||||
|
||||
# 全局调试模式开关(已注释,谨慎启用)
|
||||
#debug: true
|
||||
123
个人环境配置.md
Normal file
123
个人环境配置.md
Normal file
@@ -0,0 +1,123 @@
|
||||
|
||||
环境 版本 状态 验证结果
|
||||
Java 21.0.7 ✅ 符合要求 满足17+要求
|
||||
Maven 3.6.1 ✅ 符合要求 满足3.6+要求
|
||||
MySQL 8.0.27 ✅ 符合要求 满足8.0+要求
|
||||
Docker 28.0.1 ✅ 符合要求 满足20.10+要求
|
||||
|
||||
|
||||
netstat -ano | findstr LISTEN
|
||||
|
||||
查看端口
|
||||
|
||||
tasklist | findstr java
|
||||
|
||||
查看java进程
|
||||
|
||||
netsh interface ipv4 show excludedportrange protocol=tcp
|
||||
|
||||
查看保留端口
|
||||
|
||||
# 项目维护者运行一次,提交到Git
|
||||
mvn wrapper:wrapper
|
||||
git add .mvn/ mvnw mvnw.cmd
|
||||
git commit -m "Add Maven Wrapper"
|
||||
|
||||
## 开发启动
|
||||
1. 克隆项目
|
||||
2. 运行 `mvn spring-boot:run`
|
||||
3. 访问 http://localhost:8083
|
||||
|
||||
## 生产部署
|
||||
1. 运行 `docker build -t backserver-app .`
|
||||
2. 运行 `docker run -p 8083:8083 backserver-app`
|
||||
|
||||
建议创建不同环境的配置:
|
||||
application.yml - 默认配置
|
||||
application-dev.yml - 开发环境
|
||||
application-prod.yml - 生产环境
|
||||
|
||||
# application-dev.yml
|
||||
server:
|
||||
port: 8083
|
||||
logging:
|
||||
level:
|
||||
com.yundage.chat: debug
|
||||
|
||||
|
||||
日常开发:都使用 mvn spring-boot:run
|
||||
快速启动
|
||||
便于调试
|
||||
环境一致
|
||||
提交前测试:使用 mvn clean package + java -jar
|
||||
确保打包正常
|
||||
模拟生产环境
|
||||
部署:使用Docker
|
||||
环境隔离
|
||||
便于迁移
|
||||
|
||||
|
||||
# 添加单个文件
|
||||
git add git_test.txt
|
||||
|
||||
# 或者添加所有修改的文件
|
||||
git add .
|
||||
# 提交并添加提交信息
|
||||
git commit -m "添加 git_test.txt 文件"
|
||||
|
||||
# 或者更详细的提交信息
|
||||
git commit -m "test: 添加测试文件 git_test.txt"
|
||||
|
||||
一次上传:
|
||||
git push origin master
|
||||
|
||||
一次拉取
|
||||
|
||||
git pull origin master
|
||||
|
||||
|
||||
# 1. 获取最新的远程更改(不合并)
|
||||
git fetch origin
|
||||
|
||||
# 2. 查看有哪些更新
|
||||
git log HEAD..origin/master --oneline
|
||||
|
||||
# 3. 合并远程更改到本地
|
||||
git merge origin/master
|
||||
|
||||
|
||||
|
||||
# 查看最近的提交记录
|
||||
git log --oneline -5
|
||||
|
||||
# 查看当前工作区状态
|
||||
git status
|
||||
|
||||
# 查看最后一次提交的详细信息
|
||||
git show HEAD
|
||||
|
||||
# 查看提交历史
|
||||
git log
|
||||
|
||||
# 查看简洁的提交历史
|
||||
git log --oneline
|
||||
|
||||
# 查看特定文件的提交历史
|
||||
git log git_test.txt
|
||||
|
||||
# 查看提交的详细变更
|
||||
git show <commit-hash>
|
||||
|
||||
# 查看远程仓库地址
|
||||
git remote -v
|
||||
|
||||
# 查看所有配置信息
|
||||
git config --list
|
||||
|
||||
# 查看当前分支信息
|
||||
git branch -vv
|
||||
|
||||
# 查看仓库基本信息
|
||||
git remote show origin
|
||||
|
||||
|
||||
54
更新.md
Normal file
54
更新.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# 验证码认证实现说明
|
||||
|
||||
本项目实现了基于验证码的用户认证机制,不再使用密码进行登录和注册。
|
||||
|
||||
## 主要变更
|
||||
|
||||
1. **验证码请求和验证DTO**
|
||||
- `VerificationCodeRequest`: 请求发送验证码的DTO
|
||||
- `VerifyCodeRequest`: 验证验证码的DTO
|
||||
- 修改了`LoginRequest`和`RegisterRequest`,移除密码字段,改为使用验证码
|
||||
|
||||
2. **认证控制器**
|
||||
- 新增了`/api/auth/send-code`接口用于发送验证码
|
||||
- 修改了登录和注册接口以使用验证码认证
|
||||
|
||||
3. **用户服务**
|
||||
- 实现了验证码生成、存储和验证逻辑
|
||||
- 在开发模式下,验证码会在控制台输出
|
||||
- 支持使用万能验证码(默认为"123456")用于测试
|
||||
|
||||
4. **配置**
|
||||
- 在`application.yml`中添加了验证码相关的配置
|
||||
- 支持配置验证码过期时间、万能验证码和开发模式
|
||||
|
||||
5. **安全配置**
|
||||
- 更新了`SecurityConfig`以支持跨域请求
|
||||
- 优化了安全配置结构
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. 用户注册/登录流程:
|
||||
- 用户输入邮箱或手机号,请求发送验证码
|
||||
- 系统生成验证码,在开发模式下控制台会输出验证码
|
||||
- 用户输入收到的验证码进行注册或登录
|
||||
|
||||
2. 万能验证码:
|
||||
- 在开发或测试环境中,可以使用配置的万能验证码(默认为"123456")
|
||||
- 无需等待验证码发送,直接使用万能验证码即可
|
||||
|
||||
3. 配置说明:
|
||||
```yaml
|
||||
app:
|
||||
verification-code:
|
||||
expiration: 300 # 验证码有效期(秒)
|
||||
master-code: "123456" # 万能验证码
|
||||
development-mode: true # 开发模式(控制台输出验证码)
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 实际生产环境中,应使用Redis等缓存存储验证码,而非内存存储
|
||||
2. 需实现真实的短信和邮件发送服务来发送验证码
|
||||
3. 生产环境应禁用万能验证码和开发模式
|
||||
4. 当前实现为开发测试版本,注重功能实现,生产环境应加强安全性
|
||||
Reference in New Issue
Block a user