Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/chenfromchina123_aispring.json
T

1 line
257 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "chenfromchina123/aispring", "files": {"docs/PROJECT_TECH_OVERVIEW.md": {"e8e062a225b43bf79ca9e278530fca7c457c0fcd": {"t": 1785746586.4588833, "n": 41965, "h": "fcfb48007809378c", "c": "# AI智能学习助手系统 - 技术架构详解\n\n> 本文档详细记录系统的技术实现细节,涵盖架构设计、核心模块实现、安全机制、性能优化等方面,可作为面试技术深度的参考材料。\n\n---\n\n## 一、系统架构概览\n\n### 1.1 整体架构设计\n\n本系统采用**前后端分离 + 微服务化**的架构模式,由三个核心子系统组成:\n\n```\n┌─────────────────────────────────────────────────────────────────────────┐\n│ 用户层 (Browser) │\n└─────────────────────────────────────────────────────────────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n ┌──────────────┐ ┌──────────────┐ ┌──────────────┐\n │ vue-app │ │ word-game │ │ Nginx │\n │ (Vue 3) │ │ (Vue 3) │ │ 反向代理 │\n │ Port: 3000 │ │ Port: 5200 │ │ │\n └──────────────┘ └──────────────┘ └──────────────┘\n │ │\n └───────┬───────┘\n ▼\n ┌──────────────────────────────┐\n │ aispring 后端 │\n │ (Spring Boot 3.3.5) │\n │ Port: 5000 │\n └──────────────────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n ┌──────────────┐ ┌──────────────┐ ┌──────────────┐\n │ MySQL │ │ Redis │ │ AI Services │\n │ (主数据库) │ │ (缓存/限流) │ │ DeepSeek/豆包 │\n └──────────────┘ └──────────────┘ └──────────────┘\n │\n ▼\n ┌──────────────────────────────┐\n │ word-game-server │\n │ (Express 5 + SQLite) │\n │ Port: 5201 │\n └──────────────────────────────┘\n```\n\n### 1.2 技术栈总览\n\n| 层级 | 技术选型 | 版本 | 选型理由 |\n| -------------- | --------------------- | ------ | ----------------------------------------------- |\n| **后端框架** | Spring Boot | 3.3.5 | 企业级Java框架,生态完善,支持响应式编程 |\n| **前端框架** | Vue 3 | 3.4.0 | Composition API,更好的TypeScript支持,性能优化 |\n| **构建工具** | Vite | 5.0.0 | ES Module原生支持,HMR速度快,打包体积小 |\n| **状态管理** | Pinia | 2.1.7 | Vue官方推荐,TypeScript友好,模块化设计 |\n| **数据库** | MySQL | 8.x | 关系型数据库,支持复杂查询和事务 |\n| **缓存** | Redis | 7.x | 高性能内存数据库,支持限流、会话管理 |\n| **ORM** | Spring Data JPA | 3.x | 简化数据访问层开发,支持方法命名查询 |\n| **认证** | JWT + Spring Security | 0.12.3 | 无状态认证,支持分布式部署 |\n| **AI集成** | Spring AI | 0.8.1 | 官方AI集成框架,统一API调用方式 |\n| **数据库迁移** | Flyway | 9.x | 版本化数据库变更管理 |\n\n---\n\n## 二、后端架构详解 (aispring)\n\n### 2.1 项目结构\n\n```\naispring/\n├── src/main/java/com/aispring/\n│ ├── config/ # 配置类\n│ │ ├── SecurityConfig.java # Spring Security 安全配置\n│ │ ├── RedisConfig.java # Redis 缓存配置\n│ │ ├── CorsConfig.java # 跨域配置\n│ │ ├── JacksonConfig.java # JSON 序列化配置\n│ │ └── StorageProperties.java # 文件存储配置\n│ ├── controller/ # 控制器层 (REST API)\n│ │ ├── AuthController.java # 认证相关\n│ │ ├── AiChatController.java # AI 对话\n│ │ ├── CloudDiskController.java # 云盘管理\n│ │ └── ...\n│ ├── service/ # 服务层 (业务逻辑)\n│ │ ├── impl/ # 服务实现\n│ │ ├── AiChatService.java\n│ │ ├── AuthService.java\n│ │ └── ...\n│ ├── repository/ # 数据访问层 (JPA)\n│ ├── entity/ # 实体类\n│ ├── dto/ # 数据传输对象\n│ │ ├── request/ # 请求DTO\n│ │ └── response/ # 响应DTO\n│ ├── security/ # 安全相关\n│ │ ├── JwtAuthenticationFilter.java\n│ │ ├── CustomUserDetails.java\n│ │ └── UserDetailsServiceImpl.java\n│ ├── graphql/ # GraphQL 支持\n│ └── exception/ # 异常处理\n├── migrations/ # Flyway 数据库迁移脚本\n└── pom.xml # Maven 依赖配置\n```\n\n### 2.2 核心依赖分析 (pom.xml)\n\n```xml\n<!-- 核心框架 -->\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-web</artifactId>\n</dependency>\n\n<!-- JPA 数据访问 -->\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-data-jpa</artifactId>\n</dependency>\n\n<!-- Spring Security 安全框架 -->\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-security</artifactId>\n</dependency>\n\n<!-- JWT 令牌处理 (jjwt 0.12.3) -->\n<dependency>\n <groupId>io.jsonwebtoken</groupId>\n <artifactId>jjwt-api</artifactId>\n <version>0.12.3</version>\n</dependency>\n\n<!-- Spring AI 大模型集成 -->\n<dependency>\n <groupId>org.springframework.ai</groupId>\n <artifactId>spring-ai-openai-spring-boot-starter</artifactId>\n <version>0.8.1</version>\n</dependency>\n\n<!-- GraphQL API 支持 -->\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-graphql</artifactId>\n</dependency>\n\n<!-- Redis 缓存 -->\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-data-redis</artifactId>\n</dependency>\n\n<!-- Flyway 数据库版本管理 -->\n<dependency>\n <groupId>org.flywaydb</groupId>\n <artifactId>flyway-core</artifactId>\n</dependency>\n```\n\n**技术选型关键点:**\n\n- **Spring Boot 3.x** 要求 **Java 17+**,支持 Virtual Threads(虚拟线程)\n- **Spring AI 0.8.1** 提供统一的 AI 模型调用抽象层,支持 OpenAI 兼容 API\n- **JJWT 0.12.3** 支持 JWT 的创建、解析和验证,支持多种签名算法\n\n### 2.3 安全架构设计\n\n#### 2.3.1 Spring Security 配置\n\n```java\n@Configuration\n@EnableWebSecurity\n@EnableMethodSecurity\n@RequiredArgsConstructor\npublic class SecurityConfig {\n\n private final JwtAuthenticationFilter jwtAuthFilter;\n\n @Bean\n public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n http\n .csrf(csrf -> csrf.disable()) // 禁用 CSRF(前后端分离架构)\n .cors(cors -> {}) // 启用 CORS\n .authorizeHttpRequests(auth -> auth\n // OPTIONS 预检请求放行\n .requestMatchers(HttpMethod.OPTIONS, \"/**\").permitAll()\n // SSE 异步分发放行\n .dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll()\n // 公开端点\n .requestMatchers(\"/api/auth/**\").permitAll()\n .requestMatchers(\"/api/ask\", \"/api/ask-stream\").permitAll()\n // 其他需要认证\n .anyRequest().authenticated()\n )\n .sessionManagement(session -> session\n .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态会话\n )\n .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);\n\n return http.build();\n }\n\n @Bean\n public PasswordEncoder passwordEncoder() {\n return new BCryptPasswordEncoder(); // BCrypt 密码加密\n }\n}\n```\n\n**面试要点:**\n\n1. **为什么禁用 CSRF?** 前后端分离架构,使用 JWT Token 认证,不依赖 Cookie-Session 机制\n2. **无状态会话的意义**:服务端不存储会话信息,便于水平扩展和负载均衡\n3. **BCrypt 加密优势**:自动加盐、计算成本可调、抗彩虹表攻击\n\n#### 2.3.2 JWT 认证过滤器\n\n```java\n@Component\n@RequiredArgsConstructor\npublic class JwtAuthenticationFilter extends OncePerRequestFilter {\n\n private final JwtUtil jwtUtil;\n private final UserDetailsService userDetailsService;\n\n private static final String[] PUBLIC_PATTERNS = {\n \"/api/auth/**\", \"/swagger-ui/**\", \"/api/public-files/**\"\n };\n\n private final AntPathMatcher pathMatcher = new AntPathMatcher();\n\n @Override\n protected boolean shouldNotFilter(HttpServletRequest request) {\n // OPTIONS 请求和公开路径跳过过滤\n if (\"OPTIONS\".equalsIgnoreCase(request.getMethod())) return true;\n\n String path = request.getServletPath();\n for (String pattern : PUBLIC_PATTERNS) {\n if (pathMatcher.match(pattern, path)) return true;\n }\n return false;\n }\n\n @Override\n protected void doFilterInternal(HttpServletRequest request,\n HttpServletResponse response,\n FilterChain filterChain) {\n final String authHeader = request.getHeader(\"Authorization\");\n\n if (authHeader == null || !authHeader.startsWith(\"Bearer \")) {\n filterChain.doFilter(request, response);\n return;\n }\n\n final String jwt = authHeader.substring(7);\n final String userEmail = jwtUtil.extractUsername(jwt);\n\n if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {\n UserDetails userDetails = userDetailsService.loadUserByUsername(userEmail);\n\n if (jwtUtil.isTokenValid(jwt, userDetails)) {\n UsernamePasswordAuthenticationToken authToken =\n new UsernamePasswordAuthenticationToken(\n userDetails, null, userDetails.getAuthorities()\n );\n authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));\n SecurityContextHolder.getContext().setAuthentication(authToken);\n }\n }\n\n filterChain.doFilter(request, response);\n }\n}\n```\n\n**面试要点:**\n\n1. **OncePerRequestFilter**:确保每个请求只经过一次过滤器(避免转发/包含导致的重复过滤)\n2. **AntPathMatcher**:支持 Ant 风格路径匹配(`**`、`*`、`?` 通配符)\n3. **SecurityContextHolder**:使用 ThreadLocal 存储安全上下文,保证线程安全\n\n#### 2.3.3 RSA 前端加密传输\n\n为防止密码在传输过程中被截获,实现 RSA 非对称加密:\n\n**后端提供公钥:**\n\n```java\n@RestController\n@RequestMapping(\"/api/rsa\")\npublic class RsaController {\n\n private final RsaUtil rsaUtil;\n\n @GetMapping(\"/public-key\")\n public ApiResponse<String> getPublicKey() {\n return ApiResponse.success(\"获取成功\", rsaUtil.getPublicKey());\n }\n}\n```\n\n**前端加密实现:**\n\n```javascript\nimport JSEncrypt from \"jsencrypt\";\n\nclass RsaEncryption {\n async initialize() {\n const response = await fetch(\"/api/rsa/public-key\");\n const data = await response.json();\n this.publicKey = data.data;\n }\n\n encrypt(text) {\n const encrypt = new JSEncrypt();\n encrypt.setPublicKey(this.publicKey);\n return encrypt.encrypt(text);\n }\n}\n```\n\n**安全流程:**\n\n1. 前端获取 RSA 公钥\n2. 用户密码使用公钥加密\n3. 后端使用私钥解密\n4. 解密后的密码再使用 BCrypt 加密存储\n\n### 2.4 数据库设计\n\n#### 2.4.1 核心实体关系\n\n```\n┌─────────────┐ 1:1 ┌─────────────┐\n│ User │─────────────▶│ Admin │\n└─────────────┘ └─────────────┘\n │\n │ 1:N\n ▼\n┌─────────────┐ N:1 ┌─────────────┐\n│ UserFile │◀─────────────│ UserFolder │\n└─────────────┘ └─────────────┘\n │\n │ 1:N\n ▼\n┌─────────────┐\n│ ChatRecord │\n└─────────────┘\n │\n │ N:1\n ▼\n┌─────────────┐\n│ ChatSession │\n└─────────────┘\n```\n\n#### 2.4.2 核心实体定义\n\n**用户实体 (User.java):**\n\n```java\n@Entity\n@Table(name = \"users\", indexes = {\n @Index(name = \"idx_email\", columnList = \"email\"),\n @Index(name = \"idx_username\", columnList = \"username\")\n})\n@EntityListeners(AuditingEntityListener.class)\n@Data\npublic class User {\n\n @Id\n @GeneratedValue(strategy = GenerationType.IDENTITY)\n private Long id;\n\n @Column(nullable = false, unique = true, length = 80)\n private String username;\n\n @Column(nullable = false, unique = true, length = 120)\n private String email;\n\n @Column(name = \"password_hash\", nullable = false, length = 255)\n private String passwordHash;\n\n @CreatedDate\n @Column(name = \"created_at\", updatable = false)\n private LocalDateTime createdAt;\n\n @LastModifiedDate\n @Column(name = \"updated_at\")\n private LocalDateTime updatedAt;\n\n @OneToOne(mappedBy = \"user\", cascade = CascadeType.ALL, orphanRemoval = true)\n private Admin admin;\n\n @OneToMany(mappedBy = \"user\", cascade = CascadeType.ALL, orphanRemoval = true)\n @JsonIgnore\n private List<UserFile> files = new ArrayList<>();\n\n public void setPassword(String password) {\n BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();\n this.passwordHash = encoder.encode(password);\n }\n\n public boolean checkPassword(String password) {\n BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();\n return encoder.matches(password, this.passwordHash);\n }\n}\n```\n\n**聊天记录实体 (ChatRecord.java):**\n\n```java\n@Entity\n@Table(name = \"chat_records\", indexes = {\n @Index(name = \"idx_user_id\", columnList = \"user_id\"),\n @Index(name = \"idx_session_id\", columnList = \"session_id\"),\n @Index(name = \"idx_user_session\", columnList = \"user_id, session_id\")\n})\n@Data\n@Builder\npublic class ChatRecord {\n\n @Id\n @GeneratedValue(strategy = GenerationType.IDENTITY)\n private Long id;\n\n @Column(name = \"user_id\")\n private Long userId;\n\n @Column(name = \"session_id\", nullable = false)\n private String sessionId;\n\n @Column(name = \"message_order\", nullable = false)\n private Integer messageOrder; // 消息顺序,用于排序\n\n @Column(name = \"sender_type\", nullable = false)\n private Integer senderType; // 1: 用户, 2: AI, 3: 工具结果\n\n @Column(columnDefinition = \"TEXT\")\n private String content;\n\n @Column(name = \"reasoning_content\", columnDefinition = \"TEXT\")\n private String reasoningContent; // AI 深度思考内容(推理模型)\n\n @Column(name = \"ai_model\", length = 50)\n private String aiModel; // deepseek, doubao, deepseek-reasoner\n\n @Column(name = \"status\", length = 20)\n private String status; // pending, completed, failed, cancelled\n\n @Column(name = \"send_time\")\n private LocalDateTime sendTime;\n}\n```\n\n**面试要点:**\n\n1. **索引设计**:`idx_user_session` 复合索引优化按用户+会话查询\n2. **实体监听器**:`@CreatedDate`、`@LastModifiedDate` 自动维护时间戳\n3. **级联操作**:`CascadeType.ALL` + `orphanRemoval = true` 实现级联删除\n\n### 2.5 AI 服务集成\n\n#### 2.5.1 多模型支持架构\n\n系统支持多个 AI 模型提供商,通过策略模式实现灵活切换:\n\n```java\n@Service\npublic class AiChatServiceImpl implements AiChatService {\n\n // Spring AI 自动注入的客户端\n private final ObjectProvider<ChatClient> chatClientProvider;\n private final ObjectProvider<StreamingChatClient> streamingChatClientProvider;\n\n // 自定义模型客户端\n private ChatClient doubaoChatClient; // 豆包模型\n private ChatClient deepseekChatClient; // DeepSeek 模型\n private ChatClient deepseekReasonerClient; // DeepSeek 推理模型\n\n @Value(\"${ai.doubao.api-key:}\")\n private String doubaoApiKey;\n\n @Value(\"${ai.deepseek.api-url:}\")\n private String deepseekApiUrl;\n\n // 初始化各模型客户端\n public AiChatServiceImpl(...) {\n // 豆包模型初始化\n if (doubaoApiKey != null && !doubaoApiKey.isEmpty()) {\n OpenAiApi doubaoApi = new OpenAiApi(doubaoBaseUrl + \"/api/v3\", doubaoApiKey);\n OpenAiChatOptions doubaoOptions = OpenAiChatOptions.builder()\n .withModel(\"doubao-pro-32k\")\n .withTemperature(0.7f)\n .withMaxTokens(maxTokens)\n .build();\n this.doubaoChatClient = new OpenAiChatClient(doubaoApi, doubaoOptions);\n }\n\n // DeepSeek 模型初始化\n if (deepseekApiKey != null && !deepseekApiKey.isEmpty()) {\n OpenAiApi deepseekApi = new OpenAiApi(deepseekApiUrl, deepseekApiKey);\n this.deepseekChatClient = new OpenAiChatClient(deepseekApi);\n }\n }\n}\n```\n\n**面试要点:**\n\n1. **Spring AI 兼容性**:通过 OpenAI 兼容 API 接入国产模型(豆包、DeepSeek)\n2. **ObjectProvider 延迟注入**:避免循环依赖,按需获取 Bean\n3. **配置外部化**:API Key 和 URL 通过配置文件管理,支持环境变量覆盖\n\n#### 2.5.2 流式响应实现 (SSE)\n\n```java\n@Override\npublic SseEmitter askStream(String prompt, String sessionId, String model,\n Long userId, String ipAddress) {\n // 创建 SSE 发射器,超时 5 分钟\n SseEmitter emitter = new SseEmitter(300_000L);\n\n chatExecutor.execute(() -> {\n try {\n // 敏感信息脱敏\n String maskedPrompt = SensitiveDataMasker.mask(prompt);\n\n // 构建对话 Prompt(包含历史上下文)\n Prompt promptObj = buildPrompt(maskedPrompt, sessionId, userId, ...);\n\n // 流式调用 AI\n streamingChatClient.stream(promptObj)\n .doOnNext(chatResponse -> {\n String content = chatResponse.getResult().getOutput().getContent();\n if (content != null && !content.isEmpty()) {\n // 发送 SSE 事件\n Map<String, String> result = new HashMap<>();\n result.put(\"content\", content);\n emitter.send(SseEmitter.event()\n .data(objectMapper.writeValueAsString(result))\n .name(\"message\"));\n }\n })\n .doOnError(e -> handleError(emitter, e))\n .blockLast();\n\n // 发送完成事件\n emitter.send(SseEmitter.event().data(\"[DONE]\"));\n emitter.complete();\n\n } catch (Exception e) {\n handleError(emitter, e);\n }\n });\n\n return emitter;\n}\n```\n\n**SSE (Server-Sent Events) 技术要点:**\n\n1. **单向通信**:服务器向客户端推送数据,适合 AI 流式输出场景\n2. **自动重连**:浏览器原生支持断线重连\n3. **相比 WebSocket**:更简单、更轻量,适合单向数据流\n\n#### 2.5.3 上下文管理策略\n\n```java\n@Value(\"${ai.context.max-history-messages:30}\")\nprivate Integer maxHistoryMessages; // 最大历史消息数\n\n@Value(\"${ai.context.max-history-chars:20000}\")\nprivate Integer maxHistoryChars; // 最大历史字符数\n\nprivate Prompt buildPrompt(String prompt, String sessionId, Long userId, ...) {\n List<Message> messages = new ArrayList<>();\n\n // 1. 添加系统提示词\n messages.add(new SystemMessage(systemPrompt));\n\n // 2. 加载历史对话(带预算控制)\n if (sessionId != null && userId != null) {\n int budget = maxHistoryChars;\n List<ChatRecord> history = chatRecordRepository\n .findByUserIdAndSessionIdOrderByMessageOrderDesc(userId, sessionId,\n PageRequest.of(0, maxHistoryMessages));\n\n for (ChatRecord record : history) {\n if (budget <= 0) break;\n String role = (record.getSenderType() == 1) ? \"user\" : \"assistant\";\n String content = truncateToMax(record.getContent(), budget);\n budget -= content.length();\n messages.add(0, new Message(role, content));\n }\n }\n\n // 3. 添加当前用户输入\n messages.add(new UserMessage(prompt));\n\n return new Prompt(messages);\n}\n```\n\n**上下文管理策略:**\n\n1. **消息数量限制**:防止上下文过长导致 Token 超限\n2. **字符预算控制**:动态计算可用空间,优先保留最近对话\n3. **敏感信息脱敏**:发送给 AI 前过滤身份证、手机号等隐私数据\n\n### 2.6 GraphQL API 设计\n\n#### 2.6.1 为什么选择 GraphQL?\n\n| 特性 | REST API | GraphQL |\n| -------- | ---------------- | ---------------------- |\n| 数据获取 | 固定返回结构 | 按需获取字段 |\n| 请求次数 | 可能需要多次请求 | 单次请求获取所有数据 |\n| 版本管理 | v1, v2... | 无需版本 |\n| 适合场景 | 简单 CRUD | 复杂数据关系、前端驱动 |\n\n#### 2.6.2 声明式数据分片加载\n\n```java\n@Controller\npublic class RequirementDocGraphQLController {\n\n /**\n * 查询需求文档列表(分页)\n * 前端可通过 Fragment 指定需要的字段\n */\n @QueryMapping\n public RequirementDocConnection requirementDocs(\n @Argument Integer page,\n @Argument Integer size,\n @AuthenticationPrincipal CustomUserDetails principal) {\n\n Page<RequirementDoc> docPage = requirementDocRepository.findByUserId(\n principal.getUser().getId(),\n PageRequest.of(page, size)\n );\n return RequirementDocConnection.fromPage(docPage);\n }\n\n /**\n * 按需加载用户信息\n * 只有前端请求 user 字段时才执行\n */\n @SchemaMapping(typeName = \"RequirementDoc\", field = \"user\")\n public CompletableFuture<User> user(RequirementDoc doc) {\n return CompletableFuture.supplyAsync(() ->\n userRepository.findById(doc.getUserId()).orElse(null)\n );\n }\n\n /**\n * 按需加载统计信息\n */\n @SchemaMapping(typeName = \"RequirementDoc\", field = \"statistics\")\n public RequirementDocStatistics statistics(RequirementDoc doc) {\n int wordCount = doc.getContent() != null ? doc.getContent().length() : 0;\n int editCount = historyRepository.countByDocId(doc.getId());\n return RequirementDocStatistics.builder()\n .wordCount(wordCount)\n .editCount(editCount)\n .build();\n }\n}\n```\n\n**前端查询示例:**\n\n```graphql\nquery {\n requirementDocs(page: 0, size: 10) {\n content {\n id\n title\n ...BasicFields # 只加载基础字段\n user {\n username\n avatar\n }\n }\n }\n}\n```\n\n### 2.7 PRD 需求文档流水线\n\n#### 2.7.1 流水线架构\n\n```\n用户输入想法\n │\n ▼\n┌─────────┐ ┌─────────┐ ┌─────────────┐\n│ Outline │───▶│ Draft │───▶│ Critique │◀──┐\n│ 大纲 │ │ 初稿 │ │ 评审 │ │\n└─────────┘ └─────────┘ └─────────────┘ │\n │ │\n ▼ │\n ┌───────────┐ │\n │ Revise │────┘\n │ 修订 │ (最多3轮)\n └───────────┘\n │\n ▼\n ┌───────────┐\n │ Complete │\n │ 完成 │\n └───────────┘\n```\n\n#### 2.7.2 流水线实现\n\n```java\n@Service\npublic class PrdPipelineServiceImpl implements PrdPipelineService {\n\n private static final int MAX_REVISIONS = 3;\n private static final String APPROVAL_PHRASE = \"No issues found.\";\n\n private static final String OUTLINE_PROMPT = \"\"\"\n You are a world-class product manager. Your task is to create a structured\n outline for a Product Requirements Document (PRD)...\n \"\"\";\n\n private static final String CRITIQUE_PROMPT = \"\"\"\n You are a meticulous and critical product manager...\n If the PRD is well-structured, respond with \"No issues found.\"\n \"\"\";\n\n @Async\n @Override\n public void runPipelineAsync(String runId, String idea, Long userId) {\n String content = \"# PRD for \" + idea;\n int revision = 0;\n\n try {\n // Step 1: 生成大纲\n sendState(runId, \"Outline\", content, revision, null);\n content = aiChatService.ask(String.format(OUTLINE_PROMPT, idea), ...);\n revision++;\n\n // Step 2: 生成初稿\n sendState(runId, \"Draft\", content, revision, null);\n content = aiChatService.ask(String.format(DRAFT_PROMPT, content), ...);\n revision++;\n\n // Step 3: 评审循环\n for (int i = 0; i < MAX_REVISIONS; i++) {\n sendState(runId, \"Critique\", content, revision, null);\n String critique = aiChatService.ask(String.format(CRITIQUE_PROMPT, content), ...);\n\n if (critique.contains(APPROVAL_PHRASE)) {\n break; // 通过评审\n }\n\n // 根据评审意见修订\n content = aiChatService.ask(String.format(REVISE_PROMPT, content, critique), ...);\n revision++;\n }\n\n sendState(runId, \"Complete\", content, revision, null);\n } catch (Exception e) {\n sendState(runId, \"Error\", content, revision + 1, null);\n }\n }\n}\n```\n\n**面试要点:**\n\n1. **@Async 异步执行**:避免长时间阻塞 HTTP 请求,通过 SSE 推送进度\n2. **评审循环**:最多 3 轮修订,确保文档质量\n3. **状态推送**:通过 PrdStreamHolder 实时推送流水线状态\n\n### 2.8 限流与审计\n\n#### 2.8.1 基于 Redis 的 IP 限流\n\n```java\n@Service\npublic class RateLimitService {\n\n private final StringRedisTemplate redisTemplate;\n\n private static final String KEY_PREFIX = \"chat_limit:\";\n private static final int MAX_REQUESTS = 5; // 每日最大请求次数\n private static final Duration EXPIRATION = Duration.ofHours(24);\n\n public boolean checkAndIncrement(String ip) {\n String key = KEY_PREFIX + ip;\n\n Long count = redisTemplate.opsForValue().increment(key);\n\n // 首次请求设置过期时间\n if (count != null && count == 1) {\n redisTemplate.expire(key, EXPIRATION);\n }\n\n if (count != null && count > MAX_REQUESTS) {\n log.warn(\"Rate limit exceeded for IP: {} (Count: {})\", ip, count);\n return false;\n }\n\n return true;\n }\n}\n```\n\n**限流算法:计数器算法**\n\n- 优点:实现简单,内存占用小\n- 缺点:存在临界问题(可在临界点突发请求)\n- 改进方案:滑动窗口、令牌桶、漏桶\n\n#### 2.8.2 Token 消耗审计\n\n```java\npublic interface TokenUsageAuditService {\n void record(String provider, String modelName, Long userId, String sessionId,\n Integer inputTokens, Integer outputTokens,\n long responseTimeMs, boolean streaming);\n}\n```\n\n记录每次 AI 调用的:\n\n- Token 消耗量(输入/输出)\n- 响应时间\n- 模型提供商\n- 用户/会话信息\n\n---\n\n## 三、前端架构详解 (vue-app)\n\n### 3.1 项目结构\n\n```\nvue-app/\n├── src/\n│ ├── components/ # 可复用组件\n│ │ ├── Sidebar/ # 侧边栏组件\n│ │ ├── AppHeader.vue # 头部组件\n│ │ └── RequirementManager.vue # PRD 管理组件\n│ ├── composables/ # 组合式函数 (Hooks)\n│ │ └── useGraphQLClient.js\n│ ├── config/ # 配置文件\n│ │ └── api.js # API 端点配置\n│ ├── stores/ # Pinia 状态管理\n│ │ ├── auth.js # 认证状态\n│ │ ├── chat.js # 聊天状态\n│ │ ├── cloudDisk.js # 云盘状态\n│ │ └── theme.js # 主题状态\n│ ├── utils/ # 工具函数\n│ │ ├── request.js # Axios 封装\n│ │ ├── rsaEncryption.js # RSA 加密\n│ │ └── graphqlClient.js # GraphQL 客户端\n│ ├── views/ # 页面视图\n│ │ ├── auth/ # 认证相关页面\n│ │ ├── ChatView.vue # AI 对话页面\n│ │ ├── CloudDiskView.vue # 云盘页面\n│ │ └── WordGameView.vue # 单词记忆入口\n│ ├── router/ # 路由配置\n│ │ └── index.js\n│ ├── App.vue # 根组件\n│ └── main.js # 入口文件\n├── vite.config.js # Vite 配置\n└── package.json\n```\n\n### 3.2 Vite 构建优化\n\n```javascript\nexport default defineConfig({\n plugins: [vue()],\n resolve: {\n alias: {\n \"@\": fileURLToPath(new URL(\"./src\", import.meta.url)),\n },\n },\n server: {\n port: 3000,\n proxy: {\n \"/api\": {\n target: \"http://localhost:5000\", // 代理到后端\n changeOrigin: true,\n },\n },\n },\n build: {\n outDir: \"dist\",\n sourcemap: false,\n rollupOptions: {\n output: {\n manualChunks: {\n \"vue-vendor\": [\"vue\", \"vue-router\", \"pinia\"], // Vue 全家桶\n highlight: [\"highlight.js\"], // 代码高亮\n markdown: [\"marked\"], // Markdown 解析\n },\n },\n },\n },\n});\n```\n\n**构建优化策略:**\n\n1. **代码分割 (Code Splitting)**:将第三方库分离,利用浏览器缓存\n2. **Tree Shaking**:Vite 基于 ES Module,自动移除未使用代码\n3. **开发代理**:避免 CORS 问题,统一 API 路径\n\n### 3.3 Pinia 状态管理\n\n#### 3.3.1 认证状态管理 (auth.js)\n\n```javascript\nimport { defineStore } from \"pinia\";\nimport { computed, ref } from \"vue\";\n\nexport const useAuthStore = defineStore(\"auth\", () => {\n // 状态\n const token = ref(localStorage.getItem(\"token\") || \"\");\n const userInfo = ref(JSON.parse(localStorage.getItem(\"userInfo\") || \"null\"));\n\n // 计算属性\n const isAuthenticated = computed(() => !!token.value);\n const isAdmin = computed(() => userInfo.value?.is_admin === true);\n\n // 登录方法\n async function login(email, password) {\n const response = await request.post(API_ENDPOINTS.auth.login, {\n email,\n password,\n });\n\n const accessToken = response?.data?.access_token;\n if (accessToken) {\n token.value = accessToken;\n userInfo.value = {\n id: response.data.user_id,\n email: response.data.email,\n username: response.data.username,\n is_admin: response.data.is_admin,\n };\n\n // 持久化存储\n localStorage.setItem(\"token\", token.value);\n localStorage.setItem(\"userInfo\", JSON.stringify(userInfo.value));\n\n return { success: true };\n }\n return { success: false };\n }\n\n // 退出登录\n function logout() {\n token.value = \"\";\n userInfo.value = null;\n localStorage.removeItem(\"token\");\n localStorage.removeItem(\"userInfo\");\n }\n\n return {\n token,\n userInfo,\n isAuthenticated,\n isAdmin,\n login,\n logout,\n };\n});\n```\n\n**Pinia vs Vuex 对比:**\n| 特性 | Vuex | Pinia |\n|------|------|-------|\n| 状态定义 | state 对象 | ref/reactive |\n| 修改方式 | mutations | 直接修改 |\n| TypeScript | 需要额外配置 | 原生支持 |\n| 模块化 | modules | 独立 Store |\n| DevTools | 支持 | 支持 |\n\n#### 3.3.2 聊天状态管理 (chat.js)\n\n```javascript\nexport const useChatStore = defineStore(\"chat\", () => {\n const sessions = ref([]);\n const currentSessionId = ref(null);\n const messages = ref([]);\n const isLoading = ref(false);\n const abortController = ref(null);\n const selectedModel = ref(localStorage.getItem(\"selectedModel\") || \"deepseek-chat\");\n\n // 流式发送消息\n async function sendMessage(content, onChunk) {\n isLoading.value = true;\n abortController.value = new AbortController();\n\n // 添加用户消息\n messages.value.push({\n role: \"user\",\n content,\n timestamp: new Date().toISOString(),\n });\n\n // 添加 AI 消息占位符\n const aiMessage = {\n role: \"assistant\",\n content: \"\",\n reasoning_content: \"\",\n isStreaming: true,\n timestamp: new Date().toISOString(),\n };\n messages.value.push(aiMessage);\n\n try {\n const response = await fetch(\"/api/ask-stream\", {\n method: \"POST\",\n signal: abortController.value.signal,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${authStore.token}`,\n },\n body: JSON.stringify({\n prompt: content,\n session_id: currentSessionId.value,\n model: selectedModel.value,\n }),\n });\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"data:\")) {\n const data = line.slice(5).trim();\n if (data === \"[DONE]\") continue;\n\n const parsed = JSON.parse(data);\n\n // 处理推理内容\n if (parsed.reasoning_content) {\n aiMessage.reasoning_content += parsed.reasoning_content;\n }\n\n // 处理回复内容\n if (parsed.content) {\n aiMessage.content += parsed.content;\n }\n }\n }\n }\n\n aiMessage.isStreaming = false;\n return { success: true };\n } catch (error) {\n if (error.name === \"AbortError\") {\n console.log(\"Generation aborted by user\");\n return { success: true, aborted: true };\n }\n throw error;\n } finally {\n isLoading.value = false;\n }\n }\n\n // 停止生成\n function stopGeneration() {\n if (abortController.value) {\n abortController.value.abort();\n }\n }\n\n return {\n sessions,\n messages,\n isLoading,\n selectedModel,\n sendMessage,\n stopGeneration,\n };\n});\n```\n\n**SSE 客户端实现要点:**\n\n1. **ReadableStream API**:原生流式读取响应体\n2. **AbortController**:支持取消请求\n3. **增量更新**:逐字符追加内容,实现打字机效果\n\n### 3.4 前端安全措施\n\n1. **XSS 防护**:使用 DOMPurify 清理用户输入\n2. **密码加密**:RSA 非对称加密传输\n3. **Token 存储**:localStorage + XSS 风险权衡\n4. **CORS 配置**:后端白名单控制\n\n---\n\n## 四、单词记忆模块 (word-game)\n\n### 4.1 技术架构\n\n```\nword-game/\n├── server/ # Node.js 后端\n│ ├── index.js # Express 服务入口\n│ └── progress.db # SQLite 数据库\n├── src/ # Vue 3 前端\n│ ├── components/\n│ │ ├── game/ # 游戏组件\n│ │ │ ├── AnswerView.vue\n│ │ │ ├── TypingInput.vue\n│ │ │ └── CompletionModal.vue\n│ │ └── pages/ # 页面组件\n│ ├── composables/ # 组合式函数\n│ │ ├── useEnglishSound.ts # 发音功能\n│ │ └── useQuestion.ts # 题目逻辑\n│ ├── stores/ # Pinia 状态\n│ │ ├── courseStore.ts\n│ │ └── progressStore.ts\n│ └── services/\n│ └── wordGameApi.ts # API 封装\n└── vite.config.ts\n```\n\n### 4.2 后端实现 (Express 5 + SQLite)\n\n#### 4.2.1 数据库设计\n\n```javascript\nconst db = new Database(join(__dirname, \"progress.db\"));\ndb.pragma(\"journal_mode = WAL\"); // Write-Ahead Logging 提升并发性能\n\ndb.exec(`\n -- 用户学习进度表\n CREATE TABLE IF NOT EXISTS course_progress (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_key TEXT NOT NULL, -- 用户标识(userId 或 guest_IP)\n user_type TEXT NOT NULL DEFAULT 'guest',\n package_id TEXT NOT NULL, -- 课程包 ID\n course_index INTEGER NOT NULL, -- 课程索引\n current_q INTEGER NOT NULL DEFAULT 0, -- 当前题目\n completed INTEGER NOT NULL DEFAULT 0, -- 是否完成\n study_secs INTEGER NOT NULL DEFAULT 0, -- 学习时长\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n UNIQUE(user_key, package_id, course_index)\n );\n\n -- 用户自定义课程包\n CREATE TABLE IF NOT EXISTS user_packages (\n id TEXT PRIMARY KEY,\n user_key TEXT NOT NULL,\n name TEXT NOT NULL,\n description TEXT NOT NULL DEFAULT '',\n icon TEXT NOT NULL DEFAULT '📦',\n is_public INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n -- 课程包题目\n CREATE TABLE IF NOT EXISTS user_package_statements (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n package_id TEXT NOT NULL,\n course_index INTEGER NOT NULL DEFAULT 0,\n english TEXT NOT NULL,\n chinese TEXT NOT NULL,\n soundmark TEXT NOT NULL DEFAULT ''\n );\n`);\n```\n\n**SQLite 优势:**\n\n1. **零配置**:无需独立数据库服务\n2. **单文件存储**:便于备份和迁移\n3. **WAL 模式**:提升并发读写性能\n4. **适合场景**:嵌入式应用、小型项目、开发测试\n\n#### 4.2.2 JWT 认证中间件\n\n```javascript\n/**\n * 与主站共享 JWT 密钥,实现单点登录\n */\nfunction requireAispringAuth(req, res, next) {\n try {\n const auth = req.headers[\"authorization\"];\n if (!auth || !auth.startsWith(\"Bearer \")) {\n return res.status(401).json({ success: false, message: \"未登录\" });\n }\n\n const token = auth.slice(7);\n const key = getJwtSigningKey(); // 与主站一致的密钥\n\n jwt.verify(token, key, { algorithms: [\"HS256\"] });\n next();\n } catch (err) {\n const msg = err.name === \"TokenExpiredError\" ? \"登录已过期\" : \"无效的登录凭证\";\n res.status(401).json({ success: false, message: msg });\n }\n}\n\n// 应用中间件\napp.use(\"/api\", requireAispringAuth);\n```\n\n#### 4.2.3 核心路由\n\n```javascript\n// 获取课程包列表(支持搜索、按点击量排序)\napp.get(\"/api/packages\", (req, res) => {\n const { key } = resolveUserKey(req);\n const search = req.query.search;\n\n let list = [...PACKAGES]; // 内置课程包\n const userRows = getUserPackagesList(key, search);\n list.push(...userRows.map(userPackageToMeta));\n\n // 按点击量排序\n list.sort((a, b) => (b.clickCount || 0) - (a.clickCount || 0));\n\n res.json({ success: true, data: list });\n});\n\n// 获取课程题目\napp.get(\"/api/courses/:courseIndex/questions\", (req, res) => {\n const packageId = req.query.packageId;\n const idx = parseInt(req.params.courseIndex, 10);\n\n if (packageId && packageId.startsWith(\"up-\")) {\n // 用户自定义课程包\n const rows = db.prepare(`\n SELECT english, chinese, soundmark\n FROM user_package_statements\n WHERE package_id = ? AND course_index = ?\n ORDER BY sort_order, id\n `).all(packageId, idx - 1);\n return res.json({ success: true, data: rows });\n }\n\n // 内置课程包\n const filePath = join(COURSES_DIR, `${String(idx).padStart(2, \"0\")}.json`);\n const statements = JSON.parse(readFileSync(filePath, \"utf-8\"));\n res.json({ success: true, data: statements });\n});\n\n// 创建自定义课程包\napp.post(\"/api/packages\", (req, res) => {\n const { key } = resolveUserKey(req);\n const { name, description, sections, isPublic } = req.body;\n\n const packageId = \"up-\" + randomUUID().replace(/-/g, \"\").slice(0, 16);\n\n db.transaction(() => {\n // 插入课程包元数据\n db.prepare(`INSERT INTO user_packages ...`).run(packageId, key, name, ...);\n\n // 插入各节题目\n for (const section of sections) {\n for (const stmt of section.statements) {\n db.prepare(`INSERT INTO user_package_statements ...`).run(...);\n }\n }\n })();\n\n res.status(201).json({ success: true, data: { id: packageId } });\n});\n```\n\n### 4.3 前端实现\n\n#### 4.3.1 打字输入组件\n\n```vue\n<template>\n <div class=\"typing-input\">\n <div class=\"chinese-hint\">{{ question.chinese }}</div>\n <input\n v-model=\"userInput\"\n @keydown=\"handleKeydown\"\n :class=\"{ correct: isCorrect, wrong: isWrong }\"\n placeholder=\"输入英文...\"\n autofocus\n />\n <button @click=\"playSound\">🔊 播放</button>\n </div>\n</template>\n\n<script setup>\nimport { useEnglishSound } from \"@/composables/useEnglishSound\";\nimport { computed, ref } from \"vue\";\n\nconst props = defineProps({\n question: Object,\n});\n\nconst emit = defineEmits([\"correct\", \"wrong\"]);\n\nconst userInput = ref(\"\");\nconst { play } = useEnglishSound();\n\nconst isCorrect = computed(\n () => userInput.value.trim().toLowerCase() === props.question.english.toLowerCase(),\n);\n\nfunction handleKeydown(e) {\n if (e.key === \"Enter\") {\n if (isCorrect.value) {\n emit(\"correct\");\n userInput.value = \"\";\n } else {\n emit(\"wrong\");\n }\n }\n}\n\nfunction playSound() {\n play(props.question.english);\n}\n</script>\n```\n\n#### 4.3.2 发音功能实现\n\n```typescript\nexport function useEnglishSound() {\n const audioContext = new (window.AudioContext || window.webkitAudioContext)()\n\n async function play(text: string) {\n // 使用 Web Speech API\n const utterance = new SpeechSynthesisUtterance(text)\n utterance.lang = 'en-US'\n utterance.rate = 0.9\n speechSynthesis.speak(utterance)\n }\n\n return { play }\n}\n```\n\n### 4.4 iframe 跨域通信\n\n主站通过 iframe 嵌入单词记忆模块,使用 postMessage 实现通信:\n\n```vue\n<!-- vue-app/src/views/WordGameView.vue -->\n<template>\n <iframe\n ref=\"gameFrame\"\n :src=\"gameUrl\"\n @load=\"onFrameLoad\"\n />\n</template>\n\n<script setup>\nimport { useAuthStore } from \"@/stores/auth\";\nimport { useThemeStore } from \"@/stores/theme\";\nimport { onMounted, ref } from \"vue\";\n\nconst gameFrame = ref(null);\nconst authStore = useAuthStore();\nconst themeStore = useThemeStore();\n\nconst gameUrl = import.meta.env.PROD ? \"https://earthworm.aistudy.icu\" : \"http://localhost:5200\";\n\nfunction onFrameLoad() {\n // 同步登录状态\n gameFrame.value.contentWindow.postMessage(\n {\n type: \"AUTH_SYNC\",\n token: authStore.token,\n user: authStore.userInfo,\n },\n \"*\",\n );\n\n // 同步主题\n gameFrame.value.contentWindow.postMessage(\n {\n type: \"THEME_SYNC\",\n isDark: themeStore.isDark,\n },\n \"*\",\n );\n}\n\n// 监听 iframe 返回的消息\nwindow.addEventListener(\"message\", (event) => {\n if (event.data.type === \"PROGRESS_UPDATE\") {\n // 处理进度更新\n }\n});\n</script>\n```\n\n---\n\n## 五、部署架构\n\n### 5.1 生产环境部署\n\n```\n ┌─────────────────────────────────────┐\n │ Nginx (反向代理) │\n │ Port: 80 / 443 │\n └─────────────────────────────────────┘\n │\n ┌───────────────────────────┼───────────────────────────┐\n │ │ │\n ▼ ▼ ▼\n┌───────────────┐ ┌───────────────┐ ┌───────────────┐\n│ aistudy.icu │ │ earthworm. │ │ 静态资源 │\n│ (主站) │ │ aistudy.icu │ │ (CDN) │\n│ → :5000 │ │ → :5201 │ │ │\n└───────────────┘ └───────────────┘ └───────────────┘\n │ │\n ▼ ▼\n┌───────────────┐ ┌───────────────┐\n│ Spring Boot │ │ Node.js │\n│ JAR / Docker │ │ Express │\n└───────────────┘ └───────────────┘\n │ │\n └───────────┬───────────────┘\n ▼\n ┌───────────────────────┐\n │ MySQL 8.x │\n │ Redis 7.x │\n └───────────────────────┘\n```\n\n### 5.2 Nginx 配置示例\n\n```nginx\n# 主站\nserver {\n listen 80;\n server_name aistudy.icu;\n\n location / {\n proxy_pass http://127.0.0.1:5000;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n\n # SSE 支持\n proxy_http_version 1.1;\n proxy_set_header Connection '';\n proxy_buffering off;\n proxy_cache off;\n proxy_read_timeout 300s;\n }\n}\n\n# 单词记忆模块\nserver {\n listen 80;\n server_name earthworm.aistudy.icu;\n\n location / {\n proxy_pass http://127.0.0.1:5201;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n }\n}\n```\n\n---\n\n## 六、性能优化总结\n\n### 6.1 后端优化\n\n| 优化点 | 实现方式 | 效果 |\n| ---------- | ------------------ | ---------------- |\n| 数据库索引 | 复合索引、覆盖索引 | 查询性能提升 10x |\n| 连接池 | HikariCP 默认配置 | 减少连接创建开销 |\n| 异步处理 | @Async + 线程池 | 非阻塞响应 |\n| 缓存策略 | Redis + 本地缓存 | 减少数据库压力 |\n| 流式响应 | SSE | 首字节时间优化 |\n\n### 6.2 前端优化\n\n| 优化点 | 实现方式 | 效果 |\n| -------- | -------------------- | ---------------- |\n| 代码分割 | manualChunks | 首屏加载减少 60% |\n| 懒加载 | 路由懒加载 | 按需加载页面 |\n| 虚拟滚动 | vue-virtual-scroller | 大列表性能优化 |\n| 防抖节流 | lodash | 减少无效请求 |\n| 缓存策略 | localStorage | 减少网络请求 |\n\n---\n\n## 七、面试高频问题\n\n### Q1: 为什么选择 Spring Boot 3.x?\n\n**回答要点:**\n\n1. 要求 Java 17+,支持 Virtual Threads(虚拟线程),大幅提升并发性能\n2. 原生支持 GraalVM 编译为原生可执行文件,启动时间从秒级降到毫秒级\n3. ProblemDetail API 标准化错误响应\n4. 可观测性增强(Micrometer Tracing)\n\n### Q2: JWT Token 如何实现续期?\n\n**回答要点:**\n\n1. **短期 Token + Refresh Token**:Access Token 有效期短(如 30 分钟),Refresh Token 有效期长(如 7 天)\n2. **滑动过期**:每次请求检查 Token 剩余有效期,若低于阈值则生成新 Token 返回\n3. **本项目方案**:Token 有效期 24 小时,过期后需重新登录\n\n### Q3: 如何防止 XSS 攻击?\n\n**回答要点:**\n\n1. **输入过滤**:使用 DOMPurify 清理用户输入\n2. **输出编码**:Vue 自动转义插值表达式\n3. **CSP 策略**:Content-Security-Policy 头限制脚本来源\n4. **HttpOnly Cookie**:敏感 Cookie 不允许 JS 访问(本项目使用 localStorage,需权衡)\n\n### Q4: SSE 和 WebSocket 如何选择?\n\n| 特性 | SSE | WebSocket |\n| -------- | --------------------- | -------------- |\n| 通信方向 | 单向(服务器→客户端) | 双向 |\n| 协议 | HTTP | WS/WSS |\n| 重连 | 浏览器自动重连 | 需手动实现 |\n| 适用场景 | 推送通知、AI 流式输出 | 实时聊天、游戏 |\n\n**本项目选择 SSE 的原因**:AI 对话场景只需服务器向客户端推送数据,SSE 更简单、更轻量。\n\n### Q5: 如何保证数据库与缓存一致性?\n\n**回答要点:**\n\n1. **Cache-Aside 模式**:先更新数据库,再删除缓存\n2. **延迟双删**:删除缓存 → 更新数据库 → 延迟再删除缓存\n3. **消息队列**:通过消息队列保证最终一致性\n4. **本项目方案**:写操作直接更新数据库,读操作先查缓存再查数据库\n\n---\n\n## 八、技术亮点总结\n\n1. **多模型 AI 集成**:通过 Spring AI 统一抽象层,支持 DeepSeek、豆包等多个模型\n2. **流式响应优化**:SSE 实现打字机效果,提升用户体验\n3. **GraphQL 按需加载**:解决 REST API 过度获取/获取不足问题\n4. **PRD 流水线**:多阶段 AI 协作,自动生成需求文档\n5. **微前端架构**:iframe + postMessage 实现模块解耦\n6. **安全加固**:RSA 加密传输 + JWT 认证 + 限流防护\n\n---\n\n_文档版本: 2.0_ \n_最后更新: 2026-02-28_\n"}, "66d582c2643956640599f78c65bed8d6caee84d5": {"t": 1785766000.222989, "n": 41965, "h": "fcfb48007809378c", "c": "# AI智能学习助手系统 - 技术架构详解\n\n> 本文档详细记录系统的技术实现细节,涵盖架构设计、核心模块实现、安全机制、性能优化等方面,可作为面试技术深度的参考材料。\n\n---\n\n## 一、系统架构概览\n\n### 1.1 整体架构设计\n\n本系统采用**前后端分离 + 微服务化**的架构模式,由三个核心子系统组成:\n\n```\n┌─────────────────────────────────────────────────────────────────────────┐\n│ 用户层 (Browser) │\n└─────────────────────────────────────────────────────────────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n ┌──────────────┐ ┌──────────────┐ ┌──────────────┐\n │ vue-app │ │ word-game │ │ Nginx │\n │ (Vue 3) │ │ (Vue 3) │ │ 反向代理 │\n │ Port: 3000 │ │ Port: 5200 │ │ │\n └──────────────┘ └──────────────┘ └──────────────┘\n │ │\n └───────┬───────┘\n ▼\n ┌──────────────────────────────┐\n │ aispring 后端 │\n │ (Spring Boot 3.3.5) │\n │ Port: 5000 │\n └──────────────────────────────┘\n │\n ┌───────────────┼───────────────┐\n ▼ ▼ ▼\n ┌──────────────┐ ┌──────────────┐ ┌──────────────┐\n │ MySQL │ │ Redis │ │ AI Services │\n │ (主数据库) │ │ (缓存/限流) │ │ DeepSeek/豆包 │\n └──────────────┘ └──────────────┘ └──────────────┘\n │\n ▼\n ┌──────────────────────────────┐\n │ word-game-server │\n │ (Express 5 + SQLite) │\n │ Port: 5201 │\n └──────────────────────────────┘\n```\n\n### 1.2 技术栈总览\n\n| 层级 | 技术选型 | 版本 | 选型理由 |\n| -------------- | --------------------- | ------ | ----------------------------------------------- |\n| **后端框架** | Spring Boot | 3.3.5 | 企业级Java框架,生态完善,支持响应式编程 |\n| **前端框架** | Vue 3 | 3.4.0 | Composition API,更好的TypeScript支持,性能优化 |\n| **构建工具** | Vite | 5.0.0 | ES Module原生支持,HMR速度快,打包体积小 |\n| **状态管理** | Pinia | 2.1.7 | Vue官方推荐,TypeScript友好,模块化设计 |\n| **数据库** | MySQL | 8.x | 关系型数据库,支持复杂查询和事务 |\n| **缓存** | Redis | 7.x | 高性能内存数据库,支持限流、会话管理 |\n| **ORM** | Spring Data JPA | 3.x | 简化数据访问层开发,支持方法命名查询 |\n| **认证** | JWT + Spring Security | 0.12.3 | 无状态认证,支持分布式部署 |\n| **AI集成** | Spring AI | 0.8.1 | 官方AI集成框架,统一API调用方式 |\n| **数据库迁移** | Flyway | 9.x | 版本化数据库变更管理 |\n\n---\n\n## 二、后端架构详解 (aispring)\n\n### 2.1 项目结构\n\n```\naispring/\n├── src/main/java/com/aispring/\n│ ├── config/ # 配置类\n│ │ ├── SecurityConfig.java # Spring Security 安全配置\n│ │ ├── RedisConfig.java # Redis 缓存配置\n│ │ ├── CorsConfig.java # 跨域配置\n│ │ ├── JacksonConfig.java # JSON 序列化配置\n│ │ └── StorageProperties.java # 文件存储配置\n│ ├── controller/ # 控制器层 (REST API)\n│ │ ├── AuthController.java # 认证相关\n│ │ ├── AiChatController.java # AI 对话\n│ │ ├── CloudDiskController.java # 云盘管理\n│ │ └── ...\n│ ├── service/ # 服务层 (业务逻辑)\n│ │ ├── impl/ # 服务实现\n│ │ ├── AiChatService.java\n│ │ ├── AuthService.java\n│ │ └── ...\n│ ├── repository/ # 数据访问层 (JPA)\n│ ├── entity/ # 实体类\n│ ├── dto/ # 数据传输对象\n│ │ ├── request/ # 请求DTO\n│ │ └── response/ # 响应DTO\n│ ├── security/ # 安全相关\n│ │ ├── JwtAuthenticationFilter.java\n│ │ ├── CustomUserDetails.java\n│ │ └── UserDetailsServiceImpl.java\n│ ├── graphql/ # GraphQL 支持\n│ └── exception/ # 异常处理\n├── migrations/ # Flyway 数据库迁移脚本\n└── pom.xml # Maven 依赖配置\n```\n\n### 2.2 核心依赖分析 (pom.xml)\n\n```xml\n<!-- 核心框架 -->\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-web</artifactId>\n</dependency>\n\n<!-- JPA 数据访问 -->\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-data-jpa</artifactId>\n</dependency>\n\n<!-- Spring Security 安全框架 -->\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-security</artifactId>\n</dependency>\n\n<!-- JWT 令牌处理 (jjwt 0.12.3) -->\n<dependency>\n <groupId>io.jsonwebtoken</groupId>\n <artifactId>jjwt-api</artifactId>\n <version>0.12.3</version>\n</dependency>\n\n<!-- Spring AI 大模型集成 -->\n<dependency>\n <groupId>org.springframework.ai</groupId>\n <artifactId>spring-ai-openai-spring-boot-starter</artifactId>\n <version>0.8.1</version>\n</dependency>\n\n<!-- GraphQL API 支持 -->\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-graphql</artifactId>\n</dependency>\n\n<!-- Redis 缓存 -->\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-data-redis</artifactId>\n</dependency>\n\n<!-- Flyway 数据库版本管理 -->\n<dependency>\n <groupId>org.flywaydb</groupId>\n <artifactId>flyway-core</artifactId>\n</dependency>\n```\n\n**技术选型关键点:**\n\n- **Spring Boot 3.x** 要求 **Java 17+**,支持 Virtual Threads(虚拟线程)\n- **Spring AI 0.8.1** 提供统一的 AI 模型调用抽象层,支持 OpenAI 兼容 API\n- **JJWT 0.12.3** 支持 JWT 的创建、解析和验证,支持多种签名算法\n\n### 2.3 安全架构设计\n\n#### 2.3.1 Spring Security 配置\n\n```java\n@Configuration\n@EnableWebSecurity\n@EnableMethodSecurity\n@RequiredArgsConstructor\npublic class SecurityConfig {\n\n private final JwtAuthenticationFilter jwtAuthFilter;\n\n @Bean\n public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n http\n .csrf(csrf -> csrf.disable()) // 禁用 CSRF(前后端分离架构)\n .cors(cors -> {}) // 启用 CORS\n .authorizeHttpRequests(auth -> auth\n // OPTIONS 预检请求放行\n .requestMatchers(HttpMethod.OPTIONS, \"/**\").permitAll()\n // SSE 异步分发放行\n .dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll()\n // 公开端点\n .requestMatchers(\"/api/auth/**\").permitAll()\n .requestMatchers(\"/api/ask\", \"/api/ask-stream\").permitAll()\n // 其他需要认证\n .anyRequest().authenticated()\n )\n .sessionManagement(session -> session\n .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态会话\n )\n .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);\n\n return http.build();\n }\n\n @Bean\n public PasswordEncoder passwordEncoder() {\n return new BCryptPasswordEncoder(); // BCrypt 密码加密\n }\n}\n```\n\n**面试要点:**\n\n1. **为什么禁用 CSRF?** 前后端分离架构,使用 JWT Token 认证,不依赖 Cookie-Session 机制\n2. **无状态会话的意义**:服务端不存储会话信息,便于水平扩展和负载均衡\n3. **BCrypt 加密优势**:自动加盐、计算成本可调、抗彩虹表攻击\n\n#### 2.3.2 JWT 认证过滤器\n\n```java\n@Component\n@RequiredArgsConstructor\npublic class JwtAuthenticationFilter extends OncePerRequestFilter {\n\n private final JwtUtil jwtUtil;\n private final UserDetailsService userDetailsService;\n\n private static final String[] PUBLIC_PATTERNS = {\n \"/api/auth/**\", \"/swagger-ui/**\", \"/api/public-files/**\"\n };\n\n private final AntPathMatcher pathMatcher = new AntPathMatcher();\n\n @Override\n protected boolean shouldNotFilter(HttpServletRequest request) {\n // OPTIONS 请求和公开路径跳过过滤\n if (\"OPTIONS\".equalsIgnoreCase(request.getMethod())) return true;\n\n String path = request.getServletPath();\n for (String pattern : PUBLIC_PATTERNS) {\n if (pathMatcher.match(pattern, path)) return true;\n }\n return false;\n }\n\n @Override\n protected void doFilterInternal(HttpServletRequest request,\n HttpServletResponse response,\n FilterChain filterChain) {\n final String authHeader = request.getHeader(\"Authorization\");\n\n if (authHeader == null || !authHeader.startsWith(\"Bearer \")) {\n filterChain.doFilter(request, response);\n return;\n }\n\n final String jwt = authHeader.substring(7);\n final String userEmail = jwtUtil.extractUsername(jwt);\n\n if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {\n UserDetails userDetails = userDetailsService.loadUserByUsername(userEmail);\n\n if (jwtUtil.isTokenValid(jwt, userDetails)) {\n UsernamePasswordAuthenticationToken authToken =\n new UsernamePasswordAuthenticationToken(\n userDetails, null, userDetails.getAuthorities()\n );\n authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));\n SecurityContextHolder.getContext().setAuthentication(authToken);\n }\n }\n\n filterChain.doFilter(request, response);\n }\n}\n```\n\n**面试要点:**\n\n1. **OncePerRequestFilter**:确保每个请求只经过一次过滤器(避免转发/包含导致的重复过滤)\n2. **AntPathMatcher**:支持 Ant 风格路径匹配(`**`、`*`、`?` 通配符)\n3. **SecurityContextHolder**:使用 ThreadLocal 存储安全上下文,保证线程安全\n\n#### 2.3.3 RSA 前端加密传输\n\n为防止密码在传输过程中被截获,实现 RSA 非对称加密:\n\n**后端提供公钥:**\n\n```java\n@RestController\n@RequestMapping(\"/api/rsa\")\npublic class RsaController {\n\n private final RsaUtil rsaUtil;\n\n @GetMapping(\"/public-key\")\n public ApiResponse<String> getPublicKey() {\n return ApiResponse.success(\"获取成功\", rsaUtil.getPublicKey());\n }\n}\n```\n\n**前端加密实现:**\n\n```javascript\nimport JSEncrypt from \"jsencrypt\";\n\nclass RsaEncryption {\n async initialize() {\n const response = await fetch(\"/api/rsa/public-key\");\n const data = await response.json();\n this.publicKey = data.data;\n }\n\n encrypt(text) {\n const encrypt = new JSEncrypt();\n encrypt.setPublicKey(this.publicKey);\n return encrypt.encrypt(text);\n }\n}\n```\n\n**安全流程:**\n\n1. 前端获取 RSA 公钥\n2. 用户密码使用公钥加密\n3. 后端使用私钥解密\n4. 解密后的密码再使用 BCrypt 加密存储\n\n### 2.4 数据库设计\n\n#### 2.4.1 核心实体关系\n\n```\n┌─────────────┐ 1:1 ┌─────────────┐\n│ User │─────────────▶│ Admin │\n└─────────────┘ └─────────────┘\n │\n │ 1:N\n ▼\n┌─────────────┐ N:1 ┌─────────────┐\n│ UserFile │◀─────────────│ UserFolder │\n└─────────────┘ └─────────────┘\n │\n │ 1:N\n ▼\n┌─────────────┐\n│ ChatRecord │\n└─────────────┘\n │\n │ N:1\n ▼\n┌─────────────┐\n│ ChatSession │\n└─────────────┘\n```\n\n#### 2.4.2 核心实体定义\n\n**用户实体 (User.java):**\n\n```java\n@Entity\n@Table(name = \"users\", indexes = {\n @Index(name = \"idx_email\", columnList = \"email\"),\n @Index(name = \"idx_username\", columnList = \"username\")\n})\n@EntityListeners(AuditingEntityListener.class)\n@Data\npublic class User {\n\n @Id\n @GeneratedValue(strategy = GenerationType.IDENTITY)\n private Long id;\n\n @Column(nullable = false, unique = true, length = 80)\n private String username;\n\n @Column(nullable = false, unique = true, length = 120)\n private String email;\n\n @Column(name = \"password_hash\", nullable = false, length = 255)\n private String passwordHash;\n\n @CreatedDate\n @Column(name = \"created_at\", updatable = false)\n private LocalDateTime createdAt;\n\n @LastModifiedDate\n @Column(name = \"updated_at\")\n private LocalDateTime updatedAt;\n\n @OneToOne(mappedBy = \"user\", cascade = CascadeType.ALL, orphanRemoval = true)\n private Admin admin;\n\n @OneToMany(mappedBy = \"user\", cascade = CascadeType.ALL, orphanRemoval = true)\n @JsonIgnore\n private List<UserFile> files = new ArrayList<>();\n\n public void setPassword(String password) {\n BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();\n this.passwordHash = encoder.encode(password);\n }\n\n public boolean checkPassword(String password) {\n BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();\n return encoder.matches(password, this.passwordHash);\n }\n}\n```\n\n**聊天记录实体 (ChatRecord.java):**\n\n```java\n@Entity\n@Table(name = \"chat_records\", indexes = {\n @Index(name = \"idx_user_id\", columnList = \"user_id\"),\n @Index(name = \"idx_session_id\", columnList = \"session_id\"),\n @Index(name = \"idx_user_session\", columnList = \"user_id, session_id\")\n})\n@Data\n@Builder\npublic class ChatRecord {\n\n @Id\n @GeneratedValue(strategy = GenerationType.IDENTITY)\n private Long id;\n\n @Column(name = \"user_id\")\n private Long userId;\n\n @Column(name = \"session_id\", nullable = false)\n private String sessionId;\n\n @Column(name = \"message_order\", nullable = false)\n private Integer messageOrder; // 消息顺序,用于排序\n\n @Column(name = \"sender_type\", nullable = false)\n private Integer senderType; // 1: 用户, 2: AI, 3: 工具结果\n\n @Column(columnDefinition = \"TEXT\")\n private String content;\n\n @Column(name = \"reasoning_content\", columnDefinition = \"TEXT\")\n private String reasoningContent; // AI 深度思考内容(推理模型)\n\n @Column(name = \"ai_model\", length = 50)\n private String aiModel; // deepseek, doubao, deepseek-reasoner\n\n @Column(name = \"status\", length = 20)\n private String status; // pending, completed, failed, cancelled\n\n @Column(name = \"send_time\")\n private LocalDateTime sendTime;\n}\n```\n\n**面试要点:**\n\n1. **索引设计**:`idx_user_session` 复合索引优化按用户+会话查询\n2. **实体监听器**:`@CreatedDate`、`@LastModifiedDate` 自动维护时间戳\n3. **级联操作**:`CascadeType.ALL` + `orphanRemoval = true` 实现级联删除\n\n### 2.5 AI 服务集成\n\n#### 2.5.1 多模型支持架构\n\n系统支持多个 AI 模型提供商,通过策略模式实现灵活切换:\n\n```java\n@Service\npublic class AiChatServiceImpl implements AiChatService {\n\n // Spring AI 自动注入的客户端\n private final ObjectProvider<ChatClient> chatClientProvider;\n private final ObjectProvider<StreamingChatClient> streamingChatClientProvider;\n\n // 自定义模型客户端\n private ChatClient doubaoChatClient; // 豆包模型\n private ChatClient deepseekChatClient; // DeepSeek 模型\n private ChatClient deepseekReasonerClient; // DeepSeek 推理模型\n\n @Value(\"${ai.doubao.api-key:}\")\n private String doubaoApiKey;\n\n @Value(\"${ai.deepseek.api-url:}\")\n private String deepseekApiUrl;\n\n // 初始化各模型客户端\n public AiChatServiceImpl(...) {\n // 豆包模型初始化\n if (doubaoApiKey != null && !doubaoApiKey.isEmpty()) {\n OpenAiApi doubaoApi = new OpenAiApi(doubaoBaseUrl + \"/api/v3\", doubaoApiKey);\n OpenAiChatOptions doubaoOptions = OpenAiChatOptions.builder()\n .withModel(\"doubao-pro-32k\")\n .withTemperature(0.7f)\n .withMaxTokens(maxTokens)\n .build();\n this.doubaoChatClient = new OpenAiChatClient(doubaoApi, doubaoOptions);\n }\n\n // DeepSeek 模型初始化\n if (deepseekApiKey != null && !deepseekApiKey.isEmpty()) {\n OpenAiApi deepseekApi = new OpenAiApi(deepseekApiUrl, deepseekApiKey);\n this.deepseekChatClient = new OpenAiChatClient(deepseekApi);\n }\n }\n}\n```\n\n**面试要点:**\n\n1. **Spring AI 兼容性**:通过 OpenAI 兼容 API 接入国产模型(豆包、DeepSeek)\n2. **ObjectProvider 延迟注入**:避免循环依赖,按需获取 Bean\n3. **配置外部化**:API Key 和 URL 通过配置文件管理,支持环境变量覆盖\n\n#### 2.5.2 流式响应实现 (SSE)\n\n```java\n@Override\npublic SseEmitter askStream(String prompt, String sessionId, String model,\n Long userId, String ipAddress) {\n // 创建 SSE 发射器,超时 5 分钟\n SseEmitter emitter = new SseEmitter(300_000L);\n\n chatExecutor.execute(() -> {\n try {\n // 敏感信息脱敏\n String maskedPrompt = SensitiveDataMasker.mask(prompt);\n\n // 构建对话 Prompt(包含历史上下文)\n Prompt promptObj = buildPrompt(maskedPrompt, sessionId, userId, ...);\n\n // 流式调用 AI\n streamingChatClient.stream(promptObj)\n .doOnNext(chatResponse -> {\n String content = chatResponse.getResult().getOutput().getContent();\n if (content != null && !content.isEmpty()) {\n // 发送 SSE 事件\n Map<String, String> result = new HashMap<>();\n result.put(\"content\", content);\n emitter.send(SseEmitter.event()\n .data(objectMapper.writeValueAsString(result))\n .name(\"message\"));\n }\n })\n .doOnError(e -> handleError(emitter, e))\n .blockLast();\n\n // 发送完成事件\n emitter.send(SseEmitter.event().data(\"[DONE]\"));\n emitter.complete();\n\n } catch (Exception e) {\n handleError(emitter, e);\n }\n });\n\n return emitter;\n}\n```\n\n**SSE (Server-Sent Events) 技术要点:**\n\n1. **单向通信**:服务器向客户端推送数据,适合 AI 流式输出场景\n2. **自动重连**:浏览器原生支持断线重连\n3. **相比 WebSocket**:更简单、更轻量,适合单向数据流\n\n#### 2.5.3 上下文管理策略\n\n```java\n@Value(\"${ai.context.max-history-messages:30}\")\nprivate Integer maxHistoryMessages; // 最大历史消息数\n\n@Value(\"${ai.context.max-history-chars:20000}\")\nprivate Integer maxHistoryChars; // 最大历史字符数\n\nprivate Prompt buildPrompt(String prompt, String sessionId, Long userId, ...) {\n List<Message> messages = new ArrayList<>();\n\n // 1. 添加系统提示词\n messages.add(new SystemMessage(systemPrompt));\n\n // 2. 加载历史对话(带预算控制)\n if (sessionId != null && userId != null) {\n int budget = maxHistoryChars;\n List<ChatRecord> history = chatRecordRepository\n .findByUserIdAndSessionIdOrderByMessageOrderDesc(userId, sessionId,\n PageRequest.of(0, maxHistoryMessages));\n\n for (ChatRecord record : history) {\n if (budget <= 0) break;\n String role = (record.getSenderType() == 1) ? \"user\" : \"assistant\";\n String content = truncateToMax(record.getContent(), budget);\n budget -= content.length();\n messages.add(0, new Message(role, content));\n }\n }\n\n // 3. 添加当前用户输入\n messages.add(new UserMessage(prompt));\n\n return new Prompt(messages);\n}\n```\n\n**上下文管理策略:**\n\n1. **消息数量限制**:防止上下文过长导致 Token 超限\n2. **字符预算控制**:动态计算可用空间,优先保留最近对话\n3. **敏感信息脱敏**:发送给 AI 前过滤身份证、手机号等隐私数据\n\n### 2.6 GraphQL API 设计\n\n#### 2.6.1 为什么选择 GraphQL?\n\n| 特性 | REST API | GraphQL |\n| -------- | ---------------- | ---------------------- |\n| 数据获取 | 固定返回结构 | 按需获取字段 |\n| 请求次数 | 可能需要多次请求 | 单次请求获取所有数据 |\n| 版本管理 | v1, v2... | 无需版本 |\n| 适合场景 | 简单 CRUD | 复杂数据关系、前端驱动 |\n\n#### 2.6.2 声明式数据分片加载\n\n```java\n@Controller\npublic class RequirementDocGraphQLController {\n\n /**\n * 查询需求文档列表(分页)\n * 前端可通过 Fragment 指定需要的字段\n */\n @QueryMapping\n public RequirementDocConnection requirementDocs(\n @Argument Integer page,\n @Argument Integer size,\n @AuthenticationPrincipal CustomUserDetails principal) {\n\n Page<RequirementDoc> docPage = requirementDocRepository.findByUserId(\n principal.getUser().getId(),\n PageRequest.of(page, size)\n );\n return RequirementDocConnection.fromPage(docPage);\n }\n\n /**\n * 按需加载用户信息\n * 只有前端请求 user 字段时才执行\n */\n @SchemaMapping(typeName = \"RequirementDoc\", field = \"user\")\n public CompletableFuture<User> user(RequirementDoc doc) {\n return CompletableFuture.supplyAsync(() ->\n userRepository.findById(doc.getUserId()).orElse(null)\n );\n }\n\n /**\n * 按需加载统计信息\n */\n @SchemaMapping(typeName = \"RequirementDoc\", field = \"statistics\")\n public RequirementDocStatistics statistics(RequirementDoc doc) {\n int wordCount = doc.getContent() != null ? doc.getContent().length() : 0;\n int editCount = historyRepository.countByDocId(doc.getId());\n return RequirementDocStatistics.builder()\n .wordCount(wordCount)\n .editCount(editCount)\n .build();\n }\n}\n```\n\n**前端查询示例:**\n\n```graphql\nquery {\n requirementDocs(page: 0, size: 10) {\n content {\n id\n title\n ...BasicFields # 只加载基础字段\n user {\n username\n avatar\n }\n }\n }\n}\n```\n\n### 2.7 PRD 需求文档流水线\n\n#### 2.7.1 流水线架构\n\n```\n用户输入想法\n │\n ▼\n┌─────────┐ ┌─────────┐ ┌─────────────┐\n│ Outline │───▶│ Draft │───▶│ Critique │◀──┐\n│ 大纲 │ │ 初稿 │ │ 评审 │ │\n└─────────┘ └─────────┘ └─────────────┘ │\n │ │\n ▼ │\n ┌───────────┐ │\n │ Revise │────┘\n │ 修订 │ (最多3轮)\n └───────────┘\n │\n ▼\n ┌───────────┐\n │ Complete │\n │ 完成 │\n └───────────┘\n```\n\n#### 2.7.2 流水线实现\n\n```java\n@Service\npublic class PrdPipelineServiceImpl implements PrdPipelineService {\n\n private static final int MAX_REVISIONS = 3;\n private static final String APPROVAL_PHRASE = \"No issues found.\";\n\n private static final String OUTLINE_PROMPT = \"\"\"\n You are a world-class product manager. Your task is to create a structured\n outline for a Product Requirements Document (PRD)...\n \"\"\";\n\n private static final String CRITIQUE_PROMPT = \"\"\"\n You are a meticulous and critical product manager...\n If the PRD is well-structured, respond with \"No issues found.\"\n \"\"\";\n\n @Async\n @Override\n public void runPipelineAsync(String runId, String idea, Long userId) {\n String content = \"# PRD for \" + idea;\n int revision = 0;\n\n try {\n // Step 1: 生成大纲\n sendState(runId, \"Outline\", content, revision, null);\n content = aiChatService.ask(String.format(OUTLINE_PROMPT, idea), ...);\n revision++;\n\n // Step 2: 生成初稿\n sendState(runId, \"Draft\", content, revision, null);\n content = aiChatService.ask(String.format(DRAFT_PROMPT, content), ...);\n revision++;\n\n // Step 3: 评审循环\n for (int i = 0; i < MAX_REVISIONS; i++) {\n sendState(runId, \"Critique\", content, revision, null);\n String critique = aiChatService.ask(String.format(CRITIQUE_PROMPT, content), ...);\n\n if (critique.contains(APPROVAL_PHRASE)) {\n break; // 通过评审\n }\n\n // 根据评审意见修订\n content = aiChatService.ask(String.format(REVISE_PROMPT, content, critique), ...);\n revision++;\n }\n\n sendState(runId, \"Complete\", content, revision, null);\n } catch (Exception e) {\n sendState(runId, \"Error\", content, revision + 1, null);\n }\n }\n}\n```\n\n**面试要点:**\n\n1. **@Async 异步执行**:避免长时间阻塞 HTTP 请求,通过 SSE 推送进度\n2. **评审循环**:最多 3 轮修订,确保文档质量\n3. **状态推送**:通过 PrdStreamHolder 实时推送流水线状态\n\n### 2.8 限流与审计\n\n#### 2.8.1 基于 Redis 的 IP 限流\n\n```java\n@Service\npublic class RateLimitService {\n\n private final StringRedisTemplate redisTemplate;\n\n private static final String KEY_PREFIX = \"chat_limit:\";\n private static final int MAX_REQUESTS = 5; // 每日最大请求次数\n private static final Duration EXPIRATION = Duration.ofHours(24);\n\n public boolean checkAndIncrement(String ip) {\n String key = KEY_PREFIX + ip;\n\n Long count = redisTemplate.opsForValue().increment(key);\n\n // 首次请求设置过期时间\n if (count != null && count == 1) {\n redisTemplate.expire(key, EXPIRATION);\n }\n\n if (count != null && count > MAX_REQUESTS) {\n log.warn(\"Rate limit exceeded for IP: {} (Count: {})\", ip, count);\n return false;\n }\n\n return true;\n }\n}\n```\n\n**限流算法:计数器算法**\n\n- 优点:实现简单,内存占用小\n- 缺点:存在临界问题(可在临界点突发请求)\n- 改进方案:滑动窗口、令牌桶、漏桶\n\n#### 2.8.2 Token 消耗审计\n\n```java\npublic interface TokenUsageAuditService {\n void record(String provider, String modelName, Long userId, String sessionId,\n Integer inputTokens, Integer outputTokens,\n long responseTimeMs, boolean streaming);\n}\n```\n\n记录每次 AI 调用的:\n\n- Token 消耗量(输入/输出)\n- 响应时间\n- 模型提供商\n- 用户/会话信息\n\n---\n\n## 三、前端架构详解 (vue-app)\n\n### 3.1 项目结构\n\n```\nvue-app/\n├── src/\n│ ├── components/ # 可复用组件\n│ │ ├── Sidebar/ # 侧边栏组件\n│ │ ├── AppHeader.vue # 头部组件\n│ │ └── RequirementManager.vue # PRD 管理组件\n│ ├── composables/ # 组合式函数 (Hooks)\n│ │ └── useGraphQLClient.js\n│ ├── config/ # 配置文件\n│ │ └── api.js # API 端点配置\n│ ├── stores/ # Pinia 状态管理\n│ │ ├── auth.js # 认证状态\n│ │ ├── chat.js # 聊天状态\n│ │ ├── cloudDisk.js # 云盘状态\n│ │ └── theme.js # 主题状态\n│ ├── utils/ # 工具函数\n│ │ ├── request.js # Axios 封装\n│ │ ├── rsaEncryption.js # RSA 加密\n│ │ └── graphqlClient.js # GraphQL 客户端\n│ ├── views/ # 页面视图\n│ │ ├── auth/ # 认证相关页面\n│ │ ├── ChatView.vue # AI 对话页面\n│ │ ├── CloudDiskView.vue # 云盘页面\n│ │ └── WordGameView.vue # 单词记忆入口\n│ ├── router/ # 路由配置\n│ │ └── index.js\n│ ├── App.vue # 根组件\n│ └── main.js # 入口文件\n├── vite.config.js # Vite 配置\n└── package.json\n```\n\n### 3.2 Vite 构建优化\n\n```javascript\nexport default defineConfig({\n plugins: [vue()],\n resolve: {\n alias: {\n \"@\": fileURLToPath(new URL(\"./src\", import.meta.url)),\n },\n },\n server: {\n port: 3000,\n proxy: {\n \"/api\": {\n target: \"http://localhost:5000\", // 代理到后端\n changeOrigin: true,\n },\n },\n },\n build: {\n outDir: \"dist\",\n sourcemap: false,\n rollupOptions: {\n output: {\n manualChunks: {\n \"vue-vendor\": [\"vue\", \"vue-router\", \"pinia\"], // Vue 全家桶\n highlight: [\"highlight.js\"], // 代码高亮\n markdown: [\"marked\"], // Markdown 解析\n },\n },\n },\n },\n});\n```\n\n**构建优化策略:**\n\n1. **代码分割 (Code Splitting)**:将第三方库分离,利用浏览器缓存\n2. **Tree Shaking**:Vite 基于 ES Module,自动移除未使用代码\n3. **开发代理**:避免 CORS 问题,统一 API 路径\n\n### 3.3 Pinia 状态管理\n\n#### 3.3.1 认证状态管理 (auth.js)\n\n```javascript\nimport { defineStore } from \"pinia\";\nimport { computed, ref } from \"vue\";\n\nexport const useAuthStore = defineStore(\"auth\", () => {\n // 状态\n const token = ref(localStorage.getItem(\"token\") || \"\");\n const userInfo = ref(JSON.parse(localStorage.getItem(\"userInfo\") || \"null\"));\n\n // 计算属性\n const isAuthenticated = computed(() => !!token.value);\n const isAdmin = computed(() => userInfo.value?.is_admin === true);\n\n // 登录方法\n async function login(email, password) {\n const response = await request.post(API_ENDPOINTS.auth.login, {\n email,\n password,\n });\n\n const accessToken = response?.data?.access_token;\n if (accessToken) {\n token.value = accessToken;\n userInfo.value = {\n id: response.data.user_id,\n email: response.data.email,\n username: response.data.username,\n is_admin: response.data.is_admin,\n };\n\n // 持久化存储\n localStorage.setItem(\"token\", token.value);\n localStorage.setItem(\"userInfo\", JSON.stringify(userInfo.value));\n\n return { success: true };\n }\n return { success: false };\n }\n\n // 退出登录\n function logout() {\n token.value = \"\";\n userInfo.value = null;\n localStorage.removeItem(\"token\");\n localStorage.removeItem(\"userInfo\");\n }\n\n return {\n token,\n userInfo,\n isAuthenticated,\n isAdmin,\n login,\n logout,\n };\n});\n```\n\n**Pinia vs Vuex 对比:**\n| 特性 | Vuex | Pinia |\n|------|------|-------|\n| 状态定义 | state 对象 | ref/reactive |\n| 修改方式 | mutations | 直接修改 |\n| TypeScript | 需要额外配置 | 原生支持 |\n| 模块化 | modules | 独立 Store |\n| DevTools | 支持 | 支持 |\n\n#### 3.3.2 聊天状态管理 (chat.js)\n\n```javascript\nexport const useChatStore = defineStore(\"chat\", () => {\n const sessions = ref([]);\n const currentSessionId = ref(null);\n const messages = ref([]);\n const isLoading = ref(false);\n const abortController = ref(null);\n const selectedModel = ref(localStorage.getItem(\"selectedModel\") || \"deepseek-chat\");\n\n // 流式发送消息\n async function sendMessage(content, onChunk) {\n isLoading.value = true;\n abortController.value = new AbortController();\n\n // 添加用户消息\n messages.value.push({\n role: \"user\",\n content,\n timestamp: new Date().toISOString(),\n });\n\n // 添加 AI 消息占位符\n const aiMessage = {\n role: \"assistant\",\n content: \"\",\n reasoning_content: \"\",\n isStreaming: true,\n timestamp: new Date().toISOString(),\n };\n messages.value.push(aiMessage);\n\n try {\n const response = await fetch(\"/api/ask-stream\", {\n method: \"POST\",\n signal: abortController.value.signal,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${authStore.token}`,\n },\n body: JSON.stringify({\n prompt: content,\n session_id: currentSessionId.value,\n model: selectedModel.value,\n }),\n });\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"data:\")) {\n const data = line.slice(5).trim();\n if (data === \"[DONE]\") continue;\n\n const parsed = JSON.parse(data);\n\n // 处理推理内容\n if (parsed.reasoning_content) {\n aiMessage.reasoning_content += parsed.reasoning_content;\n }\n\n // 处理回复内容\n if (parsed.content) {\n aiMessage.content += parsed.content;\n }\n }\n }\n }\n\n aiMessage.isStreaming = false;\n return { success: true };\n } catch (error) {\n if (error.name === \"AbortError\") {\n console.log(\"Generation aborted by user\");\n return { success: true, aborted: true };\n }\n throw error;\n } finally {\n isLoading.value = false;\n }\n }\n\n // 停止生成\n function stopGeneration() {\n if (abortController.value) {\n abortController.value.abort();\n }\n }\n\n return {\n sessions,\n messages,\n isLoading,\n selectedModel,\n sendMessage,\n stopGeneration,\n };\n});\n```\n\n**SSE 客户端实现要点:**\n\n1. **ReadableStream API**:原生流式读取响应体\n2. **AbortController**:支持取消请求\n3. **增量更新**:逐字符追加内容,实现打字机效果\n\n### 3.4 前端安全措施\n\n1. **XSS 防护**:使用 DOMPurify 清理用户输入\n2. **密码加密**:RSA 非对称加密传输\n3. **Token 存储**:localStorage + XSS 风险权衡\n4. **CORS 配置**:后端白名单控制\n\n---\n\n## 四、单词记忆模块 (word-game)\n\n### 4.1 技术架构\n\n```\nword-game/\n├── server/ # Node.js 后端\n│ ├── index.js # Express 服务入口\n│ └── progress.db # SQLite 数据库\n├── src/ # Vue 3 前端\n│ ├── components/\n│ │ ├── game/ # 游戏组件\n│ │ │ ├── AnswerView.vue\n│ │ │ ├── TypingInput.vue\n│ │ │ └── CompletionModal.vue\n│ │ └── pages/ # 页面组件\n│ ├── composables/ # 组合式函数\n│ │ ├── useEnglishSound.ts # 发音功能\n│ │ └── useQuestion.ts # 题目逻辑\n│ ├── stores/ # Pinia 状态\n│ │ ├── courseStore.ts\n│ │ └── progressStore.ts\n│ └── services/\n│ └── wordGameApi.ts # API 封装\n└── vite.config.ts\n```\n\n### 4.2 后端实现 (Express 5 + SQLite)\n\n#### 4.2.1 数据库设计\n\n```javascript\nconst db = new Database(join(__dirname, \"progress.db\"));\ndb.pragma(\"journal_mode = WAL\"); // Write-Ahead Logging 提升并发性能\n\ndb.exec(`\n -- 用户学习进度表\n CREATE TABLE IF NOT EXISTS course_progress (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n user_key TEXT NOT NULL, -- 用户标识(userId 或 guest_IP)\n user_type TEXT NOT NULL DEFAULT 'guest',\n package_id TEXT NOT NULL, -- 课程包 ID\n course_index INTEGER NOT NULL, -- 课程索引\n current_q INTEGER NOT NULL DEFAULT 0, -- 当前题目\n completed INTEGER NOT NULL DEFAULT 0, -- 是否完成\n study_secs INTEGER NOT NULL DEFAULT 0, -- 学习时长\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n UNIQUE(user_key, package_id, course_index)\n );\n\n -- 用户自定义课程包\n CREATE TABLE IF NOT EXISTS user_packages (\n id TEXT PRIMARY KEY,\n user_key TEXT NOT NULL,\n name TEXT NOT NULL,\n description TEXT NOT NULL DEFAULT '',\n icon TEXT NOT NULL DEFAULT '📦',\n is_public INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n -- 课程包题目\n CREATE TABLE IF NOT EXISTS user_package_statements (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n package_id TEXT NOT NULL,\n course_index INTEGER NOT NULL DEFAULT 0,\n english TEXT NOT NULL,\n chinese TEXT NOT NULL,\n soundmark TEXT NOT NULL DEFAULT ''\n );\n`);\n```\n\n**SQLite 优势:**\n\n1. **零配置**:无需独立数据库服务\n2. **单文件存储**:便于备份和迁移\n3. **WAL 模式**:提升并发读写性能\n4. **适合场景**:嵌入式应用、小型项目、开发测试\n\n#### 4.2.2 JWT 认证中间件\n\n```javascript\n/**\n * 与主站共享 JWT 密钥,实现单点登录\n */\nfunction requireAispringAuth(req, res, next) {\n try {\n const auth = req.headers[\"authorization\"];\n if (!auth || !auth.startsWith(\"Bearer \")) {\n return res.status(401).json({ success: false, message: \"未登录\" });\n }\n\n const token = auth.slice(7);\n const key = getJwtSigningKey(); // 与主站一致的密钥\n\n jwt.verify(token, key, { algorithms: [\"HS256\"] });\n next();\n } catch (err) {\n const msg = err.name === \"TokenExpiredError\" ? \"登录已过期\" : \"无效的登录凭证\";\n res.status(401).json({ success: false, message: msg });\n }\n}\n\n// 应用中间件\napp.use(\"/api\", requireAispringAuth);\n```\n\n#### 4.2.3 核心路由\n\n```javascript\n// 获取课程包列表(支持搜索、按点击量排序)\napp.get(\"/api/packages\", (req, res) => {\n const { key } = resolveUserKey(req);\n const search = req.query.search;\n\n let list = [...PACKAGES]; // 内置课程包\n const userRows = getUserPackagesList(key, search);\n list.push(...userRows.map(userPackageToMeta));\n\n // 按点击量排序\n list.sort((a, b) => (b.clickCount || 0) - (a.clickCount || 0));\n\n res.json({ success: true, data: list });\n});\n\n// 获取课程题目\napp.get(\"/api/courses/:courseIndex/questions\", (req, res) => {\n const packageId = req.query.packageId;\n const idx = parseInt(req.params.courseIndex, 10);\n\n if (packageId && packageId.startsWith(\"up-\")) {\n // 用户自定义课程包\n const rows = db.prepare(`\n SELECT english, chinese, soundmark\n FROM user_package_statements\n WHERE package_id = ? AND course_index = ?\n ORDER BY sort_order, id\n `).all(packageId, idx - 1);\n return res.json({ success: true, data: rows });\n }\n\n // 内置课程包\n const filePath = join(COURSES_DIR, `${String(idx).padStart(2, \"0\")}.json`);\n const statements = JSON.parse(readFileSync(filePath, \"utf-8\"));\n res.json({ success: true, data: statements });\n});\n\n// 创建自定义课程包\napp.post(\"/api/packages\", (req, res) => {\n const { key } = resolveUserKey(req);\n const { name, description, sections, isPublic } = req.body;\n\n const packageId = \"up-\" + randomUUID().replace(/-/g, \"\").slice(0, 16);\n\n db.transaction(() => {\n // 插入课程包元数据\n db.prepare(`INSERT INTO user_packages ...`).run(packageId, key, name, ...);\n\n // 插入各节题目\n for (const section of sections) {\n for (const stmt of section.statements) {\n db.prepare(`INSERT INTO user_package_statements ...`).run(...);\n }\n }\n })();\n\n res.status(201).json({ success: true, data: { id: packageId } });\n});\n```\n\n### 4.3 前端实现\n\n#### 4.3.1 打字输入组件\n\n```vue\n<template>\n <div class=\"typing-input\">\n <div class=\"chinese-hint\">{{ question.chinese }}</div>\n <input\n v-model=\"userInput\"\n @keydown=\"handleKeydown\"\n :class=\"{ correct: isCorrect, wrong: isWrong }\"\n placeholder=\"输入英文...\"\n autofocus\n />\n <button @click=\"playSound\">🔊 播放</button>\n </div>\n</template>\n\n<script setup>\nimport { useEnglishSound } from \"@/composables/useEnglishSound\";\nimport { computed, ref } from \"vue\";\n\nconst props = defineProps({\n question: Object,\n});\n\nconst emit = defineEmits([\"correct\", \"wrong\"]);\n\nconst userInput = ref(\"\");\nconst { play } = useEnglishSound();\n\nconst isCorrect = computed(\n () => userInput.value.trim().toLowerCase() === props.question.english.toLowerCase(),\n);\n\nfunction handleKeydown(e) {\n if (e.key === \"Enter\") {\n if (isCorrect.value) {\n emit(\"correct\");\n userInput.value = \"\";\n } else {\n emit(\"wrong\");\n }\n }\n}\n\nfunction playSound() {\n play(props.question.english);\n}\n</script>\n```\n\n#### 4.3.2 发音功能实现\n\n```typescript\nexport function useEnglishSound() {\n const audioContext = new (window.AudioContext || window.webkitAudioContext)()\n\n async function play(text: string) {\n // 使用 Web Speech API\n const utterance = new SpeechSynthesisUtterance(text)\n utterance.lang = 'en-US'\n utterance.rate = 0.9\n speechSynthesis.speak(utterance)\n }\n\n return { play }\n}\n```\n\n### 4.4 iframe 跨域通信\n\n主站通过 iframe 嵌入单词记忆模块,使用 postMessage 实现通信:\n\n```vue\n<!-- vue-app/src/views/WordGameView.vue -->\n<template>\n <iframe\n ref=\"gameFrame\"\n :src=\"gameUrl\"\n @load=\"onFrameLoad\"\n />\n</template>\n\n<script setup>\nimport { useAuthStore } from \"@/stores/auth\";\nimport { useThemeStore } from \"@/stores/theme\";\nimport { onMounted, ref } from \"vue\";\n\nconst gameFrame = ref(null);\nconst authStore = useAuthStore();\nconst themeStore = useThemeStore();\n\nconst gameUrl = import.meta.env.PROD ? \"https://earthworm.aistudy.icu\" : \"http://localhost:5200\";\n\nfunction onFrameLoad() {\n // 同步登录状态\n gameFrame.value.contentWindow.postMessage(\n {\n type: \"AUTH_SYNC\",\n token: authStore.token,\n user: authStore.userInfo,\n },\n \"*\",\n );\n\n // 同步主题\n gameFrame.value.contentWindow.postMessage(\n {\n type: \"THEME_SYNC\",\n isDark: themeStore.isDark,\n },\n \"*\",\n );\n}\n\n// 监听 iframe 返回的消息\nwindow.addEventListener(\"message\", (event) => {\n if (event.data.type === \"PROGRESS_UPDATE\") {\n // 处理进度更新\n }\n});\n</script>\n```\n\n---\n\n## 五、部署架构\n\n### 5.1 生产环境部署\n\n```\n ┌─────────────────────────────────────┐\n │ Nginx (反向代理) │\n │ Port: 80 / 443 │\n └─────────────────────────────────────┘\n │\n ┌───────────────────────────┼───────────────────────────┐\n │ │ │\n ▼ ▼ ▼\n┌───────────────┐ ┌───────────────┐ ┌───────────────┐\n│ aistudy.icu │ │ earthworm. │ │ 静态资源 │\n│ (主站) │ │ aistudy.icu │ │ (CDN) │\n│ → :5000 │ │ → :5201 │ │ │\n└───────────────┘ └───────────────┘ └───────────────┘\n │ │\n ▼ ▼\n┌───────────────┐ ┌───────────────┐\n│ Spring Boot │ │ Node.js │\n│ JAR / Docker │ │ Express │\n└───────────────┘ └───────────────┘\n │ │\n └───────────┬───────────────┘\n ▼\n ┌───────────────────────┐\n │ MySQL 8.x │\n │ Redis 7.x │\n └───────────────────────┘\n```\n\n### 5.2 Nginx 配置示例\n\n```nginx\n# 主站\nserver {\n listen 80;\n server_name aistudy.icu;\n\n location / {\n proxy_pass http://127.0.0.1:5000;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n\n # SSE 支持\n proxy_http_version 1.1;\n proxy_set_header Connection '';\n proxy_buffering off;\n proxy_cache off;\n proxy_read_timeout 300s;\n }\n}\n\n# 单词记忆模块\nserver {\n listen 80;\n server_name earthworm.aistudy.icu;\n\n location / {\n proxy_pass http://127.0.0.1:5201;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n }\n}\n```\n\n---\n\n## 六、性能优化总结\n\n### 6.1 后端优化\n\n| 优化点 | 实现方式 | 效果 |\n| ---------- | ------------------ | ---------------- |\n| 数据库索引 | 复合索引、覆盖索引 | 查询性能提升 10x |\n| 连接池 | HikariCP 默认配置 | 减少连接创建开销 |\n| 异步处理 | @Async + 线程池 | 非阻塞响应 |\n| 缓存策略 | Redis + 本地缓存 | 减少数据库压力 |\n| 流式响应 | SSE | 首字节时间优化 |\n\n### 6.2 前端优化\n\n| 优化点 | 实现方式 | 效果 |\n| -------- | -------------------- | ---------------- |\n| 代码分割 | manualChunks | 首屏加载减少 60% |\n| 懒加载 | 路由懒加载 | 按需加载页面 |\n| 虚拟滚动 | vue-virtual-scroller | 大列表性能优化 |\n| 防抖节流 | lodash | 减少无效请求 |\n| 缓存策略 | localStorage | 减少网络请求 |\n\n---\n\n## 七、面试高频问题\n\n### Q1: 为什么选择 Spring Boot 3.x?\n\n**回答要点:**\n\n1. 要求 Java 17+,支持 Virtual Threads(虚拟线程),大幅提升并发性能\n2. 原生支持 GraalVM 编译为原生可执行文件,启动时间从秒级降到毫秒级\n3. ProblemDetail API 标准化错误响应\n4. 可观测性增强(Micrometer Tracing)\n\n### Q2: JWT Token 如何实现续期?\n\n**回答要点:**\n\n1. **短期 Token + Refresh Token**:Access Token 有效期短(如 30 分钟),Refresh Token 有效期长(如 7 天)\n2. **滑动过期**:每次请求检查 Token 剩余有效期,若低于阈值则生成新 Token 返回\n3. **本项目方案**:Token 有效期 24 小时,过期后需重新登录\n\n### Q3: 如何防止 XSS 攻击?\n\n**回答要点:**\n\n1. **输入过滤**:使用 DOMPurify 清理用户输入\n2. **输出编码**:Vue 自动转义插值表达式\n3. **CSP 策略**:Content-Security-Policy 头限制脚本来源\n4. **HttpOnly Cookie**:敏感 Cookie 不允许 JS 访问(本项目使用 localStorage,需权衡)\n\n### Q4: SSE 和 WebSocket 如何选择?\n\n| 特性 | SSE | WebSocket |\n| -------- | --------------------- | -------------- |\n| 通信方向 | 单向(服务器→客户端) | 双向 |\n| 协议 | HTTP | WS/WSS |\n| 重连 | 浏览器自动重连 | 需手动实现 |\n| 适用场景 | 推送通知、AI 流式输出 | 实时聊天、游戏 |\n\n**本项目选择 SSE 的原因**:AI 对话场景只需服务器向客户端推送数据,SSE 更简单、更轻量。\n\n### Q5: 如何保证数据库与缓存一致性?\n\n**回答要点:**\n\n1. **Cache-Aside 模式**:先更新数据库,再删除缓存\n2. **延迟双删**:删除缓存 → 更新数据库 → 延迟再删除缓存\n3. **消息队列**:通过消息队列保证最终一致性\n4. **本项目方案**:写操作直接更新数据库,读操作先查缓存再查数据库\n\n---\n\n## 八、技术亮点总结\n\n1. **多模型 AI 集成**:通过 Spring AI 统一抽象层,支持 DeepSeek、豆包等多个模型\n2. **流式响应优化**:SSE 实现打字机效果,提升用户体验\n3. **GraphQL 按需加载**:解决 REST API 过度获取/获取不足问题\n4. **PRD 流水线**:多阶段 AI 协作,自动生成需求文档\n5. **微前端架构**:iframe + postMessage 实现模块解耦\n6. **安全加固**:RSA 加密传输 + JWT 认证 + 限流防护\n\n---\n\n_文档版本: 2.0_ \n_最后更新: 2026-02-28_\n"}}, "aispring/src/main/resources/application.yml": {"e8e062a225b43bf79ca9e278530fca7c457c0fcd": {"t": 1785746941.9022975, "n": 5797, "h": "332371a2f595ee8a", "c": "spring:\n mvc:\n async:\n request-timeout: 300000 # 5 minutes\n application:\n name: ai-smart-tutor\n \n # Jackson JSON 序列化配置(将下划线转为驼峰)\n jackson:\n property-naming-strategy: LOWER_CAMEL_CASE # 使用驼峰命名(默认)\n \n # 触发DevTools重启\n # 禁用 GraphQL\n graphql:\n graphiql:\n enabled: false\n autoconfigure:\n exclude:\n - org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration\n - org.springframework.boot.autoconfigure.graphql.servlet.GraphQlWebMvcAutoConfiguration\n \n # 数据库配置(极致优化:最小化连接池以大幅降低内存占用)\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&cachePrepStmts=true&prepStmtCacheSize=100&prepStmtCacheSqlLimit=1024\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 1\n maximum-pool-size: 5\n idle-timeout: 600000\n connection-timeout: 30000\n max-lifetime: 1800000\n leak-detection-threshold: 60000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置(优化:减少内存占用,提高性能)\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: false\n properties:\n hibernate:\n \"[format_sql]\": false\n \"[jdbc.batch_size]\": 20\n \"[order_inserts]\": true\n \"[order_updates]\": true\n \"[jdbc.batch_versioned_data]\": true\n \"[generate_statistics]\": false\n \"[cache.use_second_level_cache]\": false\n \"[cache.use_query_cache]\": false\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储(极致优化:最小化连接池)\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 3000ms\n lettuce:\n pool:\n max-active: 5\n max-idle: 2\n min-idle: 1\n max-wait: 3000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置(优化:不限制上传时间,基于大小限制,提高临时文件阈值以减少磁盘IO)\n servlet:\n multipart:\n enabled: true\n max-file-size: 1000MB\n max-request-size: 1000MB\n file-size-threshold: 10MB\n # 使用流式处理,避免将整个文件加载到内存\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置(极致优化:大幅减少线程数和连接数以降低内存)\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n tomcat:\n threads:\n max: 50\n min-spare: 5\n max-connections: 200\n accept-count: 50\n connection-timeout: 300000 # 增加到 5 分钟,防止大文件传输中断\n max-http-form-post-size: -1\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:ENC(WOJOOCuurFh7APQ9My08lHfUm0dhMAO6fHVtAjblLdggFWyOZicuHGj0HUH29opv)}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置(优化:减少SQL日志输出以降低内存和IO开销)\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": INFO\n \"[org.springframework.web]\": WARN\n \"[org.hibernate.SQL]\": WARN\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": WARN\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 15\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://localhost:4173,http://127.0.0.1:3000,http://127.0.0.1:5173,http://127.0.0.1:4173,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n # 用户数据存储:开发默认当前目录;生产环境由 application-prod 指定项目外专用目录\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n file:\n # 启动时自动清理孤立的文件记录(数据库中存在但物理文件不存在)\n # 设置为 false 可禁用自动清理\n auto-cleanup-on-startup: true\n"}, "66d582c2643956640599f78c65bed8d6caee84d5": {"t": 1785765998.7814047, "n": 5797, "h": "332371a2f595ee8a", "c": "spring:\n mvc:\n async:\n request-timeout: 300000 # 5 minutes\n application:\n name: ai-smart-tutor\n \n # Jackson JSON 序列化配置(将下划线转为驼峰)\n jackson:\n property-naming-strategy: LOWER_CAMEL_CASE # 使用驼峰命名(默认)\n \n # 触发DevTools重启\n # 禁用 GraphQL\n graphql:\n graphiql:\n enabled: false\n autoconfigure:\n exclude:\n - org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration\n - org.springframework.boot.autoconfigure.graphql.servlet.GraphQlWebMvcAutoConfiguration\n \n # 数据库配置(极致优化:最小化连接池以大幅降低内存占用)\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&cachePrepStmts=true&prepStmtCacheSize=100&prepStmtCacheSqlLimit=1024\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 1\n maximum-pool-size: 5\n idle-timeout: 600000\n connection-timeout: 30000\n max-lifetime: 1800000\n leak-detection-threshold: 60000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置(优化:减少内存占用,提高性能)\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: false\n properties:\n hibernate:\n \"[format_sql]\": false\n \"[jdbc.batch_size]\": 20\n \"[order_inserts]\": true\n \"[order_updates]\": true\n \"[jdbc.batch_versioned_data]\": true\n \"[generate_statistics]\": false\n \"[cache.use_second_level_cache]\": false\n \"[cache.use_query_cache]\": false\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储(极致优化:最小化连接池)\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 3000ms\n lettuce:\n pool:\n max-active: 5\n max-idle: 2\n min-idle: 1\n max-wait: 3000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置(优化:不限制上传时间,基于大小限制,提高临时文件阈值以减少磁盘IO)\n servlet:\n multipart:\n enabled: true\n max-file-size: 1000MB\n max-request-size: 1000MB\n file-size-threshold: 10MB\n # 使用流式处理,避免将整个文件加载到内存\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置(极致优化:大幅减少线程数和连接数以降低内存)\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n tomcat:\n threads:\n max: 50\n min-spare: 5\n max-connections: 200\n accept-count: 50\n connection-timeout: 300000 # 增加到 5 分钟,防止大文件传输中断\n max-http-form-post-size: -1\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:ENC(WOJOOCuurFh7APQ9My08lHfUm0dhMAO6fHVtAjblLdggFWyOZicuHGj0HUH29opv)}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置(优化:减少SQL日志输出以降低内存和IO开销)\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": INFO\n \"[org.springframework.web]\": WARN\n \"[org.hibernate.SQL]\": WARN\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": WARN\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 15\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://localhost:4173,http://127.0.0.1:3000,http://127.0.0.1:5173,http://127.0.0.1:4173,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n # 用户数据存储:开发默认当前目录;生产环境由 application-prod 指定项目外专用目录\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n file:\n # 启动时自动清理孤立的文件记录(数据库中存在但物理文件不存在)\n # 设置为 false 可禁用自动清理\n auto-cleanup-on-startup: true\n"}, "80118682ea117636e7afe5c402a837535d97006b": {"t": 1785766000.3144174, "n": 5744, "h": "12c73fe960d0ec82", "c": "spring:\n mvc:\n async:\n request-timeout: 300000 # 5 minutes\n application:\n name: ai-smart-tutor\n \n # Jackson JSON 序列化配置(将下划线转为驼峰)\n jackson:\n property-naming-strategy: LOWER_CAMEL_CASE # 使用驼峰命名(默认)\n \n # 触发DevTools重启\n # 禁用 GraphQL\n graphql:\n graphiql:\n enabled: false\n autoconfigure:\n exclude:\n - org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration\n - org.springframework.boot.autoconfigure.graphql.servlet.GraphQlWebMvcAutoConfiguration\n \n # 数据库配置(极致优化:最小化连接池以大幅降低内存占用)\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&cachePrepStmts=true&prepStmtCacheSize=100&prepStmtCacheSqlLimit=1024\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 1\n maximum-pool-size: 5\n idle-timeout: 600000\n connection-timeout: 30000\n max-lifetime: 1800000\n leak-detection-threshold: 60000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置(优化:减少内存占用,提高性能)\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: false\n properties:\n hibernate:\n \"[format_sql]\": false\n \"[jdbc.batch_size]\": 20\n \"[order_inserts]\": true\n \"[order_updates]\": true\n \"[jdbc.batch_versioned_data]\": true\n \"[generate_statistics]\": false\n \"[cache.use_second_level_cache]\": false\n \"[cache.use_query_cache]\": false\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储(极致优化:最小化连接池)\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 3000ms\n lettuce:\n pool:\n max-active: 5\n max-idle: 2\n min-idle: 1\n max-wait: 3000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置(优化:不限制上传时间,基于大小限制,提高临时文件阈值以减少磁盘IO)\n servlet:\n multipart:\n enabled: true\n max-file-size: 1000MB\n max-request-size: 1000MB\n file-size-threshold: 10MB\n # 使用流式处理,避免将整个文件加载到内存\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置(极致优化:大幅减少线程数和连接数以降低内存)\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n tomcat:\n threads:\n max: 50\n min-spare: 5\n max-connections: 200\n accept-count: 50\n connection-timeout: 300000 # 增加到 5 分钟,防止大文件传输中断\n max-http-form-post-size: -1\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:ENC(WOJOOCuurFh7APQ9My08lHfUm0dhMAO6fHVtAjblLdggFWyOZicuHGj0HUH29opv)}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置(优化:减少SQL日志输出以降低内存和IO开销)\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": INFO\n \"[org.springframework.web]\": WARN\n \"[org.hibernate.SQL]\": WARN\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": WARN\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 15\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://localhost:4173,http://127.0.0.1:3000,http://127.0.0.1:5173,http://127.0.0.1:4173,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n file:\n # 启动时自动清理孤立的文件记录(数据库中存在但物理文件不存在)\n # 设置为 false 可禁用自动清理\n auto-cleanup-on-startup: true\n"}, "26091356bcf1c53066317af7cb88a869e00c2645": {"t": 1785766002.0197964, "n": 5352, "h": "118d29716300466d", "c": "spring:\n mvc:\n async:\n request-timeout: 300000 # 5 minutes\n application:\n name: ai-smart-tutor\n \n # 数据库配置(极致优化:最小化连接池以大幅降低内存占用)\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&cachePrepStmts=true&prepStmtCacheSize=100&prepStmtCacheSqlLimit=1024\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 1\n maximum-pool-size: 5\n idle-timeout: 600000\n connection-timeout: 30000\n max-lifetime: 1800000\n leak-detection-threshold: 60000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置(优化:减少内存占用,提高性能)\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: false\n properties:\n hibernate:\n \"[format_sql]\": false\n \"[jdbc.batch_size]\": 20\n \"[order_inserts]\": true\n \"[order_updates]\": true\n \"[jdbc.batch_versioned_data]\": true\n \"[generate_statistics]\": false\n \"[cache.use_second_level_cache]\": false\n \"[cache.use_query_cache]\": false\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储(极致优化:最小化连接池)\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 3000ms\n lettuce:\n pool:\n max-active: 5\n max-idle: 2\n min-idle: 1\n max-wait: 3000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置(优化:不限制上传时间,基于大小限制,提高临时文件阈值以减少磁盘IO)\n servlet:\n multipart:\n enabled: true\n max-file-size: 1000MB\n max-request-size: 1000MB\n file-size-threshold: 10MB\n # 使用流式处理,避免将整个文件加载到内存\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置(极致优化:大幅减少线程数和连接数以降低内存)\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n tomcat:\n threads:\n max: 50\n min-spare: 5\n max-connections: 200\n accept-count: 50\n connection-timeout: 300000 # 增加到 5 分钟,防止大文件传输中断\n max-http-form-post-size: -1\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:ENC(WOJOOCuurFh7APQ9My08lHfUm0dhMAO6fHVtAjblLdggFWyOZicuHGj0HUH29opv)}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置(优化:减少SQL日志输出以降低内存和IO开销)\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": INFO\n \"[org.springframework.web]\": WARN\n \"[org.hibernate.SQL]\": WARN\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": WARN\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 15\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://localhost:4173,http://127.0.0.1:3000,http://127.0.0.1:5173,http://127.0.0.1:4173,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n file:\n # 启动时自动清理孤立的文件记录(数据库中存在但物理文件不存在)\n # 设置为 false 可禁用自动清理\n auto-cleanup-on-startup: true\n"}, "f85aea6d2d93d53c63d9951df77a83dbfbaa1582": {"t": 1785766003.5433655, "n": 5292, "h": "4af4a120968c96f0", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置(极致优化:最小化连接池以大幅降低内存占用)\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&cachePrepStmts=true&prepStmtCacheSize=100&prepStmtCacheSqlLimit=1024\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 1\n maximum-pool-size: 5\n idle-timeout: 600000\n connection-timeout: 30000\n max-lifetime: 1800000\n leak-detection-threshold: 60000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置(优化:减少内存占用,提高性能)\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: false\n properties:\n hibernate:\n \"[format_sql]\": false\n \"[jdbc.batch_size]\": 20\n \"[order_inserts]\": true\n \"[order_updates]\": true\n \"[jdbc.batch_versioned_data]\": true\n \"[generate_statistics]\": false\n \"[cache.use_second_level_cache]\": false\n \"[cache.use_query_cache]\": false\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储(极致优化:最小化连接池)\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 3000ms\n lettuce:\n pool:\n max-active: 5\n max-idle: 2\n min-idle: 1\n max-wait: 3000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置(优化:不限制上传时间,基于大小限制,提高临时文件阈值以减少磁盘IO)\n servlet:\n multipart:\n enabled: true\n max-file-size: 1000MB\n max-request-size: 1000MB\n file-size-threshold: 10MB\n # 使用流式处理,避免将整个文件加载到内存\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置(极致优化:大幅减少线程数和连接数以降低内存)\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n tomcat:\n threads:\n max: 50\n min-spare: 5\n max-connections: 200\n accept-count: 50\n connection-timeout: 300000 # 增加到 5 分钟,防止大文件传输中断\n max-http-form-post-size: -1\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:ENC(WOJOOCuurFh7APQ9My08lHfUm0dhMAO6fHVtAjblLdggFWyOZicuHGj0HUH29opv)}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置(优化:减少SQL日志输出以降低内存和IO开销)\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": INFO\n \"[org.springframework.web]\": WARN\n \"[org.hibernate.SQL]\": WARN\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": WARN\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 15\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://localhost:4173,http://127.0.0.1:3000,http://127.0.0.1:5173,http://127.0.0.1:4173,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n file:\n # 启动时自动清理孤立的文件记录(数据库中存在但物理文件不存在)\n # 设置为 false 可禁用自动清理\n auto-cleanup-on-startup: true\n"}, "6dfb2d0a4bf958ed8f57bb15dd548d1c76e0a669": {"t": 1785766005.024385, "n": 5270, "h": "0a6987417eecca84", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置(极致优化:最小化连接池以大幅降低内存占用)\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&cachePrepStmts=true&prepStmtCacheSize=100&prepStmtCacheSqlLimit=1024\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 1\n maximum-pool-size: 5\n idle-timeout: 600000\n connection-timeout: 30000\n max-lifetime: 1800000\n leak-detection-threshold: 60000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置(优化:减少内存占用,提高性能)\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: false\n properties:\n hibernate:\n \"[format_sql]\": false\n \"[jdbc.batch_size]\": 20\n \"[order_inserts]\": true\n \"[order_updates]\": true\n \"[jdbc.batch_versioned_data]\": true\n \"[generate_statistics]\": false\n \"[cache.use_second_level_cache]\": false\n \"[cache.use_query_cache]\": false\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储(极致优化:最小化连接池)\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 3000ms\n lettuce:\n pool:\n max-active: 5\n max-idle: 2\n min-idle: 1\n max-wait: 3000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置(优化:不限制上传时间,基于大小限制,提高临时文件阈值以减少磁盘IO)\n servlet:\n multipart:\n enabled: true\n max-file-size: 1000MB\n max-request-size: 1000MB\n file-size-threshold: 10MB\n # 使用流式处理,避免将整个文件加载到内存\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置(极致优化:大幅减少线程数和连接数以降低内存)\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n tomcat:\n threads:\n max: 50\n min-spare: 5\n max-connections: 200\n accept-count: 50\n connection-timeout: 60000\n max-http-form-post-size: -1\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:ENC(WOJOOCuurFh7APQ9My08lHfUm0dhMAO6fHVtAjblLdggFWyOZicuHGj0HUH29opv)}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置(优化:减少SQL日志输出以降低内存和IO开销)\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": INFO\n \"[org.springframework.web]\": WARN\n \"[org.hibernate.SQL]\": WARN\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": WARN\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 15\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://localhost:4173,http://127.0.0.1:3000,http://127.0.0.1:5173,http://127.0.0.1:4173,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n file:\n # 启动时自动清理孤立的文件记录(数据库中存在但物理文件不存在)\n # 设置为 false 可禁用自动清理\n auto-cleanup-on-startup: true\n"}, "2522cb0562c71bbe3db552e44d90c92090da8205": {"t": 1785766006.7130783, "n": 5204, "h": "400e14ab5e5ada2b", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置(极致优化:最小化连接池以大幅降低内存占用)\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&cachePrepStmts=true&prepStmtCacheSize=100&prepStmtCacheSqlLimit=1024\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 1\n maximum-pool-size: 5\n idle-timeout: 600000\n connection-timeout: 30000\n max-lifetime: 1800000\n leak-detection-threshold: 60000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置(优化:减少内存占用,提高性能)\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: false\n properties:\n hibernate:\n \"[format_sql]\": false\n \"[jdbc.batch_size]\": 20\n \"[order_inserts]\": true\n \"[order_updates]\": true\n \"[jdbc.batch_versioned_data]\": true\n \"[generate_statistics]\": false\n \"[cache.use_second_level_cache]\": false\n \"[cache.use_query_cache]\": false\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储(极致优化:最小化连接池)\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 3000ms\n lettuce:\n pool:\n max-active: 5\n max-idle: 2\n min-idle: 1\n max-wait: 3000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置(优化:不限制上传时间,基于大小限制,提高临时文件阈值以减少磁盘IO)\n servlet:\n multipart:\n enabled: true\n max-file-size: 1000MB\n max-request-size: 1000MB\n file-size-threshold: 10MB\n # 使用流式处理,避免将整个文件加载到内存\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置(极致优化:大幅减少线程数和连接数以降低内存)\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n tomcat:\n threads:\n max: 50\n min-spare: 5\n max-connections: 200\n accept-count: 50\n connection-timeout: 60000\n max-http-form-post-size: -1\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:ENC(wbBUUElYyKgb7BxF7CgZ/wpR3RHQdJ3TgxHIhSQ0GQck1QPp1/wwd0JdHmQ7W9Xk)}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:ENC(WOJOOCuurFh7APQ9My08lHfUm0dhMAO6fHVtAjblLdggFWyOZicuHGj0HUH29opv)}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置(优化:减少SQL日志输出以降低内存和IO开销)\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": INFO\n \"[org.springframework.web]\": WARN\n \"[org.hibernate.SQL]\": WARN\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": WARN\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 15\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n file:\n # 启动时自动清理孤立的文件记录(数据库中存在但物理文件不存在)\n # 设置为 false 可禁用自动清理\n auto-cleanup-on-startup: true\n"}, "701cf14f33422901d62ef01b9b2fb8171062ae93": {"t": 1785766008.2328632, "n": 5138, "h": "273fe2df2dcf18bb", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置(极致优化:最小化连接池以大幅降低内存占用)\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&cachePrepStmts=true&prepStmtCacheSize=100&prepStmtCacheSqlLimit=1024\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 1\n maximum-pool-size: 5\n idle-timeout: 600000\n connection-timeout: 30000\n max-lifetime: 1800000\n leak-detection-threshold: 60000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置(优化:减少内存占用,提高性能)\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: false\n properties:\n hibernate:\n \"[format_sql]\": false\n \"[jdbc.batch_size]\": 20\n \"[order_inserts]\": true\n \"[order_updates]\": true\n \"[jdbc.batch_versioned_data]\": true\n \"[generate_statistics]\": false\n \"[cache.use_second_level_cache]\": false\n \"[cache.use_query_cache]\": false\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储(极致优化:最小化连接池)\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 3000ms\n lettuce:\n pool:\n max-active: 5\n max-idle: 2\n min-idle: 1\n max-wait: 3000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置(优化:不限制上传时间,基于大小限制,提高临时文件阈值以减少磁盘IO)\n servlet:\n multipart:\n enabled: true\n max-file-size: 1000MB\n max-request-size: 1000MB\n file-size-threshold: 10MB\n # 使用流式处理,避免将整个文件加载到内存\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(PXkZZlkx8FKyKOigb+jXg54bgTu/38HyEAjzh74ohFt0yN8NfIWqAd0flh3y4p2q)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置(极致优化:大幅减少线程数和连接数以降低内存)\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n tomcat:\n threads:\n max: 50\n min-spare: 5\n max-connections: 200\n accept-count: 50\n connection-timeout: 60000\n max-http-form-post-size: -1\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-7f17e997c31e4d82bf0286116ad34226}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置(优化:减少SQL日志输出以降低内存和IO开销)\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": INFO\n \"[org.springframework.web]\": WARN\n \"[org.hibernate.SQL]\": WARN\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": WARN\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 15\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n file:\n # 启动时自动清理孤立的文件记录(数据库中存在但物理文件不存在)\n # 设置为 false 可禁用自动清理\n auto-cleanup-on-startup: true\n\n"}, "2633330805c4ffd3413042f60bea72e2643fbb5d": {"t": 1785766009.6220639, "n": 5135, "h": "bd192b9d36f38552", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置(优化:减少连接池大小以降低内存占用)\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&cachePrepStmts=true&prepStmtCacheSize=250&prepStmtCacheSqlLimit=2048\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 2\n maximum-pool-size: 10\n idle-timeout: 600000\n connection-timeout: 30000\n max-lifetime: 1800000\n leak-detection-threshold: 60000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置(优化:减少内存占用,提高性能)\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: false\n properties:\n hibernate:\n \"[format_sql]\": false\n \"[jdbc.batch_size]\": 20\n \"[order_inserts]\": true\n \"[order_updates]\": true\n \"[jdbc.batch_versioned_data]\": true\n \"[generate_statistics]\": false\n \"[cache.use_second_level_cache]\": false\n \"[cache.use_query_cache]\": false\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储(优化:减少连接池大小)\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 3000ms\n lettuce:\n pool:\n max-active: 10\n max-idle: 5\n min-idle: 2\n max-wait: 3000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置(优化:不限制上传时间,基于大小限制,提高临时文件阈值以减少磁盘IO)\n servlet:\n multipart:\n enabled: true\n max-file-size: 1000MB\n max-request-size: 1000MB\n file-size-threshold: 10MB\n # 使用流式处理,避免将整个文件加载到内存\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(PXkZZlkx8FKyKOigb+jXg54bgTu/38HyEAjzh74ohFt0yN8NfIWqAd0flh3y4p2q)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置(优化:添加线程配置和连接超时配置)\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n tomcat:\n threads:\n max: 100\n min-spare: 10\n max-connections: 1000\n accept-count: 100\n connection-timeout: 60000\n max-http-form-post-size: -1\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-7f17e997c31e4d82bf0286116ad34226}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置(优化:减少SQL日志输出以降低内存和IO开销)\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": INFO\n \"[org.springframework.web]\": WARN\n \"[org.hibernate.SQL]\": WARN\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": WARN\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 15\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n file:\n # 启动时自动清理孤立的文件记录(数据库中存在但物理文件不存在)\n # 设置为 false 可禁用自动清理\n auto-cleanup-on-startup: true\n\n"}, "1e1399045962266b24c9da6a9a114739512f581e": {"t": 1785766011.1576989, "n": 5133, "h": "c11db736263c0d01", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置(优化:减少连接池大小以降低内存占用)\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&cachePrepStmts=true&prepStmtCacheSize=250&prepStmtCacheSqlLimit=2048\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 2\n maximum-pool-size: 10\n idle-timeout: 600000\n connection-timeout: 30000\n max-lifetime: 1800000\n leak-detection-threshold: 60000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置(优化:减少内存占用,提高性能)\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: false\n properties:\n hibernate:\n \"[format_sql]\": false\n \"[jdbc.batch_size]\": 20\n \"[order_inserts]\": true\n \"[order_updates]\": true\n \"[jdbc.batch_versioned_data]\": true\n \"[generate_statistics]\": false\n \"[cache.use_second_level_cache]\": false\n \"[cache.use_query_cache]\": false\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储(优化:减少连接池大小)\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 3000ms\n lettuce:\n pool:\n max-active: 10\n max-idle: 5\n min-idle: 2\n max-wait: 3000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置(优化:不限制上传时间,基于大小限制,提高临时文件阈值以减少磁盘IO)\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 10MB\n # 使用流式处理,避免将整个文件加载到内存\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(PXkZZlkx8FKyKOigb+jXg54bgTu/38HyEAjzh74ohFt0yN8NfIWqAd0flh3y4p2q)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置(优化:添加线程配置和连接超时配置)\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n tomcat:\n threads:\n max: 100\n min-spare: 10\n max-connections: 1000\n accept-count: 100\n connection-timeout: 60000\n max-http-form-post-size: -1\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-7f17e997c31e4d82bf0286116ad34226}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置(优化:减少SQL日志输出以降低内存和IO开销)\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": INFO\n \"[org.springframework.web]\": WARN\n \"[org.hibernate.SQL]\": WARN\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": WARN\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 15\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n file:\n # 启动时自动清理孤立的文件记录(数据库中存在但物理文件不存在)\n # 设置为 false 可禁用自动清理\n auto-cleanup-on-startup: true\n\n"}, "ba818b965cd47d653d83c89b3f0555ff9eb512ae": {"t": 1785766012.6000047, "n": 4411, "h": "155f83d6be14ca88", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n \"[format_sql]\": true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(PXkZZlkx8FKyKOigb+jXg54bgTu/38HyEAjzh74ohFt0yN8NfIWqAd0flh3y4p2q)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-7f17e997c31e4d82bf0286116ad34226}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": DEBUG\n \"[org.springframework.web]\": INFO\n \"[org.hibernate.SQL]\": DEBUG\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n file:\n # 启动时自动清理孤立的文件记录(数据库中存在但物理文件不存在)\n # 设置为 false 可禁用自动清理\n auto-cleanup-on-startup: true\n\n"}, "e75385858e413b3131fee2bc6a074fd16a52c21b": {"t": 1785766014.2422338, "n": 4308, "h": "a0864d2782264429", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n \"[format_sql]\": true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(PXkZZlkx8FKyKOigb+jXg54bgTu/38HyEAjzh74ohFt0yN8NfIWqAd0flh3y4p2q)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-7f17e997c31e4d82bf0286116ad34226}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": DEBUG\n \"[org.springframework.web]\": INFO\n \"[org.hibernate.SQL]\": DEBUG\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "cc6661a63a7cddd6c80788107fae4d22d77f797e": {"t": 1785766015.513871, "n": 4308, "h": "2c75298675c14a71", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n validate-on-migrate: false\n locations: classpath:db/migration\n\n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n \"[format_sql]\": true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(PXkZZlkx8FKyKOigb+jXg54bgTu/38HyEAjzh74ohFt0yN8NfIWqAd0flh3y4p2q)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-7f17e997c31e4d82bf0286116ad34226}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": DEBUG\n \"[org.springframework.web]\": INFO\n \"[org.hibernate.SQL]\": DEBUG\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "a3e058a386de179be93739f2267593937f5d71bc": {"t": 1785766017.0157943, "n": 4277, "h": "586aeb5334482bc1", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:ENC(ChwOBS62kHNKHEfErml6nw==)}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n locations: classpath:db/migration\n\n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n \"[format_sql]\": true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:ENC(LZy5qJumY+qHyFxAmZ3NLwqIl6mXaN/CBQ8KrjhAEIg=)}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:ENC(PXkZZlkx8FKyKOigb+jXg54bgTu/38HyEAjzh74ohFt0yN8NfIWqAd0flh3y4p2q)}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n\n# Jasypt 加密配置\njasypt:\n encryptor:\n password: ${JASYPT_PASSWORD:aistudy_secret}\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-7f17e997c31e4d82bf0286116ad34226}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": DEBUG\n \"[org.springframework.web]\": INFO\n \"[org.hibernate.SQL]\": DEBUG\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "7afa89fdd357a3b340a7ffb007e6916649b0cd6d": {"t": 1785766018.6395051, "n": 4015, "h": "409538ddae6da2e1", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:123456}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n locations: classpath:db/migration\n\n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n \"[format_sql]\": true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-7f17e997c31e4d82bf0286116ad34226}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-7f17e997c31e4d82bf0286116ad34226}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": DEBUG\n \"[org.springframework.web]\": INFO\n \"[org.hibernate.SQL]\": DEBUG\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "cfd5891c7d6966e98f3bd830d9952e026c070ac7": {"t": 1785766020.546475, "n": 4015, "h": "50ef2527d25fed32", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:123456}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n locations: classpath:db/migration\n\n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n \"[format_sql]\": true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": DEBUG\n \"[org.springframework.web]\": INFO\n \"[org.hibernate.SQL]\": DEBUG\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "e98facd48748eab5a3f91f2334cb984b331fbc39": {"t": 1785766022.5201743, "n": 4068, "h": "b4da158d0fe1b3a8", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:123456}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n locations: classpath:db/migration\n\n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n \"[format_sql]\": true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": DEBUG\n \"[org.springframework.web]\": INFO\n \"[org.hibernate.SQL]\": DEBUG\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://aistudy.icu,https://aistudy.icu,http://www.aistudy.icu,https://www.aistudy.icu\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "17d22879142605e5a65babcb2b84d3cc7051244f": {"t": 1785766024.0584168, "n": 3982, "h": "39dc4c4e247022b2", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:123456}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # Flyway 配置\n flyway:\n enabled: true\n baseline-on-migrate: true\n baseline-version: 1\n locations: classpath:db/migration\n\n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n \"[format_sql]\": true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": DEBUG\n \"[org.springframework.web]\": INFO\n \"[org.hibernate.SQL]\": DEBUG\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "677822bb6aa5a354aad1119c4bdb41d3d1c381f7": {"t": 1785766025.5711286, "n": 3847, "h": "254e4e1f88d5d9f1", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:ipv6_education}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: ${DB_USERNAME:root}\n password: ${DB_PASSWORD:123456}\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n \"[format_sql]\": true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置 (生产环境默认禁用)\n devtools:\n restart:\n enabled: false\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: false\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": DEBUG\n \"[org.springframework.web]\": INFO\n \"[org.hibernate.SQL]\": DEBUG\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "7fda2dacd8a9036e8414337ef3815506a3a9f176": {"t": 1785766027.1234593, "n": 3771, "h": "a6de2a10792ab47a", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n \"[format_sql]\": true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置\nlogging:\n level:\n root: INFO\n \"[com.aispring]\": DEBUG\n \"[org.springframework.web]\": INFO\n \"[org.hibernate.SQL]\": DEBUG\n \"[org.hibernate.type.descriptor.sql.BasicBinder]\": TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "d7395b740326767dcca1e2aadc3abb8d681bfa4e": {"t": 1785766028.6566975, "n": 3751, "h": "a774211e7895e3c7", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n format_sql: true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 日志配置\nlogging:\n level:\n root: INFO\n com.aispring: DEBUG\n org.springframework.web: INFO\n org.hibernate.SQL: DEBUG\n org.hibernate.type.descriptor.sql.BasicBinder: TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n auth:\n verification-code-expire-minutes: 5\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "9db8986c1d9bc1a021fb2ea65e6ca53fa6b9323b": {"t": 1785766030.1207983, "n": 4180, "h": "2ec5ba29e2c7a908", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n format_sql: true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n data:\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n # Session状态TTL配置\n session-state:\n ttl: 86400 # 24小时(秒)\n inactive-ttl: 3600 # 非活跃会话1小时后过期(秒)\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 文件存储配置\nfile:\n upload-dir: ${FILE_UPLOAD_DIR:uploads}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n max-file-size: ${MAX_FILE_SIZE:524288000} # 500MB\n allowed-extensions: jpg,jpeg,png,gif,bmp,svg,webp,txt,md,csv,json,xml,html,css,js,doc,docx,pdf,rtf,xls,xlsx,ppt,pptx,mp4,avi,mov,wmv,flv,mkv,zip,rar,7z,tar,gz\n\n# 验证码配置\nverification:\n code-expire-minutes: 5\n\n# 日志配置\nlogging:\n level:\n root: INFO\n com.aispring: DEBUG\n org.springframework.web: INFO\n org.hibernate.SQL: DEBUG\n org.hibernate.type.descriptor.sql.BasicBinder: TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "d3a6f2822ceb58e8d10d038cc6b37e5241f20b48": {"t": 1785766031.6099982, "n": 4146, "h": "d42cde51deffcb09", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n format_sql: true\n open-in-view: false\n \n # Redis配置 - 用于Session状态存储\n redis:\n host: ${REDIS_HOST:localhost}\n port: ${REDIS_PORT:6379}\n database: ${REDIS_DATABASE:1}\n password: ${REDIS_PASSWORD:}\n timeout: 2000ms\n lettuce:\n pool:\n max-active: 20\n max-idle: 10\n min-idle: 5\n max-wait: 2000ms\n shutdown-timeout: 200ms\n # Session状态TTL配置\n session-state:\n ttl: 86400 # 24小时(秒)\n inactive-ttl: 3600 # 非活跃会话1小时后过期(秒)\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 文件存储配置\nfile:\n upload-dir: ${FILE_UPLOAD_DIR:uploads}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n max-file-size: ${MAX_FILE_SIZE:524288000} # 500MB\n allowed-extensions: jpg,jpeg,png,gif,bmp,svg,webp,txt,md,csv,json,xml,html,css,js,doc,docx,pdf,rtf,xls,xlsx,ppt,pptx,mp4,avi,mov,wmv,flv,mkv,zip,rar,7z,tar,gz\n\n# 验证码配置\nverification:\n code-expire-minutes: 5\n\n# 日志配置\nlogging:\n level:\n root: INFO\n com.aispring: DEBUG\n org.springframework.web: INFO\n org.hibernate.SQL: DEBUG\n org.hibernate.type.descriptor.sql.BasicBinder: TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "46bb93a37aabf3b1fcc27babcd0724fa848c360b": {"t": 1785766033.5395615, "n": 3701, "h": "ed55535c8c41d0cb", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n format_sql: true\n open-in-view: false\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:8192}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:8192}\n\n# 文件存储配置\nfile:\n upload-dir: ${FILE_UPLOAD_DIR:uploads}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n max-file-size: ${MAX_FILE_SIZE:524288000} # 500MB\n allowed-extensions: jpg,jpeg,png,gif,bmp,svg,webp,txt,md,csv,json,xml,html,css,js,doc,docx,pdf,rtf,xls,xlsx,ppt,pptx,mp4,avi,mov,wmv,flv,mkv,zip,rar,7z,tar,gz\n\n# 验证码配置\nverification:\n code-expire-minutes: 5\n\n# 日志配置\nlogging:\n level:\n root: INFO\n com.aispring: DEBUG\n org.springframework.web: INFO\n org.hibernate.SQL: DEBUG\n org.hibernate.type.descriptor.sql.BasicBinder: TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "e7f575d42d7106cd234e9e7c09fb432387ae1e39": {"t": 1785766034.9226599, "n": 3701, "h": "233723590c89f7c6", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n format_sql: true\n open-in-view: false\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:4096}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com}\n max-tokens: ${MAX_TOKEN:4096}\n\n# 文件存储配置\nfile:\n upload-dir: ${FILE_UPLOAD_DIR:uploads}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n max-file-size: ${MAX_FILE_SIZE:524288000} # 500MB\n allowed-extensions: jpg,jpeg,png,gif,bmp,svg,webp,txt,md,csv,json,xml,html,css,js,doc,docx,pdf,rtf,xls,xlsx,ppt,pptx,mp4,avi,mov,wmv,flv,mkv,zip,rar,7z,tar,gz\n\n# 验证码配置\nverification:\n code-expire-minutes: 5\n\n# 日志配置\nlogging:\n level:\n root: INFO\n com.aispring: DEBUG\n org.springframework.web: INFO\n org.hibernate.SQL: DEBUG\n org.hibernate.type.descriptor.sql.BasicBinder: TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "0c8d0249f91880e690bb4eb13b689ae3931d4dbe": {"t": 1785766036.8737895, "n": 3693, "h": "800ff9ef0a988299", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n format_sql: true\n open-in-view: false\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:4096}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://api.doubao.com/v1}\n max-tokens: ${MAX_TOKEN:4096}\n\n# 文件存储配置\nfile:\n upload-dir: ${FILE_UPLOAD_DIR:uploads}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n max-file-size: ${MAX_FILE_SIZE:524288000} # 500MB\n allowed-extensions: jpg,jpeg,png,gif,bmp,svg,webp,txt,md,csv,json,xml,html,css,js,doc,docx,pdf,rtf,xls,xlsx,ppt,pptx,mp4,avi,mov,wmv,flv,mkv,zip,rar,7z,tar,gz\n\n# 验证码配置\nverification:\n code-expire-minutes: 5\n\n# 日志配置\nlogging:\n level:\n root: INFO\n com.aispring: DEBUG\n org.springframework.web: INFO\n org.hibernate.SQL: DEBUG\n org.hibernate.type.descriptor.sql.BasicBinder: TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "a578cb3d3bb2a7267217b05cb673bc56ebcf098a": {"t": 1785766038.5939898, "n": 3748, "h": "cfdcee3c2da3c32a", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n format_sql: true\n open-in-view: false\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:${AI_DEEPSEEK_API_KEY:sk-placeholder}}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:4096}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:${AI_DEEPSEEK_API_KEY:}}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_API_KEY:${AI_DOUBAO_API_KEY:${DOUBAO_KEY:}}}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com/api/v3}\n model: ${DOUBAO_MODEL:${AI_DOUBAO_MODEL:}}\n max-tokens: ${MAX_TOKEN:4096}\n\n# 文件存储配置\nfile:\n upload-dir: ${FILE_UPLOAD_DIR:uploads}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n max-file-size: ${MAX_FILE_SIZE:524288000} # 500MB\n allowed-extensions: jpg,jpeg,png,gif,bmp,svg,webp,txt,md,csv,json,xml,html,css,js,doc,docx,pdf,rtf,xls,xlsx,ppt,pptx,mp4,avi,mov,wmv,flv,mkv,zip,rar,7z,tar,gz\n\n# 验证码配置\nverification:\n code-expire-minutes: 5\n\n# 日志配置\nlogging:\n level:\n root: INFO\n com.aispring: DEBUG\n org.springframework.web: INFO\n org.hibernate.SQL: DEBUG\n org.hibernate.type.descriptor.sql.BasicBinder: TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "c813c4e1d0dd9d68ecb69dbb1745db35e0ece6e2": {"t": 1785766040.5170627, "n": 3634, "h": "f03f628938e41522", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n format_sql: true\n open-in-view: false\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:4096}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_API_KEY:}\n api-url: ${DOUBAO_BASEURL:https://ark.cn-beijing.volces.com/api/v3}\n model: ${DOUBAO_MODEL:}\n max-tokens: ${MAX_TOKEN:4096}\n\n# 文件存储配置\nfile:\n upload-dir: ${FILE_UPLOAD_DIR:uploads}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n max-file-size: ${MAX_FILE_SIZE:524288000} # 500MB\n allowed-extensions: jpg,jpeg,png,gif,bmp,svg,webp,txt,md,csv,json,xml,html,css,js,doc,docx,pdf,rtf,xls,xlsx,ppt,pptx,mp4,avi,mov,wmv,flv,mkv,zip,rar,7z,tar,gz\n\n# 验证码配置\nverification:\n code-expire-minutes: 5\n\n# 日志配置\nlogging:\n level:\n root: INFO\n com.aispring: DEBUG\n org.springframework.web: INFO\n org.hibernate.SQL: DEBUG\n org.hibernate.type.descriptor.sql.BasicBinder: TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "b38d2c2f707eec9117ef3603f89753c076e596d2": {"t": 1785766042.2447956, "n": 3735, "h": "e55543e473689d87", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n format_sql: true\n open-in-view: false\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:4096}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://api.doubao.com/v1}\n model: ${DOUBAO_MODEL:doubao-pro-32k}\n max-tokens: ${MAX_TOKEN:4096}\n\n# 文件存储配置\nfile:\n upload-dir: ${FILE_UPLOAD_DIR:uploads}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n max-file-size: ${MAX_FILE_SIZE:524288000} # 500MB\n allowed-extensions: jpg,jpeg,png,gif,bmp,svg,webp,txt,md,csv,json,xml,html,css,js,doc,docx,pdf,rtf,xls,xlsx,ppt,pptx,mp4,avi,mov,wmv,flv,mkv,zip,rar,7z,tar,gz\n\n# 验证码配置\nverification:\n code-expire-minutes: 5\n\n# 日志配置\nlogging:\n level:\n root: INFO\n com.aispring: DEBUG\n org.springframework.web: INFO\n org.hibernate.SQL: DEBUG\n org.hibernate.type.descriptor.sql.BasicBinder: TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "4d2455559c6555a620890eb2c3def041e93d13a8": {"t": 1785766043.8328443, "n": 3693, "h": "800ff9ef0a988299", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n format_sql: true\n open-in-view: false\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:4096}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://api.doubao.com/v1}\n max-tokens: ${MAX_TOKEN:4096}\n\n# 文件存储配置\nfile:\n upload-dir: ${FILE_UPLOAD_DIR:uploads}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n max-file-size: ${MAX_FILE_SIZE:524288000} # 500MB\n allowed-extensions: jpg,jpeg,png,gif,bmp,svg,webp,txt,md,csv,json,xml,html,css,js,doc,docx,pdf,rtf,xls,xlsx,ppt,pptx,mp4,avi,mov,wmv,flv,mkv,zip,rar,7z,tar,gz\n\n# 验证码配置\nverification:\n code-expire-minutes: 5\n\n# 日志配置\nlogging:\n level:\n root: INFO\n com.aispring: DEBUG\n org.springframework.web: INFO\n org.hibernate.SQL: DEBUG\n org.hibernate.type.descriptor.sql.BasicBinder: TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}, "a899cd47d408faa96fe21e9dfb5d8519ebffae1c": {"t": 1785766045.2330306, "n": 3690, "h": "1b69cd3c5baa72b0", "c": "spring:\n application:\n name: ai-smart-tutor\n \n # 数据库配置\n datasource:\n url: jdbc:mysql://localhost:3306/ipv6_education?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\n username: root\n password: 123456\n driver-class-name: com.mysql.cj.jdbc.Driver\n hikari:\n minimum-idle: 5\n maximum-pool-size: 20\n idle-timeout: 300000\n connection-timeout: 20000\n \n # JPA配置\n jpa:\n hibernate:\n ddl-auto: update\n show-sql: true\n properties:\n hibernate:\n dialect: org.hibernate.dialect.MySQL8Dialect\n format_sql: true\n open-in-view: false\n \n # 文件上传配置\n servlet:\n multipart:\n enabled: true\n max-file-size: 500MB\n max-request-size: 500MB\n file-size-threshold: 2KB\n \n # 邮件配置\n mail:\n host: smtp.qq.com\n port: 587\n username: ${SENDER_EMAIL:3301767269@qq.com}\n password: ${SENDER_PASSWORD:dinasyxqstbodbej}\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n ssl:\n trust: smtp.qq.com\n \n # 热部署配置\n devtools:\n restart:\n enabled: true\n poll-interval: 2000ms\n quiet-period: 1000ms\n log-condition-evaluation-delta: false\n exclude: META-INF/maven/**,META-INF/resources/**,static/**,public/**,templates/**,**/*Test.class,**/*Tests.class\n additional-exclude: logs/**\n livereload:\n enabled: true\n add-properties: true\n \n # Spring AI配置 - DeepSeek (兼容OpenAI API)\n ai:\n openai:\n api-key: ${DEEPSEEK_API_KEY:sk-45021a61b84a4693a1db4deb72cec673}\n base-url: ${DEEPSEEK_API_URL:${DEEPSEEK_URL:https://api.deepseek.com}}\n chat:\n options:\n model: deepseek-chat\n temperature: 0.7\n max-tokens: ${MAX_TOKEN:4096}\n\n# 服务器配置\nserver:\n port: 5000\n servlet:\n context-path: /\n compression:\n enabled: true\n mime-types: application/json,application/xml,text/html,text/xml,text/plain\n\n# JWT配置\njwt:\n secret: ${JWT_SECRET_KEY:default_development_key_change_in_production}\n expiration: 7200000 # 2小时,单位毫秒\n\n# AI模型配置\nai:\n deepseek:\n api-key: ${DEEPSEEK_API_KEY:45021a61b84a4693a1db4deb72cec673}\n api-url: ${DEEPSEEK_URL:https://api.deepseek.com}\n doubao:\n api-key: ${DOUBAO_KEY:6261fd4e-7ac5-46de-95a6-f68f00243230}\n api-url: ${DOUBAO_BASEURL:https://api.doubao.com/v1}\n max-tokens: ${MAX_TOKEN:4096}\n\n# 文件存储配置\nfile:\n upload-dir: ${FILE_UPLOAD_DIR:uploads}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n max-file-size: ${MAX_FILE_SIZE:524288000} # 500MB\n allowed-extensions: jpg,jpeg,png,gif,bmp,svg,webp,txt,md,csv,json,xml,html,css,js,doc,docx,pdf,rtf,xls,xlsx,ppt,pptx,mp4,avi,mov,wmv,flv,mkv,zip,rar,7z,tar,gz\n\n# 验证码配置\nverification:\n code-expire-minutes: 5\n\n# 日志配置\nlogging:\n level:\n root: INFO\n com.aispring: DEBUG\n org.springframework.web: INFO\n org.hibernate.SQL: DEBUG\n org.hibernate.type.descriptor.sql.BasicBinder: TRACE\n pattern:\n console: \"%d{yyyy-MM-dd HH:mm:ss} - %msg%n\"\n file: \"%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n\"\n file:\n name: logs/application.log\n max-size: 10MB\n max-history: 30\n\n# CORS配置\ncors:\n allowed-origins: http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000\n allowed-methods: GET,POST,PUT,DELETE,OPTIONS\n allowed-headers: \"*\"\n allow-credentials: true\n max-age: 3600\n\n# 应用信息\napp:\n name: AI智能学习导师\n version: 1.0.0\n description: 基于Spring Boot的AI智能学习助手\n storage:\n root-dir: ${APP_STORAGE_ROOT:./}\n cloud-disk-dir: ${CLOUD_DISK_DIR:cloud_disk}\n avatars-dir: ${AVATARS_DIR:avatars}\n cloud-disk:\n path: ./cloud_disk\n\n"}}}}