Compare commits

..

10 Commits

23 changed files with 2640 additions and 150 deletions

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

18
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View 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
View File

@@ -0,0 +1,3 @@
{
"java.compile.nullAnalysis.mode": "automatic"
}

View File

@@ -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"]

754
README.md
View File

@@ -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自动化测试
- 性能测试实施
---
**总结**: 这是一个架构清晰、功能完整的智能问答聊天系统。通过详细的文档和代码注释,为后续的开发和维护提供了良好的基础。项目采用了现代化的技术栈,具有良好的扩展性和可维护性。

View File

@@ -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
View 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
View File

@@ -0,0 +1,4 @@
11
22
ssdasda

308
mvnw vendored Normal file
View 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
View 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
View File

@@ -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
View 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的依赖管理机制

View File

@@ -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);
}
}

View 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;
}
}

View File

@@ -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();
}
}

View File

@@ -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, "注册失败");
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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("[SSEREQ] " + 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("[SSERESP] " + chunk);
onChunk.accept(chunk);
})
.doOnComplete(onComplete)
.doOnError(e -> {
System.err.println("[SSEERR] " + 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);
}
}

View File

@@ -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
View 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
View 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. 当前实现为开发测试版本,注重功能实现,生产环境应加强安全性