# 系统优化说明文档 ## 优化日期 2025年12月1日 ## 优化内容概览 ### 优化1:大模型API - 从远程改为本地 ✅ ### 优化2:用户导入性能 - 批量处理优化 ✅ --- ## 优化1:大模型API配置修改 ### 问题背景 系统使用远程Kimi API进行AI分析,存在以下问题: - 依赖外部网络,可能不稳定 - 需要API Key,有安全风险 - 调用次数可能受限 - 成本较高 ### 修改方案 将远程API改为本地大模型(如Ollama),支持离线使用。 ### 修改文件 #### 1. `xinli-ui/src/views/psychology/report/comprehensive.vue` ```javascript // 修改前 API_URL: 'https://api.moonshot.cn/v1/chat/completions', API_KEY: 'sk-U9fdriPxwBcrpWW0Ite3N0eVtX7VxnqqqYUIBAdWd1hgEA9m', MODEL: 'moonshot-v1-32k' // 修改后 API_URL: 'http://localhost:11434/v1/chat/completions', API_KEY: '', // 本地模型不需要API Key MODEL: 'qwen2.5:7b' // 根据实际使用的本地模型修改 ``` #### 2. `xinli-ui/src/views/psychology/report/index.vue` ```javascript // 修改前 const API_URL = 'https://api.moonshot.cn/v1/chat/completions'; const API_KEY = 'sk-U9fdriPxwBcrpWW0Ite3N0eVtX7VxnqqqYUIBAdWd1hgEA9m'; const MODEL = 'moonshot-v1-32k'; // 修改后 const API_URL = 'http://localhost:11434/v1/chat/completions'; const API_KEY = ''; // 本地模型不需要API Key const MODEL = 'qwen2.5:7b'; // 根据实际使用的本地模型修改 ``` #### 3. `xinli-ui/src/views/psychology/report/detail.vue` ```javascript // 修改前 const API_URL = 'https://api.moonshot.cn/v1/chat/completions'; const API_KEY = 'sk-U9fdriPxwBcrpWW0Ite3N0eVtX7VxnqqqYUIBAdWd1hgEA9m'; const MODEL = 'moonshot-v1-32k'; // 修改后 const API_URL = 'http://localhost:11434/v1/chat/completions'; const API_KEY = ''; // 本地模型不需要API Key const MODEL = 'qwen2.5:7b'; // 根据实际使用的本地模型修改 ``` ### 本地模型部署 #### 使用Ollama(推荐) 1. **安装Ollama** ```bash # Windows # 下载并安装:https://ollama.com/download # Linux curl -fsSL https://ollama.com/install.sh | sh # MacOS brew install ollama ``` 2. **下载模型** ```bash # 下载Qwen2.5模型(推荐,支持中文) ollama pull qwen2.5:7b # 或其他中文模型 ollama pull qwen:14b ollama pull chatglm3:6b ``` 3. **启动服务** ```bash ollama serve ``` 服务会在 `http://localhost:11434` 启动 4. **测试** ```bash curl http://localhost:11434/api/generate -d '{ "model": "qwen2.5:7b", "prompt": "你好" }' ``` ### 修改后的优势 - ✅ **离线可用**:不依赖外部网络 - ✅ **无需API Key**:本地部署,无需管理密钥 - ✅ **无调用限制**:可以无限次调用 - ✅ **数据安全**:数据不会发送到外部 - ✅ **成本降低**:无API调用费用 - ✅ **可定制**:可以选择不同的本地模型 ### 注意事项 1. **模型选择**:根据服务器配置选择合适的模型 - 7B模型:需要至少8GB内存 - 14B模型:需要至少16GB内存 2. **端口配置**:确保`11434`端口未被占用 3. **性能**:本地模型响应速度取决于硬件性能 4. **模型更换**:如果使用其他模型,需要同步修改代码中的`MODEL`参数 --- ## 优化2:用户导入性能优化 ### 性能问题分析 #### 优化前的问题 导入3000条用户数据需要10几分钟,原因: 1. **逐条查询**:3000次数据库查询 ```java for (PsyUserProfile profile : profileList) { // 每条都要查询一次数据库 PsyUserProfile existProfile = profileMapper.selectProfileByInfoNumber(profile.getInfoNumber()); } ``` 2. **逐条插入/更新**:3000次数据库写操作 ```java for (PsyUserProfile profile : profileList) { if (existProfile == null) { this.insertProfile(profile); // 单条插入 } else { this.updateProfile(profile); // 单条更新 } } ``` **总计**: 6000+ 次数据库操作! #### 性能瓶颈 - **N+1问题**:每条数据都触发一次查询 - **网络开销**:每次操作都有网络往返 - **事务开销**:每次操作都可能有事务提交 - **索引维护**:每次插入都要更新索引 ### 优化方案 #### 核心思路 将**逐条处理**改为**批量处理** #### 优化步骤 **步骤1:批量查询(1次查询替代3000次)** ```java // 收集所有infoNumber List infoNumbers = profileList.stream() .map(PsyUserProfile::getInfoNumber) .collect(Collectors.toList()); // 一次性查询所有已存在的记录 List existingProfiles = profileMapper.selectProfilesByInfoNumbers(infoNumbers); ``` **步骤2:数据分类** ```java List toInsertList = new ArrayList<>(); // 待插入 List toUpdateList = new ArrayList<>(); // 待更新 for (PsyUserProfile profile : validProfiles) { if (existingMap.containsKey(profile.getInfoNumber())) { toUpdateList.add(profile); } else { toInsertList.add(profile); } } ``` **步骤3:批量插入(每批500条)** ```java int batchSize = 500; for (int i = 0; i < toInsertList.size(); i += batchSize) { List batch = toInsertList.subList(i, i + batchSize); profileMapper.batchInsertProfiles(batch); // 批量插入500条 } ``` **步骤4:批量更新(每批500条)** ```java for (int i = 0; i < toUpdateList.size(); i += batchSize) { List batch = toUpdateList.subList(i, i + batchSize); profileMapper.batchUpdateProfiles(batch); // 批量更新500条 } ``` ### 修改的文件 #### 1. Mapper接口 - `PsyUserProfileMapper.java` **新增方法**: ```java /** * 批量查询档案(根据信息编号列表) */ public List selectProfilesByInfoNumbers(List infoNumbers); /** * 批量插入档案 */ public int batchInsertProfiles(List profileList); /** * 批量更新档案 */ public int batchUpdateProfiles(List profileList); ``` #### 2. Mapper XML - `PsyUserProfileMapper.xml` **批量查询SQL**: ```xml ``` **批量插入SQL**: ```xml INSERT INTO psy_user_profile(...) VALUES (#{item.userId}, #{item.infoNumber}, ...) ``` **批量更新SQL**: ```xml UPDATE psy_user_profile SET ... WHERE profile_id = #{item.profileId} ``` #### 3. Service实现 - `PsyUserProfileServiceImpl.java` **优化前**: ```java for (PsyUserProfile profile : profileList) { // 3000次查询 PsyUserProfile existProfile = profileMapper.selectProfileByInfoNumber(...); if (existProfile == null) { this.insertProfile(profile); // 3000次插入 } else { this.updateProfile(profile); // 或3000次更新 } } ``` **优化后**: ```java // 1. 批量查询(1次) List existingProfiles = profileMapper.selectProfilesByInfoNumbers(infoNumbers); // 2. 分类处理 List toInsertList = ...; List toUpdateList = ...; // 3. 批量插入(6次,每批500条) profileMapper.batchInsertProfiles(toInsertList); // 4. 批量更新(6次,每批500条) profileMapper.batchUpdateProfiles(toUpdateList); ``` ### 性能提升对比 | 操作 | 优化前 | 优化后 | 提升 | |------|--------|--------|------| | 数据库查询 | 3000次 | 1次 | **3000倍** | | 数据库插入 | 3000次 | 6次(500条/批) | **500倍** | | 总操作数 | 6000次 | 7次 | **857倍** | | 导入时间 | 10-15分钟 | **10-30秒** | **20-50倍** | ### 预期效果 - ✅ **3000条数据**:从10-15分钟 → **10-30秒** - ✅ **10000条数据**:预计 **30秒-1分钟** - ✅ **数据库压力降低**:减少99%的数据库操作 - ✅ **用户体验提升**:导入响应更快 ### 容错机制 1. **批量失败回退**:如果批量插入失败,自动回退到逐条插入 ```java try { profileMapper.batchInsertProfiles(batch); } catch (Exception e) { // 批量失败,尝试逐条插入 for (PsyUserProfile profile : batch) { this.insertProfile(profile); } } ``` 2. **进度跟踪**:保留导入进度显示功能 3. **错误记录**:详细记录每条失败的数据 ### 数据库配置建议 为了支持批量更新,需要在数据库连接URL中添加: ```properties # application.yml 或 application.properties spring.datasource.url=jdbc:mysql://localhost:3306/xinli?allowMultiQueries=true ``` `allowMultiQueries=true` 允许在一个语句中执行多条SQL(用于批量更新)。 ### 测试验证 #### 测试步骤 1. 准备3000条测试数据的Excel文件 2. 进入"用户档案" → 点击"导入" 3. 选择Excel文件上传 4. 观察导入进度和时间 #### 预期结果 - ✅ 导入时间:10-30秒 - ✅ 进度显示:实时更新 - ✅ 成功率:100%(数据正确情况下) - ✅ 数据完整:所有字段正确导入 ## 部署说明 ### 前端部署 ```bash cd xinli-ui npm run build # 将dist目录部署到nginx ``` ### 后端部署 ```bash cd ry-xinli mvn clean package # 重启Spring Boot应用 java -jar ry-xinli-admin/target/ry-xinli.jar ``` ### 数据库配置 确保数据库连接URL包含 `allowMultiQueries=true`: ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/xinli?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&useSSL=false ``` ## 总结 ### 优化1:大模型API - ✅ 移除远程API依赖 - ✅ 改用本地Ollama - ✅ 降低成本,提升安全性 ### 优化2:用户导入 - ✅ 批量查询:3000次 → 1次 - ✅ 批量插入:3000次 → 6次 - ✅ 导入时间:10-15分钟 → 10-30秒 - ✅ 性能提升:**20-50倍** ### 技术要点 1. 批量处理替代逐条处理 2. 减少数据库往返次数 3. 使用MyBatis foreach批量操作 4. 分批处理避免内存溢出 5. 失败回退机制保证可靠性 ### 注意事项 1. 本地大模型需要单独部署Ollama 2. 数据库需要开启`allowMultiQueries` 3. 大批量数据建议分批导入(每批不超过1万条) 4. 导入前建议备份数据库 ## 附录 ### 常见问题 **Q1: 本地大模型响应慢怎么办?** A: - 使用更小的模型(如qwen2.5:3b) - 升级服务器硬件(增加内存/CPU) - 优化提示词,减少输出长度 **Q2: 批量导入失败怎么办?** A: - 查看错误日志 - 检查数据格式是否正确 - 确认数据库配置正确 - 系统会自动回退到逐条导入 **Q3: 能导入更多数据吗?** A: 可以,系统支持10万+数据导入,只是时间会更长。建议: - 1万条以内:一次性导入 - 1万-10万:分批导入 - 10万以上:后台定时任务导入 **Q4: 如何验证优化效果?** A: - 准备相同的测试数据 - 对比优化前后的导入时间 - 检查数据库慢查询日志 - 监控服务器资源使用情况