清理并优化项目
This commit is contained in:
parent
e7c1563f15
commit
2c277d0344
80
History.md
80
History.md
|
|
@ -1,80 +0,0 @@
|
|||
# 项目修改历史记录
|
||||
|
||||
## 2025-01-XX - 学员登录功能改造
|
||||
|
||||
### 修改内容
|
||||
|
||||
#### 1. 登录页面改造
|
||||
- **文件**: `ruoyi-ui/src/views/login.vue`
|
||||
- **修改说明**:
|
||||
- 将登录页面改为学员登录页面
|
||||
- 只需输入学员编号即可登录,无需密码和验证码
|
||||
- 添加醒目的"管理员登录"链接,链接到管理员登录页面
|
||||
- 登录成功后跳转到学员测试题列表页面
|
||||
|
||||
#### 2. 创建管理员登录页面
|
||||
- **文件**: `ruoyi-ui/src/views/admin-login.vue`
|
||||
- **修改说明**:
|
||||
- 创建独立的管理员登录页面
|
||||
- 保留原有的账号、密码、验证码登录方式
|
||||
- 添加"返回学员登录"链接
|
||||
|
||||
#### 3. 学员登录API
|
||||
- **文件**: `ruoyi-ui/src/api/login.js`
|
||||
- **修改说明**:
|
||||
- 新增 `studentLogin` 方法,用于学员登录
|
||||
- 接口地址: `/student/login`
|
||||
- 参数: `studentNo` (学员编号)
|
||||
- 添加判空处理,确保学员编号不为空
|
||||
|
||||
#### 4. Store用户模块扩展
|
||||
- **文件**: `ruoyi-ui/src/store/modules/user.js`
|
||||
- **修改说明**:
|
||||
- 新增 `StudentLogin` action,处理学员登录逻辑
|
||||
- 学员登录成功后设置token和学员角色
|
||||
- 添加判空处理,确保数据安全
|
||||
|
||||
#### 5. 学员测试题列表页面
|
||||
- **文件**: `ruoyi-ui/src/views/student/tests.vue`
|
||||
- **修改说明**:
|
||||
- 创建学员端心理测试题列表页面
|
||||
- 显示所有开放的测试题(量表和问卷)
|
||||
- 支持搜索功能
|
||||
- 卡片式展示,点击可开始测试
|
||||
- 支持量表和问卷两种类型的测试
|
||||
- 显示学员编号和退出登录功能
|
||||
|
||||
#### 6. 路由配置更新
|
||||
- **文件**: `ruoyi-ui/src/router/index.js`
|
||||
- **修改说明**:
|
||||
- 添加管理员登录路由: `/admin-login`
|
||||
- 添加学员测试题列表路由: `/student/tests`
|
||||
- 学员测试页面不使用Layout组件,作为独立页面
|
||||
|
||||
#### 7. 权限控制更新
|
||||
- **文件**: `ruoyi-ui/src/permission.js`
|
||||
- **修改说明**:
|
||||
- 将 `/admin-login` 添加到白名单
|
||||
- 处理学员登录后的路由跳转逻辑
|
||||
- 学员角色只能访问学员相关页面
|
||||
- 已登录用户访问登录页面时根据角色跳转
|
||||
|
||||
### 功能特点
|
||||
|
||||
1. **学员登录简化**: 只需输入学员编号即可登录,无需密码,提升用户体验
|
||||
2. **管理员登录分离**: 管理员和学员使用不同的登录入口,界面更清晰
|
||||
3. **测试题展示**: 学员登录后可以看到所有开放的心理测试题
|
||||
4. **权限控制**: 学员和管理员使用不同的角色权限,确保数据安全
|
||||
5. **响应式设计**: 测试题列表页面采用卡片式布局,支持响应式显示
|
||||
|
||||
### 技术要点
|
||||
|
||||
- 使用Vue.js + Element UI实现
|
||||
- 采用Vuex进行状态管理
|
||||
- 路由守卫控制页面访问权限
|
||||
- API接口统一管理
|
||||
- 代码添加详细注释,提高可维护性
|
||||
|
||||
### 开发者
|
||||
wanxiubin
|
||||
|
||||
152
TTS功能清理完成报告.md
152
TTS功能清理完成报告.md
|
|
@ -1,152 +0,0 @@
|
|||
# TTS 功能清理完成报告
|
||||
|
||||
## ✅ 清理完成
|
||||
|
||||
**清理时间**:2025-01-27
|
||||
**清理范围**:所有 TTS 相关代码、配置和文档
|
||||
|
||||
---
|
||||
|
||||
## 🗑️ 已删除的文件
|
||||
|
||||
### Java 代码文件
|
||||
|
||||
1. ✅ `ry-xinli-common/src/main/java/com/ddnai/common/utils/tts/PaddleTtsUtil.java`
|
||||
2. ✅ `ry-xinli-admin/src/main/java/com/ddnai/web/controller/common/TtsController.java`
|
||||
3. ✅ `ry-xinli-common/src/main/java/com/ddnai/common/utils/tts/` 目录
|
||||
|
||||
### Python 服务文件
|
||||
|
||||
1. ✅ `tts-service/` 整个目录(包含所有文件)
|
||||
- `app.py`
|
||||
- `requirements.txt`
|
||||
- `README.md`
|
||||
- `start.sh`
|
||||
- `start.bat`
|
||||
- `test_service.py`
|
||||
|
||||
### 配置文件
|
||||
|
||||
1. ✅ `ry-xinli-admin/src/main/resources/application.yml` - 已移除 TTS 配置节
|
||||
|
||||
### 依赖配置
|
||||
|
||||
1. ✅ `ry-xinli-admin/pom.xml` - 已移除 `paddle-tts-java` 依赖
|
||||
|
||||
### 测试脚本
|
||||
|
||||
1. ✅ `test-tts-integration.sh`
|
||||
2. ✅ `test-tts-integration.bat`
|
||||
3. ✅ `download-tts-sdk.ps1`
|
||||
|
||||
### 文档文件
|
||||
|
||||
1. ✅ `TTS功能快速开始.md`
|
||||
2. ✅ `TTS功能实施完整总结.md`
|
||||
3. ✅ `TTS功能实施完成总结.md`
|
||||
4. ✅ `TTS功能完整部署指南.md`
|
||||
5. ✅ `TTS服务部署架构说明.md`
|
||||
6. ✅ `TTS服务快速部署参考.md`
|
||||
7. ✅ `TTS功能部署检查清单.md`
|
||||
8. ✅ `TTS功能快速开始指南.md`
|
||||
9. ✅ `TTS功能实施说明.md`
|
||||
10. ✅ `TTS方案可行性分析报告.md`
|
||||
11. ✅ `TTS依赖验证结果报告-更新.md`
|
||||
12. ✅ `TTS依赖验证结果报告.md`
|
||||
13. ✅ `paddle-tts-java依赖验证最终结果.md`
|
||||
14. ✅ `paddle-tts-java下载链接和验证方法.md`
|
||||
15. ✅ `paddle-tts-java下载和验证指南.md`
|
||||
16. ✅ `Python-TTS服务实施方案.md`
|
||||
17. ✅ `README-TTS功能.md`
|
||||
18. ✅ `PaddleSpeech分析报告.md`
|
||||
|
||||
---
|
||||
|
||||
## ✅ 已恢复的文件
|
||||
|
||||
### 配置文件
|
||||
|
||||
1. ✅ `ry-xinli-admin/src/main/resources/application.yml`
|
||||
- 已移除 `tts:` 配置节
|
||||
- 恢复到原始状态
|
||||
|
||||
### 依赖配置
|
||||
|
||||
1. ✅ `ry-xinli-admin/pom.xml`
|
||||
- 已移除 `paddle-tts-java` 依赖
|
||||
- 恢复到原始状态
|
||||
|
||||
---
|
||||
|
||||
## 🔍 验证结果
|
||||
|
||||
### 代码检查
|
||||
|
||||
- ✅ 无 TTS 相关 Java 代码残留
|
||||
- ✅ 无 TTS 相关配置文件残留
|
||||
- ✅ 无 TTS 相关依赖残留
|
||||
|
||||
### 目录检查
|
||||
|
||||
- ✅ `tts-service/` 目录已删除
|
||||
- ✅ `ry-xinli-common/.../utils/tts/` 目录已删除
|
||||
|
||||
---
|
||||
|
||||
## 📋 清理清单
|
||||
|
||||
- [x] 删除 Java TTS 工具类
|
||||
- [x] 删除 TTS Controller
|
||||
- [x] 删除 Python TTS 服务目录
|
||||
- [x] 删除配置文件中的 TTS 配置
|
||||
- [x] 删除 pom.xml 中的 TTS 依赖
|
||||
- [x] 删除所有 TTS 相关文档
|
||||
- [x] 删除测试脚本
|
||||
- [x] 验证无残留代码
|
||||
|
||||
---
|
||||
|
||||
## ✨ 清理完成
|
||||
|
||||
所有 TTS 相关代码、配置和文档已完全清除。
|
||||
|
||||
项目已恢复到未集成 TTS 功能之前的状态。
|
||||
|
||||
---
|
||||
|
||||
## 🔍 最终验证
|
||||
|
||||
### 代码文件
|
||||
- ✅ `TtsController.java` - 已删除(未找到)
|
||||
- ✅ `PaddleTtsUtil.java` - 已删除(未找到)
|
||||
|
||||
### 配置文件
|
||||
- ✅ `application.yml` - TTS 配置已移除
|
||||
- ✅ `pom.xml` - TTS 依赖已移除
|
||||
|
||||
### 目录
|
||||
- ✅ `tts-service/` - 已删除
|
||||
- ⚠️ `ry-xinli-common/.../utils/tts/` - 空目录(如果存在可手动删除)
|
||||
|
||||
### 文档
|
||||
- ✅ 所有 TTS 相关文档已删除(18个文档文件)
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
如果 `ry-xinli-common/src/main/java/com/ddnai/common/utils/tts/` 空目录仍然存在,可以手动删除:
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
rmdir /s ry-xinli-common\src\main\java\com\ddnai\common\utils\tts
|
||||
|
||||
# Linux/Mac
|
||||
rm -rf ry-xinli-common/src/main/java/com/ddnai/common/utils/tts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**清理完成时间**:2025-01-27
|
||||
**清理状态**:✅ 完成
|
||||
|
||||
67
ry.bat
67
ry.bat
|
|
@ -1,67 +0,0 @@
|
|||
@echo off
|
||||
|
||||
rem jarƽ<72><C6BD>Ŀ¼
|
||||
set AppName=ry-xinli-admin.jar
|
||||
|
||||
rem JVM<56><4D><EFBFBD><EFBFBD>
|
||||
set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
|
||||
|
||||
|
||||
ECHO.
|
||||
ECHO. [1] <20><><EFBFBD><EFBFBD>%AppName%
|
||||
ECHO. [2] <20>ر<EFBFBD>%AppName%
|
||||
ECHO. [3] <20><><EFBFBD><EFBFBD>%AppName%
|
||||
ECHO. [4] <20><><EFBFBD><EFBFBD>״̬ %AppName%
|
||||
ECHO. [5] <20><> <20><>
|
||||
ECHO.
|
||||
|
||||
ECHO.<2E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѡ<EFBFBD><D1A1><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD><EFBFBD><EFBFBD>:
|
||||
set /p ID=
|
||||
IF "%id%"=="1" GOTO start
|
||||
IF "%id%"=="2" GOTO stop
|
||||
IF "%id%"=="3" GOTO restart
|
||||
IF "%id%"=="4" GOTO status
|
||||
IF "%id%"=="5" EXIT
|
||||
PAUSE
|
||||
:start
|
||||
for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
|
||||
set pid=%%a
|
||||
set image_name=%%b
|
||||
)
|
||||
if defined pid (
|
||||
echo %%is running
|
||||
PAUSE
|
||||
)
|
||||
|
||||
start javaw %JVM_OPTS% -jar %AppName%
|
||||
|
||||
echo starting<6E><67><EFBFBD><EFBFBD>
|
||||
echo Start %AppName% success...
|
||||
goto:eof
|
||||
|
||||
rem <20><><EFBFBD><EFBFBD>stopͨ<70><CDA8>jps<70><73><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>pid<69><64><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
:stop
|
||||
for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
|
||||
set pid=%%a
|
||||
set image_name=%%b
|
||||
)
|
||||
if not defined pid (echo process %AppName% does not exists) else (
|
||||
echo prepare to kill %image_name%
|
||||
echo start kill %pid% ...
|
||||
rem <20><><EFBFBD>ݽ<EFBFBD><DDBD><EFBFBD>ID<49><44>kill<6C><6C><EFBFBD><EFBFBD>
|
||||
taskkill /f /pid %pid%
|
||||
)
|
||||
goto:eof
|
||||
:restart
|
||||
call :stop
|
||||
call :start
|
||||
goto:eof
|
||||
:status
|
||||
for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
|
||||
set pid=%%a
|
||||
set image_name=%%b
|
||||
)
|
||||
if not defined pid (echo process %AppName% is dead ) else (
|
||||
echo %image_name% is running
|
||||
)
|
||||
goto:eof
|
||||
|
|
@ -129,7 +129,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { getUserAssessmentSummary, getStudentOptions } from '@/api/psychology/assessment'
|
||||
import { getUserAssessmentSummary, getStudentOptions, listAssessment } from '@/api/psychology/assessment'
|
||||
import { getProfileByUserId, listProfile } from '@/api/psychology/profile'
|
||||
import { getReport, listReport } from '@/api/psychology/report'
|
||||
import { parseTime } from '@/utils/ruoyi'
|
||||
|
|
@ -329,32 +329,63 @@ export default {
|
|||
}
|
||||
this.loading = true
|
||||
try {
|
||||
// 加载用户测评汇总
|
||||
// 直接加载该用户的所有测评记录(不使用汇总接口,避免被过滤)
|
||||
const assessmentResponse = await listAssessment({
|
||||
userId: this.selectedUserId,
|
||||
status: '1', // 只加载已完成的测评
|
||||
pageNum: 1,
|
||||
pageSize: 1000
|
||||
})
|
||||
|
||||
const assessments = assessmentResponse.rows || []
|
||||
|
||||
// 获取所有量表报告(包含reportId)
|
||||
const scaleReportsResponse = await listReport({
|
||||
userId: this.selectedUserId,
|
||||
sourceType: 'assessment',
|
||||
isGenerated: '1',
|
||||
pageNum: 1,
|
||||
pageSize: 1000
|
||||
})
|
||||
const reportMap = new Map()
|
||||
if (scaleReportsResponse.rows) {
|
||||
scaleReportsResponse.rows.forEach(report => {
|
||||
if (report.assessmentId) {
|
||||
reportMap.set(report.assessmentId, report)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 转换为报告列表格式
|
||||
const scaleReports = []
|
||||
for (const assessment of assessments) {
|
||||
// 检查是否有报告
|
||||
if (assessment.hasReport) {
|
||||
const report = reportMap.get(assessment.assessmentId)
|
||||
scaleReports.push({
|
||||
key: `${assessment.scaleId}-${assessment.assessmentId}`,
|
||||
scaleId: assessment.scaleId,
|
||||
scaleName: assessment.scaleName,
|
||||
assessmentId: assessment.assessmentId,
|
||||
reportId: report ? report.reportId : null,
|
||||
reportTitle: report ? report.reportTitle : `${assessment.scaleName}测评报告`,
|
||||
submitTime: assessment.submitTime || assessment.startTime,
|
||||
totalScore: assessment.totalScore,
|
||||
summary: report ? report.summary : '',
|
||||
status: assessment.status,
|
||||
sourceType: 'assessment'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户基本信息(用于显示)
|
||||
const summaryResponse = await getUserAssessmentSummary(this.selectedUserId)
|
||||
this.userSummary = summaryResponse.data || null
|
||||
|
||||
// 转换量表数据
|
||||
const rawScales = (this.userSummary && Array.isArray(this.userSummary.scales)) ? this.userSummary.scales : []
|
||||
const scaleReports = rawScales.reduce((result, scale) => {
|
||||
const attempts = Array.isArray(scale.attempts) ? scale.attempts : []
|
||||
const reportRows = attempts
|
||||
.filter((attempt) => attempt && attempt.reportId)
|
||||
.map((attempt) => ({
|
||||
key: `${scale.scaleId}-${attempt.assessmentId}`,
|
||||
scaleId: scale.scaleId,
|
||||
scaleName: scale.scaleName,
|
||||
assessmentId: attempt.assessmentId,
|
||||
reportId: attempt.reportId,
|
||||
reportTitle: attempt.reportTitle || `${scale.scaleName}测评报告`,
|
||||
submitTime: attempt.submitTime || attempt.startTime,
|
||||
totalScore: attempt.totalScore,
|
||||
summary: attempt.reportSummary || '',
|
||||
status: attempt.status,
|
||||
sourceType: 'assessment'
|
||||
}))
|
||||
return result.concat(reportRows)
|
||||
}, [])
|
||||
|
||||
// 加载问卷报告
|
||||
const questionnaireReports = await this.loadQuestionnaireReports(this.selectedUserId)
|
||||
|
||||
// 合并量表和问卷报告
|
||||
const combinedReports = [...scaleReports, ...questionnaireReports].sort((a, b) => {
|
||||
const timeA = a.submitTime ? new Date(a.submitTime).getTime() : 0
|
||||
const timeB = b.submitTime ? new Date(b.submitTime).getTime() : 0
|
||||
|
|
|
|||
139
主观题评分功能使用说明.md
139
主观题评分功能使用说明.md
|
|
@ -1,139 +0,0 @@
|
|||
# 主观题评分功能使用说明
|
||||
|
||||
## 功能说明
|
||||
|
||||
主观题评分功能允许管理员对问卷中的主观题(简答题、问答题、作文题)进行手动评分。
|
||||
|
||||
## 添加菜单方法
|
||||
|
||||
### 方法一:执行SQL脚本(推荐)
|
||||
|
||||
1. 打开数据库管理工具(如Navicat、MySQL Workbench等)
|
||||
2. 连接到数据库
|
||||
3. 执行 `sql/添加主观题评分菜单.sql` 文件中的SQL语句
|
||||
4. 刷新浏览器页面,菜单应该会出现在"心理测评管理"下
|
||||
|
||||
### 方法二:通过菜单管理页面手动添加
|
||||
|
||||
1. 登录系统,进入 **系统管理** -> **菜单管理**
|
||||
2. 找到 **心理测评管理**(菜单ID:2009)
|
||||
3. 点击 **新增** 按钮
|
||||
4. 填写以下信息:
|
||||
- **菜单名称**:主观题评分
|
||||
- **父菜单**:心理测评管理
|
||||
- **显示顺序**:13
|
||||
- **路由地址**:questionnaire/scoring
|
||||
- **组件路径**:psychology/questionnaire/scoring
|
||||
- **路由名称**:QuestionnaireScoring
|
||||
- **菜单类型**:菜单
|
||||
- **菜单图标**:edit
|
||||
- **权限标识**:psychology:questionnaire:score
|
||||
- **是否显示**:显示
|
||||
- **状态**:正常
|
||||
5. 点击 **确定** 保存
|
||||
|
||||
6. 添加按钮权限(可选):
|
||||
- 在菜单管理中找到刚创建的"主观题评分"菜单
|
||||
- 点击 **新增** 添加按钮权限:
|
||||
- **菜单名称**:评分查询
|
||||
- **权限标识**:psychology:questionnaire:score:query
|
||||
- **菜单类型**:按钮
|
||||
|
||||
- 再添加一个:
|
||||
- **菜单名称**:评分操作
|
||||
- **权限标识**:psychology:questionnaire:score
|
||||
- **菜单类型**:按钮
|
||||
|
||||
7. 分配菜单权限:
|
||||
- 进入 **系统管理** -> **角色管理**
|
||||
- 找到 **管理员** 角色,点击 **修改**
|
||||
- 在 **菜单权限** 中勾选 **主观题评分** 及其按钮权限
|
||||
- 点击 **确定** 保存
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 访问主观题评分页面
|
||||
|
||||
- 方法一:在左侧菜单中找到 **心理测评管理** -> **主观题评分**
|
||||
- 方法二:直接访问URL:`/psychology/questionnaire/scoring`
|
||||
|
||||
### 2. 查看待评分题目
|
||||
|
||||
页面会自动显示所有待评分的主观题,包括:
|
||||
- 问卷名称
|
||||
- 答题人
|
||||
- 题目序号和内容
|
||||
- 题目类型(简答题/问答题/作文题)
|
||||
- 题目分值
|
||||
- 答案内容
|
||||
- 提交时间
|
||||
|
||||
### 3. 单个评分
|
||||
|
||||
1. 在列表中点击 **评分** 按钮
|
||||
2. 在弹出的对话框中:
|
||||
- 查看题目内容和答案内容
|
||||
- 输入得分(不能超过题目分值)
|
||||
- 可选:输入评语
|
||||
3. 点击 **确定** 保存评分
|
||||
|
||||
### 4. 批量评分
|
||||
|
||||
1. 在列表中勾选多个待评分的题目
|
||||
2. 点击 **批量评分** 按钮
|
||||
3. 在批量评分对话框中为每个题目输入得分和评语
|
||||
4. 点击 **确定** 批量保存
|
||||
|
||||
### 5. 搜索和筛选
|
||||
|
||||
- 可以通过 **问卷名称** 搜索
|
||||
- 可以通过 **答题人** 搜索
|
||||
|
||||
## 功能特点
|
||||
|
||||
1. **自动识别主观题**:系统自动识别简答题(text)、问答题(textarea)、作文题(essay)三种类型
|
||||
2. **分数验证**:评分不能超过题目分值,系统会自动限制
|
||||
3. **自动更新总分**:评分后自动重新计算答题记录的总分
|
||||
4. **自动更新排名**:评分后自动重新计算该问卷的排名
|
||||
5. **批量评分**:支持同时为多个题目评分,提高效率
|
||||
6. **评语功能**:可以为每个答案添加评语(可选)
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 只有已提交的问卷中的主观题才会出现在待评分列表中
|
||||
2. 已经评分的题目不会出现在待评分列表中
|
||||
3. 评分后,用户查看报告时会看到更新后的成绩
|
||||
4. 如果评分失败,请检查:
|
||||
- 是否有评分权限(`psychology:questionnaire:score`)
|
||||
- 题目是否为主观题且未评分
|
||||
- 得分是否超过题目分值
|
||||
|
||||
## 权限配置
|
||||
|
||||
需要的权限标识:
|
||||
- `psychology:questionnaire:score` - 评分操作权限
|
||||
- `psychology:questionnaire:score:query` - 查询权限(可选)
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 菜单中没有显示"主观题评分"?
|
||||
A: 需要执行SQL脚本或通过菜单管理页面手动添加菜单,并确保已分配菜单权限给当前角色。
|
||||
|
||||
### Q: 点击菜单后页面空白?
|
||||
A: 检查:
|
||||
1. 路由配置是否正确(`ruoyi-ui/src/router/index.js`)
|
||||
2. 页面文件是否存在(`ruoyi-ui/src/views/psychology/questionnaire/scoring.vue`)
|
||||
3. 浏览器控制台是否有错误信息
|
||||
|
||||
### Q: 没有待评分的数据?
|
||||
A: 检查:
|
||||
1. 是否有已提交的问卷答题记录
|
||||
2. 问卷中是否包含主观题(text、textarea、essay类型)
|
||||
3. 主观题是否已经被评分过(已评分的不会显示)
|
||||
|
||||
### Q: 评分后总分没有更新?
|
||||
A: 系统会自动更新,如果未更新,请:
|
||||
1. 刷新页面查看
|
||||
2. 检查后端日志是否有错误
|
||||
3. 确认评分是否成功(查看操作日志)
|
||||
|
||||
142
二维码功能修复说明-最终版.md
142
二维码功能修复说明-最终版.md
|
|
@ -1,142 +0,0 @@
|
|||
# 二维码功能修复说明(最终版)
|
||||
|
||||
## 修复时间
|
||||
2025年1月
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 一、删除快速生成功能
|
||||
|
||||
**修改内容**:
|
||||
1. 删除前端"快速生成"按钮和相关方法
|
||||
2. 删除后端快速生成接口(`/quick/register` 和 `/quick/login`)
|
||||
3. 删除前端API调用方法
|
||||
|
||||
**说明**:用户可以通过"新增"按钮手动创建注册和登录二维码。
|
||||
|
||||
### 二、修复本地环境二维码URL生成问题
|
||||
|
||||
**问题**:扫描二维码后显示的地址是 `localhost:30081`,这会导致手机无法访问。
|
||||
|
||||
**原因分析**:
|
||||
- 本地环境:前端运行在80端口,后端运行在30081端口
|
||||
- 二维码URL应该使用前端地址(`localhost`,80端口),而不是后端地址(`localhost:30081`)
|
||||
- 之前的逻辑在某些情况下仍然会生成包含30081端口的URL
|
||||
|
||||
**修复方案**:
|
||||
1. 优化 `buildServerAddress()` 方法,确保在本地环境下正确识别前端地址
|
||||
2. 如果检测到后端端口(30081),自动改为前端端口(80,不显示端口号)
|
||||
3. 优先从 `Referer` 头获取前端地址,如果失败则使用请求的服务器地址并转换端口
|
||||
|
||||
**修复后的URL格式**:
|
||||
- 本地环境:`http://localhost/psychology/qrcode/scan/xxx`(不包含端口号,默认80端口)
|
||||
- 生产环境:`http://1.15.149.240/psychology/qrcode/scan/xxx`(不包含端口号,默认80端口)
|
||||
|
||||
### 三、二维码功能实现方式说明
|
||||
|
||||
**当前实现方式**:
|
||||
1. **手动创建二维码**:通过"新增"按钮创建二维码,选择类型(测评、查看报告、注册、登录)
|
||||
2. **二维码生成**:系统自动生成二维码编码和Base64图片
|
||||
3. **扫码跳转**:扫描二维码后自动跳转到对应页面
|
||||
|
||||
**本地环境使用建议**:
|
||||
1. **使用本机IP地址**:如果需要在手机上扫码,建议:
|
||||
- 获取本机IP地址(如:192.168.1.100)
|
||||
- 在浏览器中通过 `http://192.168.1.100` 访问前端页面
|
||||
- 这样生成的二维码URL会是 `http://192.168.1.100/psychology/qrcode/scan/xxx`
|
||||
- 手机和电脑在同一局域网内,可以正常扫码访问
|
||||
|
||||
2. **使用localhost**:如果只在电脑上测试:
|
||||
- 使用 `http://localhost` 访问前端页面
|
||||
- 生成的二维码URL会是 `http://localhost/psychology/qrcode/scan/xxx`
|
||||
- 只能在电脑上访问,手机无法访问
|
||||
|
||||
**新的实现方式建议**(可选):
|
||||
如果需要更好的本地开发体验,可以考虑:
|
||||
1. **配置前端地址**:在 `application.yml` 中添加前端地址配置
|
||||
2. **使用环境变量**:通过环境变量区分开发环境和生产环境
|
||||
3. **使用配置文件**:在配置文件中明确指定前端和后端地址
|
||||
|
||||
## 测试步骤
|
||||
|
||||
### 1. 重启后端服务
|
||||
**重要**:修改代码后必须重启后端服务才能生效。
|
||||
|
||||
### 2. 测试二维码生成
|
||||
1. 登录管理系统
|
||||
2. 进入"二维码管理"页面
|
||||
3. 点击"新增"按钮
|
||||
4. 选择二维码类型(如:注册、登录、测评、查看报告)
|
||||
5. 填写相关信息
|
||||
6. 保存后应成功生成二维码
|
||||
|
||||
### 3. 测试二维码扫码功能
|
||||
1. 使用手机扫描生成的二维码
|
||||
2. 应能正常打开扫码页面
|
||||
3. 根据二维码类型自动跳转到对应页面:
|
||||
- 注册二维码 → 跳转到注册页面
|
||||
- 登录二维码 → 跳转到登录页面
|
||||
- 测评二维码 → 跳转到测评开始页面
|
||||
- 报告二维码 → 跳转到报告详情页面
|
||||
|
||||
### 4. 检查二维码URL
|
||||
生成的二维码URL应该:
|
||||
- **不包含30081端口**:应该是 `http://localhost/...` 或 `http://192.168.1.100/...`
|
||||
- **使用前端地址**:指向前端页面(80端口),而不是后端API(30081端口)
|
||||
- **手机可以访问**:如果使用本机IP地址,手机在同一局域网内可以正常访问
|
||||
|
||||
## 关键代码位置
|
||||
|
||||
### 后端
|
||||
- **Controller**:`ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQrcodeController.java`
|
||||
- `buildServerAddress()` - 构建服务器地址(已修复,确保使用前端地址)
|
||||
- `enrichQrcodeWithBase64()` - 为二维码添加Base64图片
|
||||
- `scan()` - 扫码接口
|
||||
- `buildRedirectUrl()` - 构建跳转地址
|
||||
|
||||
- **Service**:`ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyQrcodeServiceImpl.java`
|
||||
- `generateQrcode()` - 生成二维码Base64
|
||||
- `buildQrcodeContent()` - 构建二维码内容URL
|
||||
|
||||
### 前端
|
||||
- **页面**:
|
||||
- `xinli-ui/src/views/psychology/qrcode/index.vue` - 二维码管理页面(已删除快速生成功能)
|
||||
- `xinli-ui/src/views/psychology/qrcode/scan.vue` - 扫码页面
|
||||
|
||||
- **API**:`xinli-ui/src/api/psychology/qrcode.js`(已删除快速生成相关方法)
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **必须重启后端服务**:修改代码后必须重启后端服务才能生效
|
||||
2. **本地环境测试**:
|
||||
- 如果需要在手机上扫码,使用本机IP地址访问前端页面
|
||||
- 如果只在电脑上测试,使用localhost即可
|
||||
3. **端口配置**:前端运行在80端口,后端运行在30081端口
|
||||
4. **二维码URL**:生成的二维码URL会自动使用前端地址(80端口),不包含30081端口
|
||||
|
||||
## 如果仍有问题
|
||||
|
||||
1. **检查二维码URL**:
|
||||
- 查看生成的二维码URL是否包含30081端口
|
||||
- 如果包含,说明 `buildServerAddress()` 方法没有正确工作
|
||||
- 检查后端日志,查看实际生成的URL
|
||||
|
||||
2. **检查Referer头**:
|
||||
- 在浏览器开发者工具的Network面板中查看请求的Referer头
|
||||
- 确保Referer头包含正确的前端地址
|
||||
|
||||
3. **本地环境测试**:
|
||||
- 如果使用localhost,只能在电脑上访问
|
||||
- 如果需要在手机上扫码,使用本机IP地址(如:192.168.1.100)
|
||||
|
||||
4. **检查网络**:
|
||||
- 确保手机和电脑在同一局域网内
|
||||
- 确保防火墙没有阻止访问
|
||||
|
||||
## 修复完成
|
||||
|
||||
所有问题已修复完成:
|
||||
- ✅ 删除快速生成功能
|
||||
- ✅ 修复本地环境二维码URL生成(确保使用前端地址,不包含30081端口)
|
||||
- ✅ 完善扫码跳转功能
|
||||
|
||||
136
二维码功能修复说明.md
136
二维码功能修复说明.md
|
|
@ -1,136 +0,0 @@
|
|||
# 二维码功能修复说明
|
||||
|
||||
## 修复时间
|
||||
2025年1月
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 一、修复快速生成接口404问题
|
||||
|
||||
**问题**:快速生成注册和登录二维码时,前端返回404错误。
|
||||
|
||||
**原因分析**:
|
||||
1. Spring Boot路径映射可能存在冲突
|
||||
2. 路径变量 `/{qrcodeId}` 可能优先匹配 `/generate/register` 和 `/generate/login`
|
||||
|
||||
**修复方案**:
|
||||
1. 将 `/generate/register` 和 `/generate/login` 方法移到其他 `/generate/*` 方法附近,保持一致性
|
||||
2. 在 `@PostMapping` 注解中添加 `produces = "application/json;charset=UTF-8"`,明确指定响应类型
|
||||
3. 确保 `/{qrcodeId}` 路径变量方法放在所有具体路径之后
|
||||
|
||||
**修复后的接口路径**:
|
||||
- `POST /psychology/qrcode/generate/register` - 快速生成注册二维码
|
||||
- `POST /psychology/qrcode/generate/login` - 快速生成登录二维码
|
||||
|
||||
### 二、修复二维码URL生成问题
|
||||
|
||||
**问题**:生成的二维码URL使用后端地址和端口(如 `http://1.15.149.240:30081`),但前端页面运行在80端口,导致手机扫码无法访问。
|
||||
|
||||
**修复方案**:
|
||||
1. 优化 `buildServerAddress()` 方法,优先从 `Referer` 头提取前端地址
|
||||
2. 如果检测到后端端口(30081),自动改为前端端口(80,不显示端口号)
|
||||
3. 生成的二维码URL现在指向前端页面,而不是后端API
|
||||
|
||||
**修复后的URL格式**:
|
||||
- 本地开发:`http://localhost/psychology/qrcode/scan/xxx`
|
||||
- 生产环境:`http://1.15.149.240/psychology/qrcode/scan/xxx`
|
||||
|
||||
### 三、完善扫码跳转逻辑
|
||||
|
||||
**功能说明**:
|
||||
1. **扫码接口**:`GET /psychology/qrcode/scan/{qrcodeCode}`(公开接口,使用 `@Anonymous` 注解)
|
||||
2. **跳转地址映射**:
|
||||
- `test`(测评)→ `/psychology/assessment/start?scaleId={id}` 或 `/psychology/questionnaire/start?questionnaireId={id}`
|
||||
- `view_report`(查看报告)→ `/psychology/report/detail?reportId={id}` 或 `/psychology/report/detail?assessmentId={id}`
|
||||
- `register`(注册)→ `/register`
|
||||
- `login`(登录)→ `/login`
|
||||
|
||||
3. **前端扫码页面**(`scan.vue`):
|
||||
- 自动解析URL和查询参数
|
||||
- 使用Vue Router进行内部跳转
|
||||
- 支持完整URL的外部跳转
|
||||
- 提供错误提示和重试机制
|
||||
|
||||
## 测试步骤
|
||||
|
||||
### 1. 重启后端服务
|
||||
**重要**:修改代码后必须重启后端服务才能生效。
|
||||
|
||||
### 2. 测试快速生成功能
|
||||
1. 登录管理系统
|
||||
2. 进入"二维码管理"页面
|
||||
3. 点击"快速生成" → "注册二维码"
|
||||
4. 应成功生成二维码并显示预览
|
||||
5. 点击"快速生成" → "登录二维码"
|
||||
6. 应成功生成二维码并显示预览
|
||||
|
||||
### 3. 测试二维码扫码功能
|
||||
1. 使用手机扫描生成的二维码
|
||||
2. 应能正常打开扫码页面(`/psychology/qrcode/scan/{qrcodeCode}`)
|
||||
3. 根据二维码类型自动跳转到对应页面:
|
||||
- 注册二维码 → 跳转到注册页面
|
||||
- 登录二维码 → 跳转到登录页面
|
||||
- 测评二维码 → 跳转到测评开始页面
|
||||
- 报告二维码 → 跳转到报告详情页面
|
||||
|
||||
### 4. 检查二维码URL
|
||||
生成的二维码URL应该:
|
||||
- 使用前端地址(80端口),而不是后端地址(30081端口)
|
||||
- 格式为:`http://{host}/psychology/qrcode/scan/{qrcodeCode}`
|
||||
- 手机可以正常访问
|
||||
|
||||
## 关键代码位置
|
||||
|
||||
### 后端
|
||||
- **Controller**:`ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQrcodeController.java`
|
||||
- `generateRegisterQrcode()` - 快速生成注册二维码
|
||||
- `generateLoginQrcode()` - 快速生成登录二维码
|
||||
- `scan()` - 扫码接口
|
||||
- `buildServerAddress()` - 构建服务器地址
|
||||
- `buildRedirectUrl()` - 构建跳转地址
|
||||
|
||||
- **Service**:`ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyQrcodeServiceImpl.java`
|
||||
- `generateQrcode()` - 生成二维码Base64
|
||||
- `buildQrcodeContent()` - 构建二维码内容URL
|
||||
|
||||
### 前端
|
||||
- **API**:`xinli-ui/src/api/psychology/qrcode.js`
|
||||
- `generateRegisterQrcode()` - 调用快速生成注册二维码接口
|
||||
- `generateLoginQrcode()` - 调用快速生成登录二维码接口
|
||||
- `scanQrcode()` - 调用扫码接口
|
||||
|
||||
- **页面**:
|
||||
- `xinli-ui/src/views/psychology/qrcode/index.vue` - 二维码管理页面
|
||||
- `xinli-ui/src/views/psychology/qrcode/scan.vue` - 扫码页面
|
||||
|
||||
- **路由**:`xinli-ui/src/router/index.js`
|
||||
- `/psychology/qrcode/scan/:qrcodeCode` - 扫码页面路由
|
||||
- `/register` - 注册页面路由
|
||||
- `/login` - 登录页面路由
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **必须重启后端服务**:修改代码后必须重启后端服务才能生效
|
||||
2. **权限检查**:快速生成接口需要 `psychology:qrcode:add` 权限
|
||||
3. **扫码接口**:扫码接口使用 `@Anonymous` 注解,不需要登录即可访问
|
||||
4. **URL生成**:二维码URL会根据请求的 `Referer` 头自动识别前端地址
|
||||
5. **端口配置**:前端运行在80端口,后端运行在30081端口
|
||||
|
||||
## 如果仍有问题
|
||||
|
||||
1. **检查后端日志**:查看是否有路径映射错误或权限错误
|
||||
2. **检查浏览器控制台**:查看实际请求的URL和错误信息
|
||||
3. **检查网络请求**:在浏览器开发者工具的Network面板中查看请求详情
|
||||
4. **确认服务状态**:确保后端服务已正确启动并运行在30081端口
|
||||
5. **确认权限**:确保当前用户有 `psychology:qrcode:add` 权限
|
||||
|
||||
## 修复完成
|
||||
|
||||
所有二维码相关功能已修复完成,包括:
|
||||
- ✅ 快速生成注册二维码
|
||||
- ✅ 快速生成登录二维码
|
||||
- ✅ 二维码URL生成(指向前端页面)
|
||||
- ✅ 扫码跳转功能
|
||||
- ✅ 测评二维码扫码
|
||||
- ✅ 报告二维码扫码
|
||||
|
||||
186
二维码功能完善说明.md
186
二维码功能完善说明.md
|
|
@ -1,186 +0,0 @@
|
|||
# 二维码功能完善说明
|
||||
|
||||
## 完成时间
|
||||
2025年1月
|
||||
|
||||
## 完善内容
|
||||
|
||||
### 一、后端功能增强
|
||||
|
||||
#### 1. 新增快速生成接口
|
||||
- ✅ **生成注册二维码接口**: `POST /psychology/qrcode/generate/register`
|
||||
- ✅ **生成登录二维码接口**: `POST /psychology/qrcode/generate/login`
|
||||
|
||||
#### 2. 优化扫码接口
|
||||
- ✅ **增强跳转URL构建**: 添加 `buildFullRedirectUrl()` 方法,自动构建完整的跳转URL(包含协议、域名、端口)
|
||||
- ✅ **返回完整URL**: 扫码接口现在返回 `redirectUrl`(相对路径)和 `fullRedirectUrl`(完整URL)
|
||||
|
||||
#### 3. 跳转地址映射
|
||||
已完善所有二维码类型的跳转地址:
|
||||
|
||||
| 二维码类型 | 目标类型 | 跳转地址 |
|
||||
|----------|---------|---------|
|
||||
| test(测评) | scale(量表) | `/psychology/assessment/start?scaleId={id}` |
|
||||
| test(测评) | questionnaire(问卷) | `/psychology/questionnaire/start?questionnaireId={id}` |
|
||||
| test(测评) | 其他 | `/psychology/assessment/start` |
|
||||
| view_report(查看报告) | report(报告) | `/psychology/report/detail?reportId={id}` |
|
||||
| view_report(查看报告) | assessment(测评) | `/psychology/report/detail?assessmentId={id}` |
|
||||
| view_report(查看报告) | 其他 | `/psychology/report` |
|
||||
| register(注册) | - | `/register` |
|
||||
| login(登录) | - | `/login` |
|
||||
|
||||
---
|
||||
|
||||
### 二、前端功能增强
|
||||
|
||||
#### 1. 扫码页面优化 (`scan.vue`)
|
||||
- ✅ **智能跳转**: 支持相对路径和完整URL的自动识别和跳转
|
||||
- ✅ **用户提示**: 根据二维码类型显示不同的跳转提示信息
|
||||
- ✅ **重试机制**: 如果自动跳转失败,提供手动重试链接
|
||||
- ✅ **错误处理**: 完善的错误提示和异常处理
|
||||
|
||||
#### 2. 二维码管理页面增强 (`index.vue`)
|
||||
- ✅ **快速生成按钮**: 添加"快速生成"下拉按钮
|
||||
- 注册二维码:一键生成注册二维码
|
||||
- 登录二维码:一键生成登录二维码
|
||||
- ✅ **自动预览**: 生成二维码后自动显示预览对话框
|
||||
- ✅ **用户体验优化**: 生成成功后自动刷新列表并显示二维码
|
||||
|
||||
#### 3. API接口完善 (`qrcode.js`)
|
||||
- ✅ 添加 `generateRegisterQrcode()` - 生成注册二维码
|
||||
- ✅ 添加 `generateLoginQrcode()` - 生成登录二维码
|
||||
|
||||
---
|
||||
|
||||
### 三、功能使用说明
|
||||
|
||||
#### 1. 生成二维码
|
||||
|
||||
**方式一:快速生成(推荐)**
|
||||
1. 进入"心理测评" -> "二维码管理"
|
||||
2. 点击"快速生成"下拉按钮
|
||||
3. 选择"注册二维码"或"登录二维码"
|
||||
4. 确认后自动生成并显示二维码
|
||||
|
||||
**方式二:手动创建**
|
||||
1. 点击"新增"按钮
|
||||
2. 选择二维码类型(测评/查看报告/注册/登录)
|
||||
3. 根据类型选择目标(如量表、报告等)
|
||||
4. 保存后生成二维码
|
||||
|
||||
#### 2. 扫码使用流程
|
||||
|
||||
**扫码测试流程**:
|
||||
1. 管理员生成量表测评二维码
|
||||
2. 用户使用手机扫描二维码
|
||||
3. 自动跳转到测评开始页面
|
||||
4. 用户填写信息并开始测评
|
||||
|
||||
**扫码查看报告流程**:
|
||||
1. 管理员生成报告查看二维码
|
||||
2. 用户使用手机扫描二维码
|
||||
3. 自动跳转到报告详情页面
|
||||
4. 用户查看测评报告
|
||||
|
||||
**扫码注册流程**:
|
||||
1. 管理员生成注册二维码
|
||||
2. 新用户使用手机扫描二维码
|
||||
3. 自动跳转到注册页面
|
||||
4. 用户完成注册
|
||||
|
||||
**扫码登录流程**:
|
||||
1. 管理员生成登录二维码
|
||||
2. 用户使用手机扫描二维码
|
||||
3. 自动跳转到登录页面
|
||||
4. 用户完成登录
|
||||
|
||||
---
|
||||
|
||||
### 四、技术实现细节
|
||||
|
||||
#### 1. 二维码URL构建
|
||||
- **二维码内容**: `http://{serverAddress}:{port}{contextPath}/psychology/qrcode/scan/{qrcodeCode}`
|
||||
- **扫码后处理**: 后端验证二维码有效性,返回跳转地址
|
||||
- **前端跳转**: 根据返回的URL自动跳转到目标页面
|
||||
|
||||
#### 2. 扫码统计
|
||||
- 每次扫码自动增加扫码次数
|
||||
- 可在二维码管理页面查看扫码统计
|
||||
|
||||
#### 3. 二维码有效期
|
||||
- 支持设置过期时间
|
||||
- 过期后扫码会提示"二维码已过期"
|
||||
|
||||
---
|
||||
|
||||
### 五、文件清单
|
||||
|
||||
#### 后端文件
|
||||
- `ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQrcodeController.java`
|
||||
- 新增 `generateRegisterQrcode()` 方法
|
||||
- 新增 `generateLoginQrcode()` 方法
|
||||
- 新增 `buildFullRedirectUrl()` 方法
|
||||
- 优化 `scan()` 方法,返回完整URL
|
||||
|
||||
#### 前端文件
|
||||
- `xinli-ui/src/views/psychology/qrcode/scan.vue`
|
||||
- 优化跳转逻辑
|
||||
- 添加重试机制
|
||||
- 优化用户提示
|
||||
|
||||
- `xinli-ui/src/views/psychology/qrcode/index.vue`
|
||||
- 添加快速生成按钮
|
||||
- 添加生成注册/登录二维码方法
|
||||
|
||||
- `xinli-ui/src/api/psychology/qrcode.js`
|
||||
- 添加 `generateRegisterQrcode()` API
|
||||
- 添加 `generateLoginQrcode()` API
|
||||
|
||||
---
|
||||
|
||||
### 六、测试建议
|
||||
|
||||
#### 1. 功能测试
|
||||
- [ ] 测试生成注册二维码并扫码跳转
|
||||
- [ ] 测试生成登录二维码并扫码跳转
|
||||
- [ ] 测试生成测评二维码并扫码跳转
|
||||
- [ ] 测试生成报告查看二维码并扫码跳转
|
||||
- [ ] 测试二维码过期功能
|
||||
- [ ] 测试扫码次数统计
|
||||
|
||||
#### 2. 兼容性测试
|
||||
- [ ] 测试不同手机浏览器的扫码功能
|
||||
- [ ] 测试微信扫码
|
||||
- [ ] 测试支付宝扫码
|
||||
- [ ] 测试其他扫码APP
|
||||
|
||||
#### 3. 性能测试
|
||||
- [ ] 测试大量并发扫码
|
||||
- [ ] 测试二维码生成速度
|
||||
|
||||
---
|
||||
|
||||
### 七、注意事项
|
||||
|
||||
1. **服务器地址配置**: 二维码URL使用配置的服务器地址,如果部署到不同环境,需要修改 `application.yml` 中的 `server.address` 配置
|
||||
|
||||
2. **HTTPS支持**: 当前默认使用HTTP,如果需要HTTPS,需要修改 `PsyQrcodeServiceImpl.java` 中的协议设置
|
||||
|
||||
3. **移动端适配**: 确保所有跳转页面在移动端显示正常
|
||||
|
||||
4. **二维码有效期**: 建议为不同类型的二维码设置合理的有效期
|
||||
|
||||
---
|
||||
|
||||
### 八、后续优化建议
|
||||
|
||||
1. **短链接功能**: 如果二维码URL过长,可以考虑集成短链接服务
|
||||
2. **二维码美化**: 可以添加Logo、颜色等美化功能
|
||||
3. **批量生成**: 支持批量生成二维码
|
||||
4. **二维码统计报表**: 添加详细的扫码统计分析
|
||||
5. **动态二维码**: 支持动态更新二维码内容
|
||||
|
||||
---
|
||||
|
||||
**完成状态**: ✅ 所有功能已完成并测试通过
|
||||
|
||||
229
使用指南-总览.md
229
使用指南-总览.md
|
|
@ -1,229 +0,0 @@
|
|||
# AI心理健康测评系统 - 使用指南总览
|
||||
|
||||
## 📚 文档导航
|
||||
|
||||
本系统为不同身份的用户提供了详细的使用指南,请根据您的身份选择对应的指南文档。
|
||||
|
||||
---
|
||||
|
||||
## 👥 用户身份说明
|
||||
|
||||
### 1. 系统管理员
|
||||
|
||||
**适用对象**:
|
||||
- 系统管理员
|
||||
- 拥有完整系统管理权限的用户
|
||||
|
||||
**主要职责**:
|
||||
- 管理量表、用户、权限
|
||||
- 配置预警规则
|
||||
- 查看和管理测评报告
|
||||
- 管理二维码
|
||||
- 系统设置和维护
|
||||
|
||||
**对应指南**:👉 [系统管理员使用指南](./使用指南-系统管理员.md)
|
||||
|
||||
---
|
||||
|
||||
### 2. 普通用户/测评者
|
||||
|
||||
**适用对象**:
|
||||
- 已获得量表权限的注册用户
|
||||
- 需要进行心理测评的用户
|
||||
- 查看测评报告的用户
|
||||
|
||||
**主要功能**:
|
||||
- 进行心理量表测评
|
||||
- 查看测评报告
|
||||
- 查看历史记录
|
||||
- 使用二维码扫描
|
||||
- 管理个人档案
|
||||
|
||||
**对应指南**:👉 [普通用户使用指南](./使用指南-普通用户.md)
|
||||
|
||||
---
|
||||
|
||||
### 3. 注册用户
|
||||
|
||||
**适用对象**:
|
||||
- 新注册的用户
|
||||
- 等待权限分配的用户
|
||||
|
||||
**主要流程**:
|
||||
- 注册账号
|
||||
- 首次登录
|
||||
- 等待权限分配
|
||||
- 开始使用系统
|
||||
|
||||
**对应指南**:👉 [注册用户使用指南](./使用指南-注册用户.md)
|
||||
|
||||
---
|
||||
|
||||
## 📖 快速开始
|
||||
|
||||
### 我是系统管理员
|
||||
|
||||
1. 使用默认账号登录(用户名:`admin`,密码:`admin123`)
|
||||
2. 阅读 [系统管理员使用指南](./使用指南-系统管理员.md)
|
||||
3. 开始配置系统和管理用户
|
||||
|
||||
### 我是新用户
|
||||
|
||||
1. 在登录页面点击"注册"
|
||||
2. 填写注册信息完成注册
|
||||
3. 阅读 [注册用户使用指南](./使用指南-注册用户.md)
|
||||
4. 等待管理员分配权限
|
||||
5. 获得权限后,阅读 [普通用户使用指南](./使用指南-普通用户.md)
|
||||
6. 开始进行测评
|
||||
|
||||
### 我是已有权限的用户
|
||||
|
||||
1. 使用账号登录系统
|
||||
2. 阅读 [普通用户使用指南](./使用指南-普通用户.md)
|
||||
3. 开始进行测评
|
||||
|
||||
---
|
||||
|
||||
## 🎯 功能模块说明
|
||||
|
||||
### 量表管理
|
||||
- **管理员**:创建、导入、配置量表
|
||||
- **普通用户**:选择量表进行测评
|
||||
|
||||
### 测评管理
|
||||
- **管理员**:查看和管理所有测评记录
|
||||
- **普通用户**:查看自己的测评记录
|
||||
|
||||
### 报告管理
|
||||
- **管理员**:查看、编辑、导出所有报告
|
||||
- **普通用户**:查看自己的测评报告
|
||||
|
||||
### 权限管理
|
||||
- **管理员**:为用户分配量表权限
|
||||
- **普通用户**:查看自己的权限范围
|
||||
|
||||
### 问卷管理
|
||||
- **管理员**:创建自定义问卷、主观题评分
|
||||
- **普通用户**:参与问卷答题
|
||||
|
||||
### 预警管理
|
||||
- **管理员**:配置预警规则、查看预警信息
|
||||
- **普通用户**:不涉及
|
||||
|
||||
### 二维码管理
|
||||
- **管理员**:生成和管理二维码
|
||||
- **普通用户**:扫描二维码进行测评或查看报告
|
||||
|
||||
---
|
||||
|
||||
## 📋 文档结构
|
||||
|
||||
```
|
||||
使用指南总览.md ← 您当前所在的位置
|
||||
├── 使用指南-系统管理员.md ← 管理员完整使用指南
|
||||
├── 使用指南-普通用户.md ← 普通用户完整使用指南
|
||||
└── 使用指南-注册用户.md ← 注册用户指南
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 常见问题快速查找
|
||||
|
||||
### 关于注册
|
||||
- 如何注册账号? → [注册用户使用指南](./使用指南-注册用户.md#注册流程)
|
||||
- 注册后无法登录? → [注册用户使用指南](./使用指南-注册用户.md#常见问题)
|
||||
|
||||
### 关于测评
|
||||
- 如何开始测评? → [普通用户使用指南](./使用指南-普通用户.md#开始测评)
|
||||
- 如何答题? → [普通用户使用指南](./使用指南-普通用户.md#答题操作)
|
||||
- 可以暂停测评吗? → [普通用户使用指南](./使用指南-普通用户.md#暂停和继续)
|
||||
|
||||
### 关于报告
|
||||
- 如何查看报告? → [普通用户使用指南](./使用指南-普通用户.md#查看报告)
|
||||
- 报告什么时候生成? → [普通用户使用指南](./使用指南-普通用户.md#常见问题)
|
||||
|
||||
### 关于权限
|
||||
- 看不到量表怎么办? → [普通用户使用指南](./使用指南-普通用户.md#常见问题)
|
||||
- 如何分配权限? → [系统管理员使用指南](./使用指南-系统管理员.md#权限管理)
|
||||
|
||||
### 关于量表
|
||||
- 如何导入新量表? → [系统管理员使用指南](./使用指南-系统管理员.md#量表管理)
|
||||
- 如何配置量表? → [系统管理员使用指南](./使用指南-系统管理员.md#配置量表)
|
||||
|
||||
---
|
||||
|
||||
## 📞 获取帮助
|
||||
|
||||
### 技术支持
|
||||
|
||||
如遇到问题,可以:
|
||||
|
||||
1. **查看文档**:阅读对应的使用指南
|
||||
2. **查看常见问题**:每个指南都包含常见问题部分
|
||||
3. **联系管理员**:联系系统管理员获取帮助
|
||||
4. **查看系统日志**:管理员可以查看系统日志排查问题
|
||||
|
||||
### 文档更新
|
||||
|
||||
- 文档会随着系统更新而更新
|
||||
- 建议定期查看最新版本
|
||||
- 如有疑问,请联系技术支持
|
||||
|
||||
---
|
||||
|
||||
## ✅ 使用检查清单
|
||||
|
||||
### 新用户检查清单
|
||||
|
||||
- [ ] 已完成账号注册
|
||||
- [ ] 已成功登录系统
|
||||
- [ ] 已阅读注册用户使用指南
|
||||
- [ ] 已联系管理员申请权限
|
||||
- [ ] 已获得量表权限
|
||||
- [ ] 已阅读普通用户使用指南
|
||||
- [ ] 已准备好进行首次测评
|
||||
|
||||
### 管理员检查清单
|
||||
|
||||
- [ ] 已修改默认密码
|
||||
- [ ] 已阅读系统管理员使用指南
|
||||
- [ ] 已配置系统基础设置
|
||||
- [ ] 已导入或创建量表
|
||||
- [ ] 已为用户分配权限
|
||||
- [ ] 已配置预警规则(如需要)
|
||||
- [ ] 已测试系统功能
|
||||
|
||||
---
|
||||
|
||||
## 🎓 学习路径建议
|
||||
|
||||
### 对于新用户
|
||||
|
||||
1. **第一步**:注册账号 → [注册用户使用指南](./使用指南-注册用户.md)
|
||||
2. **第二步**:等待权限分配
|
||||
3. **第三步**:学习使用系统 → [普通用户使用指南](./使用指南-普通用户.md)
|
||||
4. **第四步**:进行首次测评
|
||||
5. **第五步**:查看测评报告
|
||||
|
||||
### 对于管理员
|
||||
|
||||
1. **第一步**:登录系统并修改密码
|
||||
2. **第二步**:阅读管理员指南 → [系统管理员使用指南](./使用指南-系统管理员.md)
|
||||
3. **第三步**:配置系统基础设置
|
||||
4. **第四步**:导入或创建量表
|
||||
5. **第五步**:为用户分配权限
|
||||
6. **第六步**:测试系统功能
|
||||
7. **第七步**:开始日常管理
|
||||
|
||||
---
|
||||
|
||||
## 📝 文档说明
|
||||
|
||||
- **最后更新**:2025-01-XX
|
||||
- **文档版本**:v1.0
|
||||
- **适用系统版本**:AI心理健康测评系统 v1.0
|
||||
|
||||
---
|
||||
|
||||
**祝您使用愉快!** 🎉
|
||||
|
||||
375
使用指南-普通用户.md
375
使用指南-普通用户.md
|
|
@ -1,375 +0,0 @@
|
|||
# AI心理健康测评系统 - 普通用户使用指南
|
||||
|
||||
## 📋 目录
|
||||
|
||||
1. [系统概述](#系统概述)
|
||||
2. [注册账号](#注册账号)
|
||||
3. [登录系统](#登录系统)
|
||||
4. [开始测评](#开始测评)
|
||||
5. [答题操作](#答题操作)
|
||||
6. [查看报告](#查看报告)
|
||||
7. [查看历史记录](#查看历史记录)
|
||||
8. [使用二维码](#使用二维码)
|
||||
9. [用户档案](#用户档案)
|
||||
10. [常见问题](#常见问题)
|
||||
|
||||
---
|
||||
|
||||
## 系统概述
|
||||
|
||||
AI心理健康测评系统是一个专业的心理测评平台,您可以通过该系统进行各种心理量表测评,查看测评报告,了解自己的心理健康状况。
|
||||
|
||||
### 主要功能
|
||||
|
||||
- **心理测评**:进行各种心理量表测评
|
||||
- **查看报告**:查看详细的测评报告和结果解释
|
||||
- **历史记录**:查看历史测评记录
|
||||
- **二维码扫描**:通过扫码快速开始测评
|
||||
- **用户档案**:管理个人档案信息
|
||||
|
||||
---
|
||||
|
||||
## 注册账号
|
||||
|
||||
### 注册步骤
|
||||
|
||||
1. 打开浏览器,访问系统地址(如:`http://localhost:80`)
|
||||
2. 在登录页面,点击 **"注册"** 链接
|
||||
3. 填写注册信息:
|
||||
- **用户名**:用于登录的用户名(必填,唯一)
|
||||
- **密码**:登录密码(必填,建议8位以上)
|
||||
- **确认密码**:再次输入密码(必填)
|
||||
- **手机号**:手机号码(可选)
|
||||
- **邮箱**:邮箱地址(可选)
|
||||
4. 输入验证码(如显示)
|
||||
5. 点击 **"注册"** 按钮
|
||||
6. 注册成功后,系统会提示注册成功
|
||||
|
||||
### 注册注意事项
|
||||
|
||||
- 用户名不能重复,如果提示用户名已存在,请更换用户名
|
||||
- 密码建议包含字母和数字,提高安全性
|
||||
- 注册后需要等待管理员分配量表权限才能进行测评
|
||||
- 如果忘记密码,请联系管理员重置
|
||||
|
||||
---
|
||||
|
||||
## 登录系统
|
||||
|
||||
### 登录步骤
|
||||
|
||||
1. 打开浏览器,访问系统地址
|
||||
2. 在登录页面输入:
|
||||
- **用户名**:您的用户名
|
||||
- **密码**:您的密码
|
||||
- **验证码**:输入显示的验证码(如显示)
|
||||
3. 点击 **"登录"** 按钮
|
||||
4. 登录成功后进入系统首页
|
||||
|
||||
### 登录问题
|
||||
|
||||
- **忘记密码**:请联系管理员重置密码
|
||||
- **账号被锁定**:联系管理员解锁账号
|
||||
- **验证码看不清**:点击验证码图片刷新
|
||||
|
||||
---
|
||||
|
||||
## 开始测评
|
||||
|
||||
### 方式一:通过菜单开始
|
||||
|
||||
1. 登录系统后,点击左侧菜单 **"心理测评管理"** → **"测评管理"**
|
||||
2. 点击 **"开始测评"** 按钮
|
||||
3. 在量表选择页面,选择要进行的量表
|
||||
4. 填写被测评人信息:
|
||||
- **姓名**:被测评人姓名
|
||||
- **性别**:男/女
|
||||
- **年龄**:年龄
|
||||
- **其他信息**:根据需要填写
|
||||
5. 点击 **"开始测评"** 按钮
|
||||
6. 进入答题页面
|
||||
|
||||
### 方式二:通过二维码扫描
|
||||
|
||||
1. 使用手机扫描管理员提供的测评二维码
|
||||
2. 如果未登录,系统会提示先登录
|
||||
3. 登录后自动跳转到测评开始页面
|
||||
4. 填写被测评人信息
|
||||
5. 开始答题
|
||||
|
||||
### 量表选择说明
|
||||
|
||||
- **权限限制**:您只能看到管理员为您分配权限的量表
|
||||
- **量表信息**:每个量表显示名称、类型、题目数量、预计时间
|
||||
- **无权限提示**:如果没有可用的量表,系统会提示联系管理员
|
||||
|
||||
---
|
||||
|
||||
## 答题操作
|
||||
|
||||
### 答题界面
|
||||
|
||||
答题页面包含以下元素:
|
||||
- **量表信息**:显示量表名称和进度
|
||||
- **题目区域**:显示当前题目和选项
|
||||
- **进度条**:显示答题进度
|
||||
- **导航按钮**:上一题、下一题、提交
|
||||
|
||||
### 答题步骤
|
||||
|
||||
1. **阅读题目**:仔细阅读题目内容
|
||||
2. **选择答案**:
|
||||
- **单选题**:选择一个选项
|
||||
- **多选题**:可以选择多个选项
|
||||
3. **保存答案**:选择答案后,系统自动保存
|
||||
4. **继续答题**:
|
||||
- 点击 **"下一题"** 继续
|
||||
- 或点击 **"上一题"** 返回修改
|
||||
5. **完成答题**:
|
||||
- 答完所有题目后,点击 **"提交"** 按钮
|
||||
- 确认提交后,系统生成测评报告
|
||||
|
||||
### 答题技巧
|
||||
|
||||
- **认真阅读**:仔细阅读每个题目,理解题意
|
||||
- **如实回答**:根据自己的真实情况回答,不要猜测
|
||||
- **不要跳过**:尽量回答所有题目,确保结果准确
|
||||
- **可以修改**:在提交前可以随时返回修改答案
|
||||
- **注意时间**:某些量表有时间限制,注意答题时间
|
||||
|
||||
### 暂停和继续
|
||||
|
||||
如果无法一次性完成测评:
|
||||
|
||||
1. **暂停测评**:
|
||||
- 点击 **"暂停"** 按钮
|
||||
- 系统保存当前进度
|
||||
2. **继续测评**:
|
||||
- 下次登录后,进入 **"测评管理"**
|
||||
- 找到状态为"进行中"的测评
|
||||
- 点击 **"继续测评"** 按钮
|
||||
- 系统自动跳转到上次的答题位置
|
||||
|
||||
---
|
||||
|
||||
## 查看报告
|
||||
|
||||
### 查看方式
|
||||
|
||||
#### 方式一:通过菜单查看
|
||||
|
||||
1. 登录系统后,点击 **"心理测评管理"** → **"测评报告"**
|
||||
2. 在报告列表中,找到要查看的报告
|
||||
3. 点击 **"查看"** 按钮
|
||||
4. 查看报告详细内容
|
||||
|
||||
#### 方式二:通过测评记录查看
|
||||
|
||||
1. 进入 **"测评管理"**
|
||||
2. 找到已完成的测评记录
|
||||
3. 点击 **"查看报告"** 按钮
|
||||
4. 查看报告
|
||||
|
||||
#### 方式三:通过二维码扫描
|
||||
|
||||
1. 扫描管理员提供的报告查看二维码
|
||||
2. 如果未登录,系统会提示先登录
|
||||
3. 登录后自动跳转到报告详情页面
|
||||
|
||||
### 报告内容
|
||||
|
||||
测评报告通常包含以下内容:
|
||||
|
||||
- **基本信息**:
|
||||
- 被测评人信息
|
||||
- 测评时间
|
||||
- 量表名称
|
||||
- **测评结果**:
|
||||
- 总分
|
||||
- 各因子得分(如有)
|
||||
- 分数解释
|
||||
- **结果分析**:
|
||||
- 结果等级(如:正常、轻度、中度、重度)
|
||||
- 详细解释
|
||||
- 因子分析(如有)
|
||||
- **建议指导**:
|
||||
- 针对性的建议
|
||||
- 改善方法
|
||||
- 注意事项
|
||||
|
||||
### 报告说明
|
||||
|
||||
- **报告生成**:提交测评后,系统自动生成报告
|
||||
- **报告准确性**:报告结果基于您的答题情况,请如实回答
|
||||
- **仅供参考**:报告结果仅供参考,不能替代专业诊断
|
||||
- **隐私保护**:您的测评数据受到隐私保护
|
||||
|
||||
---
|
||||
|
||||
## 查看历史记录
|
||||
|
||||
### 查看测评记录
|
||||
|
||||
1. 进入 **"心理测评管理"** → **"测评管理"**
|
||||
2. 查看所有测评记录:
|
||||
- **进行中**:尚未完成的测评
|
||||
- **已完成**:已完成的测评
|
||||
- **已暂停**:暂停的测评
|
||||
3. 可以按条件搜索:
|
||||
- 量表名称
|
||||
- 被测评人
|
||||
- 测评状态
|
||||
- 创建时间
|
||||
|
||||
### 查看报告记录
|
||||
|
||||
1. 进入 **"心理测评管理"** → **"测评报告"**
|
||||
2. 查看所有报告记录
|
||||
3. 可以按条件搜索:
|
||||
- 报告标题
|
||||
- 量表名称
|
||||
- 创建时间
|
||||
|
||||
### 对比分析
|
||||
|
||||
- 可以查看同一被测评人的多次测评记录
|
||||
- 对比不同时间的测评结果
|
||||
- 了解心理变化趋势
|
||||
|
||||
---
|
||||
|
||||
## 使用二维码
|
||||
|
||||
### 扫描测评二维码
|
||||
|
||||
1. 使用手机扫描管理员提供的测评二维码
|
||||
2. 如果未登录,系统会提示先登录
|
||||
3. 登录后自动跳转到测评开始页面
|
||||
4. 填写被测评人信息
|
||||
5. 开始答题
|
||||
|
||||
### 扫描报告二维码
|
||||
|
||||
1. 使用手机扫描管理员提供的报告查看二维码
|
||||
2. 如果未登录,系统会提示先登录
|
||||
3. 登录后自动跳转到报告详情页面
|
||||
4. 查看报告内容
|
||||
|
||||
### 二维码使用说明
|
||||
|
||||
- **扫码工具**:可以使用微信、支付宝等应用的扫一扫功能
|
||||
- **网络要求**:需要网络连接才能访问
|
||||
- **权限要求**:需要登录并有相应权限
|
||||
- **有效期**:二维码可能有过期时间,过期后无法使用
|
||||
|
||||
---
|
||||
|
||||
## 用户档案
|
||||
|
||||
### 查看个人档案
|
||||
|
||||
1. 进入 **"心理测评管理"** → **"用户档案"**
|
||||
2. 查看个人档案信息
|
||||
3. 可以查看:
|
||||
- 基本信息
|
||||
- 测评记录
|
||||
- 档案历史
|
||||
|
||||
### 编辑个人档案
|
||||
|
||||
1. 在档案页面,点击 **"编辑"** 按钮
|
||||
2. 修改档案信息(如允许)
|
||||
3. 点击 **"确定"** 保存
|
||||
|
||||
### 档案说明
|
||||
|
||||
- **档案内容**:由管理员配置,可能包含自定义字段
|
||||
- **隐私保护**:档案信息受到隐私保护
|
||||
- **权限限制**:某些字段可能无法自行修改
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 注册后无法看到量表?
|
||||
|
||||
**A**: 注册后需要等待管理员为您分配量表权限。请联系管理员分配权限。
|
||||
|
||||
### Q2: 忘记密码怎么办?
|
||||
|
||||
**A**: 请联系管理员重置密码。管理员可以在用户管理中重置您的密码。
|
||||
|
||||
### Q3: 答题过程中可以暂停吗?
|
||||
|
||||
**A**: 可以。点击"暂停"按钮,系统会保存当前进度。下次登录后可以继续完成测评。
|
||||
|
||||
### Q4: 可以修改已提交的答案吗?
|
||||
|
||||
**A**: 不可以。提交后无法修改答案。请在提交前仔细检查。
|
||||
|
||||
### Q5: 报告什么时候生成?
|
||||
|
||||
**A**: 提交测评后,系统会自动生成报告。通常几秒钟内完成。
|
||||
|
||||
### Q6: 可以查看历史测评记录吗?
|
||||
|
||||
**A**: 可以。在"测评管理"中可以查看所有历史测评记录。
|
||||
|
||||
### Q7: 报告结果准确吗?
|
||||
|
||||
**A**: 报告结果基于您的答题情况。请如实回答题目,确保结果准确。但报告结果仅供参考,不能替代专业诊断。
|
||||
|
||||
### Q8: 可以导出报告吗?
|
||||
|
||||
**A**: 普通用户无法导出报告。如需导出,请联系管理员。
|
||||
|
||||
### Q9: 二维码扫描后无法访问?
|
||||
|
||||
**A**: 检查以下几点:
|
||||
- 是否已登录
|
||||
- 是否有相应权限
|
||||
- 二维码是否过期
|
||||
- 网络连接是否正常
|
||||
|
||||
### Q10: 如何联系管理员?
|
||||
|
||||
**A**: 请联系系统管理员或技术支持人员。
|
||||
|
||||
---
|
||||
|
||||
## 使用提示
|
||||
|
||||
### 测评前
|
||||
|
||||
- ✅ 确保网络连接正常
|
||||
- ✅ 选择一个安静的环境
|
||||
- ✅ 预留足够的答题时间
|
||||
- ✅ 准备好被测评人的基本信息
|
||||
|
||||
### 测评中
|
||||
|
||||
- ✅ 认真阅读每个题目
|
||||
- ✅ 根据自己的真实情况回答
|
||||
- ✅ 不要猜测或随意选择
|
||||
- ✅ 注意答题时间
|
||||
|
||||
### 测评后
|
||||
|
||||
- ✅ 查看生成的报告
|
||||
- ✅ 理解报告内容
|
||||
- ✅ 如有疑问,咨询专业人员
|
||||
- ✅ 保存报告信息(如需要)
|
||||
|
||||
---
|
||||
|
||||
## 隐私说明
|
||||
|
||||
- 您的测评数据受到严格保护
|
||||
- 只有管理员和您本人可以查看您的测评记录
|
||||
- 系统不会向第三方泄露您的个人信息
|
||||
- 请妥善保管您的账号密码
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-01-XX
|
||||
|
||||
264
使用指南-注册用户.md
264
使用指南-注册用户.md
|
|
@ -1,264 +0,0 @@
|
|||
# AI心理健康测评系统 - 注册用户使用指南
|
||||
|
||||
## 📋 目录
|
||||
|
||||
1. [注册流程](#注册流程)
|
||||
2. [首次登录](#首次登录)
|
||||
3. [等待权限分配](#等待权限分配)
|
||||
4. [开始使用](#开始使用)
|
||||
5. [常见问题](#常见问题)
|
||||
|
||||
---
|
||||
|
||||
## 注册流程
|
||||
|
||||
### 访问注册页面
|
||||
|
||||
1. 打开浏览器,访问系统地址(如:`http://localhost:80`)
|
||||
2. 在登录页面,点击 **"注册"** 链接或按钮
|
||||
3. 进入注册页面
|
||||
|
||||
### 填写注册信息
|
||||
|
||||
在注册页面填写以下信息:
|
||||
|
||||
#### 必填信息
|
||||
|
||||
- **用户名**:
|
||||
- 用于登录的用户名
|
||||
- 必须唯一,不能与其他用户重复
|
||||
- 建议使用字母、数字组合
|
||||
- 长度建议:3-20个字符
|
||||
|
||||
- **密码**:
|
||||
- 登录密码
|
||||
- 建议8位以上
|
||||
- 建议包含字母和数字
|
||||
- 不要使用过于简单的密码(如:123456)
|
||||
|
||||
- **确认密码**:
|
||||
- 再次输入密码
|
||||
- 必须与密码一致
|
||||
|
||||
#### 可选信息
|
||||
|
||||
- **手机号**:手机号码(用于找回密码等)
|
||||
- **邮箱**:邮箱地址(用于接收通知等)
|
||||
|
||||
### 完成注册
|
||||
|
||||
1. 输入验证码(如显示)
|
||||
2. 阅读并同意用户协议(如有)
|
||||
3. 点击 **"注册"** 按钮
|
||||
4. 系统验证信息:
|
||||
- 如果用户名已存在,会提示"用户名已存在"
|
||||
- 如果密码不一致,会提示"两次密码不一致"
|
||||
- 如果验证码错误,会提示"验证码错误"
|
||||
5. 注册成功后,系统会显示"注册成功"提示
|
||||
|
||||
---
|
||||
|
||||
## 首次登录
|
||||
|
||||
### 登录步骤
|
||||
|
||||
1. 注册成功后,返回登录页面
|
||||
2. 使用注册的用户名和密码登录
|
||||
3. 首次登录后,您可能会看到:
|
||||
- 欢迎页面
|
||||
- 系统提示(如:等待管理员分配权限)
|
||||
- 空白的量表列表(如果还没有权限)
|
||||
|
||||
### 登录后状态
|
||||
|
||||
注册用户首次登录后,通常处于以下状态:
|
||||
|
||||
- ✅ **账号已激活**:可以正常登录系统
|
||||
- ⏳ **等待权限**:需要等待管理员分配量表权限
|
||||
- 📋 **功能受限**:可能无法看到量表或进行测评
|
||||
|
||||
---
|
||||
|
||||
## 等待权限分配
|
||||
|
||||
### 为什么需要等待?
|
||||
|
||||
注册用户需要管理员分配量表访问权限后才能进行测评。这是为了:
|
||||
|
||||
- **权限控制**:确保用户只能访问被授权的量表
|
||||
- **数据安全**:保护测评数据的安全
|
||||
- **管理规范**:便于管理员统一管理
|
||||
|
||||
### 如何知道权限已分配?
|
||||
|
||||
权限分配后,您可以通过以下方式确认:
|
||||
|
||||
1. **刷新页面**:登录后刷新浏览器页面
|
||||
2. **查看量表列表**:进入"测评管理",如果能看到量表列表,说明已有权限
|
||||
3. **联系管理员**:如果长时间没有权限,可以联系管理员确认
|
||||
|
||||
### 权限说明
|
||||
|
||||
管理员可以为您分配:
|
||||
|
||||
- **量表权限**:指定您可以访问哪些量表
|
||||
- **时间范围**:权限的有效期(开始时间和结束时间)
|
||||
- **权限类型**:按用户、角色或部门分配
|
||||
|
||||
---
|
||||
|
||||
## 开始使用
|
||||
|
||||
### 获得权限后
|
||||
|
||||
当管理员为您分配权限后,您可以:
|
||||
|
||||
1. **查看可用量表**:
|
||||
- 进入"心理测评管理" → "测评管理"
|
||||
- 点击"开始测评"
|
||||
- 查看可用的量表列表
|
||||
|
||||
2. **开始测评**:
|
||||
- 选择要进行的量表
|
||||
- 填写被测评人信息
|
||||
- 开始答题
|
||||
|
||||
3. **查看报告**:
|
||||
- 完成测评后查看报告
|
||||
- 查看历史测评记录
|
||||
|
||||
### 使用流程
|
||||
|
||||
完整的测评流程:
|
||||
|
||||
```
|
||||
注册账号
|
||||
↓
|
||||
登录系统
|
||||
↓
|
||||
等待权限分配
|
||||
↓
|
||||
选择量表
|
||||
↓
|
||||
填写被测评人信息
|
||||
↓
|
||||
开始答题
|
||||
↓
|
||||
提交测评
|
||||
↓
|
||||
查看报告
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 注册时提示"用户名已存在"?
|
||||
|
||||
**A**: 该用户名已被其他用户使用。请更换一个不同的用户名。
|
||||
|
||||
**建议**:
|
||||
- 使用字母+数字组合
|
||||
- 添加下划线或连字符
|
||||
- 使用邮箱前缀作为用户名
|
||||
|
||||
### Q2: 注册时提示"密码不一致"?
|
||||
|
||||
**A**: "密码"和"确认密码"输入不一致。请重新输入,确保两次输入的密码完全相同。
|
||||
|
||||
### Q3: 注册后无法登录?
|
||||
|
||||
**A**: 检查以下几点:
|
||||
- 用户名和密码是否正确
|
||||
- 是否输入了正确的验证码
|
||||
- 账号是否被管理员停用
|
||||
- 联系管理员确认账号状态
|
||||
|
||||
### Q4: 登录后看不到量表?
|
||||
|
||||
**A**: 这是正常情况。注册用户需要等待管理员分配量表权限。请:
|
||||
- 等待管理员分配权限
|
||||
- 或联系管理员申请权限
|
||||
|
||||
### Q5: 如何联系管理员?
|
||||
|
||||
**A**: 请联系系统管理员或技术支持人员,告知您的用户名和需求。
|
||||
|
||||
### Q6: 忘记密码怎么办?
|
||||
|
||||
**A**: 请联系管理员重置密码。管理员可以在用户管理中重置您的密码。
|
||||
|
||||
### Q7: 可以修改注册信息吗?
|
||||
|
||||
**A**: 部分信息可以修改:
|
||||
- 登录后进入"个人中心"(如有)
|
||||
- 或联系管理员修改
|
||||
|
||||
### Q8: 注册后多久可以获得权限?
|
||||
|
||||
**A**: 这取决于管理员的工作安排。通常:
|
||||
- 管理员会定期审核新注册用户
|
||||
- 或根据申请及时分配权限
|
||||
- 建议主动联系管理员申请
|
||||
|
||||
### Q9: 可以注册多个账号吗?
|
||||
|
||||
**A**: 可以,但需要:
|
||||
- 使用不同的用户名
|
||||
- 每个账号独立管理
|
||||
- 遵守系统使用规范
|
||||
|
||||
### Q10: 注册信息会被泄露吗?
|
||||
|
||||
**A**: 不会。系统严格保护用户隐私:
|
||||
- 注册信息受到加密保护
|
||||
- 不会向第三方泄露
|
||||
- 只有管理员可以查看(用于权限管理)
|
||||
|
||||
---
|
||||
|
||||
## 注册注意事项
|
||||
|
||||
### 用户名选择
|
||||
|
||||
- ✅ 使用易记的用户名
|
||||
- ✅ 避免使用特殊字符
|
||||
- ✅ 不要使用真实姓名(保护隐私)
|
||||
- ❌ 不要使用过于简单的用户名(如:abc、123)
|
||||
|
||||
### 密码设置
|
||||
|
||||
- ✅ 使用8位以上的密码
|
||||
- ✅ 包含字母和数字
|
||||
- ✅ 定期更换密码
|
||||
- ❌ 不要使用过于简单的密码
|
||||
- ❌ 不要使用个人信息作为密码
|
||||
|
||||
### 信息填写
|
||||
|
||||
- ✅ 如实填写信息(如手机号、邮箱)
|
||||
- ✅ 信息用于账号管理和找回密码
|
||||
- ⚠️ 注意保护个人隐私
|
||||
|
||||
---
|
||||
|
||||
## 下一步
|
||||
|
||||
注册成功后,建议您:
|
||||
|
||||
1. **保存账号信息**:记录用户名和密码,妥善保管
|
||||
2. **联系管理员**:主动联系管理员申请量表权限
|
||||
3. **了解系统**:阅读《普通用户使用指南》,了解系统功能
|
||||
4. **准备测评**:获得权限后,准备好进行测评
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [普通用户使用指南](./使用指南-普通用户.md) - 详细的使用说明
|
||||
- [系统管理员使用指南](./使用指南-系统管理员.md) - 管理员功能说明
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-01-XX
|
||||
|
||||
650
使用指南-系统管理员.md
650
使用指南-系统管理员.md
|
|
@ -1,650 +0,0 @@
|
|||
# AI心理健康测评系统 - 系统管理员使用指南
|
||||
|
||||
## 📋 目录
|
||||
|
||||
1. [系统概述](#系统概述)
|
||||
2. [登录系统](#登录系统)
|
||||
3. [量表管理](#量表管理)
|
||||
4. [用户管理](#用户管理)
|
||||
5. [权限管理](#权限管理)
|
||||
6. [测评管理](#测评管理)
|
||||
7. [报告管理](#报告管理)
|
||||
8. [问卷管理](#问卷管理)
|
||||
9. [预警管理](#预警管理)
|
||||
10. [二维码管理](#二维码管理)
|
||||
11. [用户档案管理](#用户档案管理)
|
||||
12. [心理网站管理](#心理网站管理)
|
||||
13. [系统设置](#系统设置)
|
||||
14. [数据导出](#数据导出)
|
||||
15. [常见问题](#常见问题)
|
||||
|
||||
---
|
||||
|
||||
## 系统概述
|
||||
|
||||
AI心理健康测评系统是一个基于Web的心理测评管理平台,支持量表测评、自定义问卷、用户档案管理、预警管理等功能。
|
||||
|
||||
### 主要功能模块
|
||||
|
||||
- **量表管理**:创建、导入、配置心理量表
|
||||
- **用户管理**:管理用户账号、分配权限
|
||||
- **测评管理**:查看和管理测评记录
|
||||
- **报告管理**:查看、编辑、导出测评报告
|
||||
- **问卷管理**:创建自定义问卷、主观题评分
|
||||
- **预警管理**:配置预警规则、查看预警信息
|
||||
- **二维码管理**:生成和管理测评二维码
|
||||
- **用户档案**:管理用户档案信息
|
||||
- **心理网站**:管理心理教育网站内容
|
||||
|
||||
---
|
||||
|
||||
## 登录系统
|
||||
|
||||
### 访问系统
|
||||
|
||||
1. 打开浏览器,访问系统地址(如:`http://localhost:80`)
|
||||
2. 进入登录页面
|
||||
|
||||
### 默认管理员账号
|
||||
|
||||
- **用户名**:`admin`
|
||||
- **密码**:`admin123`
|
||||
|
||||
> ⚠️ **安全提示**:首次登录后请立即修改密码!
|
||||
|
||||
### 登录步骤
|
||||
|
||||
1. 输入用户名和密码
|
||||
2. 输入验证码(如显示)
|
||||
3. 点击"登录"按钮
|
||||
4. 登录成功后进入系统首页
|
||||
|
||||
---
|
||||
|
||||
## 量表管理
|
||||
|
||||
### 功能说明
|
||||
|
||||
量表管理是系统的核心功能,管理员可以创建、导入、配置和管理心理量表。
|
||||
|
||||
### 创建新量表
|
||||
|
||||
#### 方式一:手动创建
|
||||
|
||||
1. 进入 **心理测评管理** → **量表管理**
|
||||
2. 点击 **"新增"** 按钮
|
||||
3. 填写量表基本信息:
|
||||
- **量表编码**:唯一标识(如:`SCL_90`)
|
||||
- **量表名称**:量表完整名称
|
||||
- **量表类型**:选择类型(情绪、人格、行为等)
|
||||
- **量表简介**:简短介绍
|
||||
- **量表描述**:详细描述
|
||||
- **题目数量**:预计题目数
|
||||
- **预计完成时间**:分钟数
|
||||
- **适用人群**:如"一般人群"、"青少年"等
|
||||
- **作者**:量表作者
|
||||
- **来源**:量表来源
|
||||
- **状态**:选择"正常"(启用)
|
||||
4. 点击 **"确定"** 保存
|
||||
|
||||
#### 方式二:JSON格式导入
|
||||
|
||||
1. 进入 **量表管理** 页面
|
||||
2. 点击 **"导入"** 按钮
|
||||
3. 选择JSON格式的量表文件
|
||||
4. 系统自动解析并导入量表数据
|
||||
5. 检查导入结果,确认无误
|
||||
|
||||
> 📖 **详细说明**:参考《新量表导入完整操作指南.md》
|
||||
|
||||
### 配置量表
|
||||
|
||||
创建量表后,需要完成以下配置:
|
||||
|
||||
#### 1. 添加题目
|
||||
|
||||
1. 在量表列表中,点击 **"题目管理"** 按钮
|
||||
2. 点击 **"新增"** 添加题目
|
||||
3. 填写题目信息:
|
||||
- **题目序号**:如 1、2、3...
|
||||
- **题目内容**:题目的完整文字
|
||||
- **题目类型**:单选题/多选题
|
||||
- **是否必答**:是/否
|
||||
4. 保存题目
|
||||
|
||||
#### 2. 配置选项
|
||||
|
||||
1. 在题目列表中,点击 **"选项管理"** 按钮
|
||||
2. 为每个题目添加选项:
|
||||
- **选项编码**:如 A、B、C、D
|
||||
- **选项内容**:选项文字
|
||||
- **选项分数**:该选项对应的分数
|
||||
3. 保存选项
|
||||
|
||||
#### 3. 添加因子(如需要)
|
||||
|
||||
1. 在量表列表中,点击 **"因子管理"** 按钮
|
||||
2. 点击 **"新增"** 添加因子
|
||||
3. 填写因子信息:
|
||||
- **因子编码**:如 F1、F2
|
||||
- **因子名称**:如"躯体化因子"
|
||||
- **因子描述**:详细说明
|
||||
4. 保存因子
|
||||
|
||||
#### 4. 配置计分规则(如需要)
|
||||
|
||||
1. 在因子列表中,点击 **"计分规则"** 按钮
|
||||
2. 为每个因子配置计分规则:
|
||||
- 选择题目
|
||||
- 选择选项(可选)
|
||||
- 设置权重
|
||||
- 选择计算方式(求和/平均/最大/最小)
|
||||
3. 保存规则
|
||||
|
||||
#### 5. 配置结果解释
|
||||
|
||||
1. 进入 **心理测评管理** → **解释配置**
|
||||
2. 点击 **"新增"** 添加解释
|
||||
3. 填写解释信息:
|
||||
- **量表**:选择量表
|
||||
- **因子**:选择因子(可选)
|
||||
- **分数下限/上限**:分数范围
|
||||
- **等级**:如"低"、"中"、"高"
|
||||
- **解释标题**:解释标题
|
||||
- **解释内容**:详细解释
|
||||
- **建议指导**:建议和指导
|
||||
4. 保存解释
|
||||
|
||||
### 管理量表
|
||||
|
||||
- **编辑量表**:点击 **"修改"** 按钮
|
||||
- **删除量表**:点击 **"删除"** 按钮(需谨慎)
|
||||
- **查看详情**:点击量表名称
|
||||
- **导出量表**:勾选量表后点击 **"导出"** 按钮
|
||||
|
||||
---
|
||||
|
||||
## 用户管理
|
||||
|
||||
### 功能说明
|
||||
|
||||
用户管理用于管理系统用户账号,包括创建用户、分配角色、设置权限等。
|
||||
|
||||
### 查看用户列表
|
||||
|
||||
1. 进入 **系统管理** → **用户管理**
|
||||
2. 查看用户列表,可以按条件搜索:
|
||||
- 用户名
|
||||
- 手机号
|
||||
- 状态
|
||||
- 创建时间
|
||||
|
||||
### 创建新用户
|
||||
|
||||
1. 在用户管理页面,点击 **"新增"** 按钮
|
||||
2. 填写用户信息:
|
||||
- **用户名**:登录用户名
|
||||
- **昵称**:显示名称
|
||||
- **邮箱**:邮箱地址
|
||||
- **手机号**:手机号码
|
||||
- **性别**:男/女
|
||||
- **密码**:初始密码
|
||||
- **部门**:所属部门
|
||||
- **角色**:选择角色
|
||||
- **状态**:正常/停用
|
||||
3. 点击 **"确定"** 保存
|
||||
|
||||
### 管理用户
|
||||
|
||||
- **编辑用户**:点击 **"修改"** 按钮
|
||||
- **删除用户**:点击 **"删除"** 按钮
|
||||
- **重置密码**:点击 **"重置密码"** 按钮
|
||||
- **分配角色**:点击 **"更多"** → **"分配角色"**
|
||||
- **分配量表权限**:点击 **"更多"** → **"分配量表权限"**
|
||||
|
||||
### 批量操作
|
||||
|
||||
1. 勾选多个用户
|
||||
2. 点击 **"批量删除"** 或 **"批量导出"**
|
||||
|
||||
---
|
||||
|
||||
## 权限管理
|
||||
|
||||
### 功能说明
|
||||
|
||||
权限管理用于控制用户对量表的访问权限,支持按用户、角色、部门分配权限。
|
||||
|
||||
### 量表权限管理
|
||||
|
||||
1. 进入 **心理测评管理** → **量表权限管理**
|
||||
2. 查看权限列表,可以按条件筛选:
|
||||
- 量表名称
|
||||
- 用户名称
|
||||
- 部门
|
||||
- 状态
|
||||
|
||||
### 分配权限
|
||||
|
||||
#### 方式一:通过权限管理页面
|
||||
|
||||
1. 在权限管理页面,点击 **"新增"** 按钮
|
||||
2. 填写权限信息:
|
||||
- **量表**:选择量表
|
||||
- **用户**:选择用户(可选)
|
||||
- **角色**:选择角色(可选)
|
||||
- **部门**:选择部门(可选)
|
||||
- **开始时间**:权限生效时间
|
||||
- **结束时间**:权限失效时间
|
||||
- **状态**:有效/无效
|
||||
3. 点击 **"确定"** 保存
|
||||
|
||||
#### 方式二:通过用户管理页面
|
||||
|
||||
1. 进入 **系统管理** → **用户管理**
|
||||
2. 找到要分配权限的用户
|
||||
3. 点击 **"更多"** → **"分配量表权限"**
|
||||
4. 在权限分配页面,选择量表并设置时间范围
|
||||
5. 点击 **"确定"** 保存
|
||||
|
||||
### 权限规则
|
||||
|
||||
- **管理员**(userId = 1)自动拥有所有量表的访问权限
|
||||
- **用户直接权限**:优先级最高
|
||||
- **角色权限**:次优先级
|
||||
- **部门权限**:再次优先级
|
||||
- **全局权限**:最低优先级(所有用户)
|
||||
|
||||
---
|
||||
|
||||
## 测评管理
|
||||
|
||||
### 功能说明
|
||||
|
||||
测评管理用于查看和管理所有测评记录,包括进行中的测评和已完成的测评。
|
||||
|
||||
### 查看测评列表
|
||||
|
||||
1. 进入 **心理测评管理** → **测评管理**
|
||||
2. 查看测评列表,可以按条件搜索:
|
||||
- 量表名称
|
||||
- 被测评人
|
||||
- 测评状态
|
||||
- 创建时间
|
||||
|
||||
### 测评状态
|
||||
|
||||
- **进行中**:测评尚未完成
|
||||
- **已完成**:测评已完成并生成报告
|
||||
- **已暂停**:测评被暂停(支持继续测评)
|
||||
|
||||
### 管理测评
|
||||
|
||||
- **查看详情**:点击测评记录
|
||||
- **查看报告**:点击 **"查看报告"** 按钮
|
||||
- **删除测评**:点击 **"删除"** 按钮
|
||||
- **继续测评**:对于暂停的测评,可以继续完成
|
||||
|
||||
### 管理员快速填充(测试功能)
|
||||
|
||||
> ⚠️ **注意**:此功能仅用于测试,不建议用于真实测评数据。
|
||||
|
||||
1. 进入测评页面
|
||||
2. 点击 **"快速填充"** 下拉按钮
|
||||
3. 选择填充策略:
|
||||
- 填充第一个选项并提交
|
||||
- 填充中间选项并提交
|
||||
- 填充最后一个选项并提交
|
||||
- 随机填充并提交
|
||||
4. 确认后系统自动填充并提交测评
|
||||
|
||||
---
|
||||
|
||||
## 报告管理
|
||||
|
||||
### 功能说明
|
||||
|
||||
报告管理用于查看、编辑、导出测评报告。
|
||||
|
||||
### 查看报告列表
|
||||
|
||||
1. 进入 **心理测评管理** → **测评报告**
|
||||
2. 查看报告列表,可以按条件搜索:
|
||||
- 报告标题
|
||||
- 被测评人
|
||||
- 量表名称
|
||||
- 生成状态
|
||||
- 创建时间
|
||||
|
||||
### 查看报告详情
|
||||
|
||||
1. 在报告列表中,点击 **"查看"** 按钮
|
||||
2. 查看报告详细内容:
|
||||
- 基本信息
|
||||
- 测评结果
|
||||
- 因子分析
|
||||
- 结果解释
|
||||
- 建议指导
|
||||
|
||||
### 编辑报告
|
||||
|
||||
1. 在报告列表中,点击 **"编辑"** 按钮
|
||||
2. 修改报告内容:
|
||||
- 解释标题
|
||||
- 解释内容
|
||||
- 建议指导
|
||||
3. 点击 **"确定"** 保存
|
||||
|
||||
### 导出报告
|
||||
|
||||
1. 勾选需要导出的报告(可多选)
|
||||
2. 点击 **"导出"** 按钮
|
||||
3. 系统生成Excel文件并下载
|
||||
|
||||
---
|
||||
|
||||
## 问卷管理
|
||||
|
||||
### 功能说明
|
||||
|
||||
问卷管理用于创建自定义问卷,支持多种题型,适合在线考试、练习等场景。
|
||||
|
||||
### 创建问卷
|
||||
|
||||
1. 进入 **心理测评管理** → **问卷管理**
|
||||
2. 点击 **"新增"** 按钮
|
||||
3. 填写问卷信息:
|
||||
- **问卷名称**:问卷标题
|
||||
- **问卷描述**:问卷说明
|
||||
- **问卷类型**:选择类型
|
||||
- **状态**:正常/停用
|
||||
4. 点击 **"确定"** 保存
|
||||
|
||||
### 添加题目
|
||||
|
||||
1. 在问卷列表中,点击 **"题目管理"** 按钮
|
||||
2. 点击 **"新增"** 添加题目
|
||||
3. 选择题目类型:
|
||||
- **单选题**:只有一个正确答案
|
||||
- **多选题**:可以有多个正确答案
|
||||
- **判断题**:对/错
|
||||
- **填空题**:文本输入
|
||||
- **简答题**:短文本回答
|
||||
- **问答题**:长文本回答
|
||||
- **作文题**:长文本回答
|
||||
4. 填写题目信息:
|
||||
- **题目内容**:题目文字
|
||||
- **题目分值**:该题分数
|
||||
- **是否必答**:是/否
|
||||
- **正确答案**:客观题的正确答案
|
||||
5. 保存题目
|
||||
|
||||
### 主观题评分
|
||||
|
||||
1. 进入 **心理测评管理** → **主观题评分**
|
||||
2. 查看待评分题目列表
|
||||
3. 点击 **"评分"** 按钮
|
||||
4. 输入得分和评语(可选)
|
||||
5. 点击 **"确定"** 保存
|
||||
|
||||
### 批量评分
|
||||
|
||||
1. 勾选多个待评分题目
|
||||
2. 点击 **"批量评分"** 按钮
|
||||
3. 为每个题目输入得分和评语
|
||||
4. 点击 **"确定"** 批量保存
|
||||
|
||||
---
|
||||
|
||||
## 预警管理
|
||||
|
||||
### 功能说明
|
||||
|
||||
预警管理用于配置预警规则,当测评结果异常时自动向管理员发出警告。
|
||||
|
||||
### 配置预警规则
|
||||
|
||||
1. 进入 **心理测评管理** → **预警规则配置**
|
||||
2. 点击 **"新增"** 按钮
|
||||
3. 填写预警规则信息:
|
||||
- **量表**:选择量表
|
||||
- **因子**:选择因子(可选)
|
||||
- **规则名称**:如"重度抑郁预警"
|
||||
- **预警等级**:低/中/高/紧急
|
||||
- **分数下限/上限**:触发预警的分数范围
|
||||
- **百分位下限/上限**:触发预警的百分位范围
|
||||
- **自动解除**:是否自动解除预警
|
||||
- **解除条件**:自动解除的条件
|
||||
- **状态**:正常/停用
|
||||
4. 点击 **"确定"** 保存
|
||||
|
||||
### 查看预警信息
|
||||
|
||||
1. 进入 **心理测评管理** → **预警管理**
|
||||
2. 查看预警列表,可以按条件筛选:
|
||||
- 预警等级
|
||||
- 量表名称
|
||||
- 被测评人
|
||||
- 预警状态
|
||||
- 创建时间
|
||||
|
||||
### 处理预警
|
||||
|
||||
- **查看详情**:点击预警记录查看详细信息
|
||||
- **解除预警**:手动解除预警
|
||||
- **导出预警**:导出预警数据
|
||||
|
||||
---
|
||||
|
||||
## 二维码管理
|
||||
|
||||
### 功能说明
|
||||
|
||||
二维码管理用于生成和管理测评二维码,方便用户通过扫码快速访问测评和查看报告。
|
||||
|
||||
### 生成量表测评二维码
|
||||
|
||||
1. 进入 **心理测评管理** → **量表管理**
|
||||
2. 找到目标量表
|
||||
3. 点击 **"二维码"** 按钮
|
||||
4. 系统生成二维码并显示
|
||||
5. 可以下载二维码图片或直接打印
|
||||
|
||||
### 生成报告查看二维码
|
||||
|
||||
1. 进入 **心理测评管理** → **测评报告**
|
||||
2. 找到目标报告
|
||||
3. 点击 **"二维码"** 按钮
|
||||
4. 系统生成二维码
|
||||
|
||||
### 管理二维码
|
||||
|
||||
1. 进入 **心理测评管理** → **二维码管理**
|
||||
2. 查看所有二维码:
|
||||
- 二维码类型
|
||||
- 目标信息
|
||||
- 扫码次数
|
||||
- 状态
|
||||
- 过期时间
|
||||
3. 可以重新生成或删除二维码
|
||||
|
||||
---
|
||||
|
||||
## 用户档案管理
|
||||
|
||||
### 功能说明
|
||||
|
||||
用户档案管理用于管理用户的档案信息,支持自定义档案项目。
|
||||
|
||||
### 查看用户档案
|
||||
|
||||
1. 进入 **心理测评管理** → **用户档案**
|
||||
2. 查看用户档案列表
|
||||
3. 可以按条件搜索用户
|
||||
|
||||
### 编辑用户档案
|
||||
|
||||
1. 在档案列表中,点击 **"编辑"** 按钮
|
||||
2. 修改档案信息:
|
||||
- 基本信息
|
||||
- 自定义字段
|
||||
3. 点击 **"确定"** 保存
|
||||
|
||||
### 自定义档案项目
|
||||
|
||||
1. 进入 **系统管理** → **参数设置**(如支持)
|
||||
2. 配置自定义档案字段
|
||||
3. 保存配置
|
||||
|
||||
---
|
||||
|
||||
## 心理网站管理
|
||||
|
||||
### 功能说明
|
||||
|
||||
心理网站管理用于管理心理教育网站的内容,包括文章、分类、评论等。
|
||||
|
||||
### 管理文章
|
||||
|
||||
1. 进入 **心理测评管理** → **心理网站** → **内容管理**
|
||||
2. 查看文章列表
|
||||
3. 可以新增、编辑、删除文章
|
||||
|
||||
### 管理分类
|
||||
|
||||
1. 进入 **心理测评管理** → **心理网站** → **分类管理**
|
||||
2. 管理文章分类
|
||||
|
||||
### 管理评论
|
||||
|
||||
1. 进入 **心理测评管理** → **心理网站** → **评论管理**
|
||||
2. 查看和管理用户评论
|
||||
|
||||
---
|
||||
|
||||
## 系统设置
|
||||
|
||||
### 角色管理
|
||||
|
||||
1. 进入 **系统管理** → **角色管理**
|
||||
2. 查看和管理系统角色
|
||||
3. 为角色分配菜单权限和按钮权限
|
||||
|
||||
### 菜单管理
|
||||
|
||||
1. 进入 **系统管理** → **菜单管理**
|
||||
2. 查看和管理系统菜单
|
||||
3. 可以新增、编辑、删除菜单
|
||||
|
||||
### 参数设置
|
||||
|
||||
1. 进入 **系统管理** → **参数设置**
|
||||
2. 配置系统参数
|
||||
|
||||
### 字典管理
|
||||
|
||||
1. 进入 **系统管理** → **字典管理**
|
||||
2. 管理系统字典数据
|
||||
|
||||
---
|
||||
|
||||
## 数据导出
|
||||
|
||||
### 量表导出
|
||||
|
||||
1. 进入 **心理测评管理** → **量表管理**
|
||||
2. 勾选需要导出的量表(可多选)
|
||||
3. 点击 **"导出"** 按钮
|
||||
4. 系统生成JSON格式文件并下载
|
||||
|
||||
### 报告导出
|
||||
|
||||
1. 进入 **心理测评管理** → **测评报告**
|
||||
2. 勾选需要导出的报告(可多选)
|
||||
3. 点击 **"导出"** 按钮
|
||||
4. 系统生成Excel格式文件并下载
|
||||
|
||||
### 数据备份
|
||||
|
||||
1. 进入 **系统管理** → **数据备份**(如支持)
|
||||
2. 执行数据备份操作
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 如何导入新量表?
|
||||
|
||||
**A**: 有两种方式:
|
||||
1. **手动创建**:在量表管理中逐个添加题目和选项
|
||||
2. **JSON导入**:准备符合格式的JSON文件,使用导入功能
|
||||
|
||||
详细说明请参考《新量表导入完整操作指南.md》
|
||||
|
||||
### Q2: 用户看不到量表怎么办?
|
||||
|
||||
**A**: 检查以下几点:
|
||||
1. 量表状态是否为"正常"
|
||||
2. 用户是否有量表访问权限
|
||||
3. 权限是否在有效期内
|
||||
4. 刷新浏览器页面
|
||||
|
||||
### Q3: 如何配置预警规则?
|
||||
|
||||
**A**:
|
||||
1. 进入"预警规则配置"
|
||||
2. 选择量表和因子
|
||||
3. 设置分数范围或百分位范围
|
||||
4. 选择预警等级
|
||||
5. 保存规则
|
||||
|
||||
### Q4: 二维码无法访问怎么办?
|
||||
|
||||
**A**: 检查以下几点:
|
||||
1. 二维码是否过期
|
||||
2. 二维码状态是否为"有效"
|
||||
3. 网络连接是否正常
|
||||
4. 系统URL配置是否正确
|
||||
|
||||
### Q5: 如何批量分配用户权限?
|
||||
|
||||
**A**:
|
||||
1. 进入"量表权限管理"
|
||||
2. 点击"新增"
|
||||
3. 选择量表
|
||||
4. 选择角色或部门(不选择用户表示批量分配)
|
||||
5. 设置时间范围
|
||||
6. 保存
|
||||
|
||||
### Q6: 报告生成失败怎么办?
|
||||
|
||||
**A**: 检查以下几点:
|
||||
1. 量表是否配置了结果解释
|
||||
2. 因子计分规则是否正确
|
||||
3. 测评是否已完成
|
||||
4. 查看系统日志错误信息
|
||||
|
||||
### Q7: 如何修改报告内容?
|
||||
|
||||
**A**:
|
||||
1. 进入"测评报告"
|
||||
2. 找到目标报告
|
||||
3. 点击"编辑"按钮
|
||||
4. 修改解释内容或建议指导
|
||||
5. 保存
|
||||
|
||||
---
|
||||
|
||||
## 技术支持
|
||||
|
||||
如遇到问题,请:
|
||||
|
||||
1. 查看系统日志
|
||||
2. 检查浏览器控制台错误
|
||||
3. 参考相关文档
|
||||
4. 联系技术支持
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-01-XX
|
||||
|
||||
182
修复504超时错误说明.md
182
修复504超时错误说明.md
|
|
@ -1,182 +0,0 @@
|
|||
# 用户导入504超时错误修复说明
|
||||
|
||||
## 问题描述
|
||||
在导入用户档案数据时,出现 **504 Gateway Time-out** 错误。这是因为:
|
||||
1. 数据量较大,导入处理时间超过了网关(Nginx)的超时限制
|
||||
2. 同步处理导致HTTP请求长时间等待响应
|
||||
|
||||
## 修复方案
|
||||
采用**异步导入**方式,将耗时的导入操作放到后台线程执行,HTTP请求立即返回,通过进度轮询查看导入状态。
|
||||
|
||||
## 修改内容
|
||||
|
||||
### 1. Controller层修改 ✅
|
||||
**文件**: `ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyUserProfileController.java`
|
||||
|
||||
**修改内容**:
|
||||
- 修改 `importData` 方法,调用异步导入方法 `importProfileAsync`
|
||||
- 立即返回响应,告知前端导入任务已启动
|
||||
- 前端通过 `/importProgress` 接口轮询获取进度
|
||||
|
||||
```java
|
||||
@PostMapping("/importData")
|
||||
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
|
||||
{
|
||||
ExcelUtil<PsyUserProfile> util = new ExcelUtil<PsyUserProfile>(PsyUserProfile.class);
|
||||
List<PsyUserProfile> profileList = util.importExcel(file.getInputStream());
|
||||
String operName = getUsername();
|
||||
|
||||
// 异步执行导入任务,立即返回
|
||||
profileService.importProfileAsync(profileList, updateSupport, operName);
|
||||
|
||||
// 立即返回响应,告知前端导入任务已启动
|
||||
return success("导入任务已启动,共 " + profileList.size() + " 条数据。请稍候查看导入进度...");
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Service接口层修改 ✅
|
||||
**文件**: `ry-xinli-system/src/main/java/com/ddnai/system/service/psychology/IPsyUserProfileService.java`
|
||||
|
||||
**修改内容**:
|
||||
- 添加异步导入方法接口 `importProfileAsync`
|
||||
|
||||
```java
|
||||
/**
|
||||
* 异步导入用户档案数据(避免超时)
|
||||
*/
|
||||
public void importProfileAsync(List<PsyUserProfile> profileList, Boolean isUpdateSupport, String operName);
|
||||
```
|
||||
|
||||
### 3. Service实现层修改 ✅
|
||||
**文件**: `ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyUserProfileServiceImpl.java`
|
||||
|
||||
**修改内容**:
|
||||
- 添加 `@Async` 注解导入
|
||||
- 实现异步导入方法,使用 `@Async` 注解标记
|
||||
|
||||
```java
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public void importProfileAsync(List<PsyUserProfile> profileList, Boolean isUpdateSupport, String operName)
|
||||
{
|
||||
try
|
||||
{
|
||||
log.info("异步导入任务开始,操作人:{},数据量:{}", operName, profileList.size());
|
||||
// 调用同步导入方法执行实际导入逻辑
|
||||
importProfile(profileList, isUpdateSupport, operName);
|
||||
log.info("异步导入任务完成,操作人:{}", operName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("异步导入任务失败,操作人:{},错误:{}", operName, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 启动类修改 ✅
|
||||
**文件**: `ry-xinli-admin/src/main/java/com/ddnai/XinliApplication.java`
|
||||
|
||||
**修改内容**:
|
||||
- 添加 `@EnableAsync` 注解启用异步支持
|
||||
- 配置异步任务线程池
|
||||
|
||||
```java
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
|
||||
@EnableAsync
|
||||
public class XinliApplication
|
||||
{
|
||||
@Bean(name = "taskExecutor")
|
||||
public Executor taskExecutor()
|
||||
{
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(5); // 核心线程数
|
||||
executor.setMaxPoolSize(10); // 最大线程数
|
||||
executor.setQueueCapacity(100); // 队列容量
|
||||
executor.setThreadNamePrefix("async-import-");
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
executor.setAwaitTerminationSeconds(60);
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 配置文件修改 ✅
|
||||
**文件**: `ry-xinli-admin/src/main/resources/application.yml`
|
||||
|
||||
**修改内容**:
|
||||
- 增加MVC异步请求超时配置(作为保险)
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
mvc:
|
||||
async:
|
||||
# 异步请求超时时间(毫秒),设置为10分钟
|
||||
request-timeout: 600000
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. **文件上传**: 用户上传Excel文件
|
||||
2. **解析数据**: Controller解析Excel文件为对象列表
|
||||
3. **启动异步任务**: 调用 `importProfileAsync` 方法,Spring会在独立线程中执行
|
||||
4. **立即返回**: HTTP请求立即返回成功响应
|
||||
5. **进度管理**: 后台线程执行导入,通过 `ImportProgressManager` 记录进度
|
||||
6. **前端轮询**: 前端通过 `/importProgress` 接口定期查询导入进度
|
||||
7. **完成通知**: 导入完成后,前端获取到最终结果并显示
|
||||
|
||||
## 优势
|
||||
|
||||
✅ **避免超时**: HTTP请求立即返回,不会因导入时间长而超时
|
||||
✅ **用户体验好**: 前端实时显示导入进度
|
||||
✅ **不阻塞服务器**: 导入任务在独立线程池中执行
|
||||
✅ **支持大批量**: 可以处理大量数据而不影响其他请求
|
||||
✅ **向后兼容**: 保留了同步导入方法,不影响其他功能
|
||||
|
||||
## 注意事项
|
||||
|
||||
⚠️ **重启应用**: 修改完成后需要重启Spring Boot应用才能生效
|
||||
⚠️ **数据库连接**: 异步线程需要数据库连接,确保连接池配置足够
|
||||
⚠️ **事务管理**: 异步方法中的事务与主线程独立,需注意事务边界
|
||||
⚠️ **线程池监控**: 生产环境建议监控线程池状态,避免资源耗尽
|
||||
|
||||
## 如何测试
|
||||
|
||||
1. 重启Spring Boot应用
|
||||
2. 准备一个包含较多数据的Excel文件(建议500+条)
|
||||
3. 在用户档案管理页面点击"导入"
|
||||
4. 上传文件后,应该立即收到"导入任务已启动"的提示
|
||||
5. 观察进度条实时更新
|
||||
6. 等待导入完成,查看结果统计
|
||||
|
||||
## 进一步优化建议(可选)
|
||||
|
||||
如果仍然遇到问题,可以考虑:
|
||||
|
||||
1. **增加Nginx超时配置** (如果使用了Nginx反向代理):
|
||||
```nginx
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
```
|
||||
|
||||
2. **优化批量插入性能**:
|
||||
- 已实现分批处理(每批500条)
|
||||
- 可以根据实际情况调整批次大小
|
||||
|
||||
3. **数据库优化**:
|
||||
- 确保相关字段有索引
|
||||
- 检查批量插入SQL性能
|
||||
|
||||
---
|
||||
|
||||
**修复完成日期**: 2025-12-01
|
||||
**修复人员**: Cascade AI
|
||||
**影响范围**: 用户档案导入功能
|
||||
**风险评估**: 低(仅改变执行方式,核心逻辑未变)
|
||||
|
|
@ -1,431 +0,0 @@
|
|||
# 🎯 内网环境ANR问题彻底解决方案
|
||||
|
||||
## 问题诊断
|
||||
|
||||
### 原始问题
|
||||
- App在内网环境下朗读按钮显示**灰色**(disabled)
|
||||
- 点击无反应,无法使用朗读功能
|
||||
- 一段时间后出现**ANR**(Application Not Responding)
|
||||
- 应用被系统强制关闭
|
||||
|
||||
### 根本原因
|
||||
1. **前端检测逻辑不完整**:只检查 `window.AndroidTTS` 是否存在,未调用 `isAvailable()` 方法
|
||||
2. **旧实现依赖外网**:使用百度在线TTS API (`https://fanyi.baidu.com/gettts`)
|
||||
3. **内网无法访问外网**:网络请求超时导致主线程阻塞
|
||||
4. **导致ANR**:长时间等待响应,系统判定应用无响应
|
||||
|
||||
---
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 核心改进
|
||||
1. **使用Android原生TextToSpeech引擎**:完全离线,不需要网络
|
||||
2. **修改前端检测逻辑**:正确调用 `isAvailable()` 方法检查TTS状态
|
||||
3. **添加延迟重试机制**:处理TTS异步初始化的时序问题
|
||||
4. **自动降级策略**:如果原生TTS不可用,自动切换到浏览器TTS
|
||||
|
||||
---
|
||||
|
||||
## 📝 已修改的文件
|
||||
|
||||
### Android代码
|
||||
|
||||
#### 1. `TtsHelper.java` - 使用原生TTS引擎
|
||||
**文件位置**:`xinli-App/app/src/main/java/com/xinli/app/TtsHelper.java`
|
||||
|
||||
**核心改动**:
|
||||
```java
|
||||
// 使用Android原生TextToSpeech引擎
|
||||
private TextToSpeech tts;
|
||||
|
||||
private void initTts() {
|
||||
tts = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
|
||||
@Override
|
||||
public void onInit(int status) {
|
||||
if (status == TextToSpeech.SUCCESS) {
|
||||
// 设置中文语言
|
||||
int result = tts.setLanguage(Locale.CHINESE);
|
||||
isReady = true;
|
||||
Log.i(TAG, "✅ Android原生TTS初始化成功(离线)");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void speak(String text) {
|
||||
tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, utteranceId);
|
||||
}
|
||||
```
|
||||
|
||||
**特性**:
|
||||
- ✅ 完全离线工作
|
||||
- ✅ 使用系统内置TTS引擎
|
||||
- ✅ 支持中文朗读
|
||||
- ✅ 无网络超时风险
|
||||
- ✅ 不会导致ANR
|
||||
|
||||
### 前端代码
|
||||
|
||||
#### 2. `assessment/taking.vue` - 测评答题页面
|
||||
**文件位置**:`xinli-ui/src/views/psychology/assessment/taking.vue`
|
||||
|
||||
**核心改动**:
|
||||
```javascript
|
||||
initTts() {
|
||||
// 检测 Android App 环境
|
||||
if (window.AndroidTTS && typeof window.AndroidTTS.isAvailable === 'function') {
|
||||
// 调用 isAvailable() 方法检查 TTS 是否真的可用
|
||||
if (window.AndroidTTS.isAvailable()) {
|
||||
this.isTtsSupported = true;
|
||||
this.useAndroidTts = true;
|
||||
console.log('✅ 使用Android原生TTS');
|
||||
return;
|
||||
} else {
|
||||
// TTS 可能还在初始化中,延迟重试
|
||||
console.log('⏳ Android TTS尚未就绪,延迟检查...');
|
||||
setTimeout(() => {
|
||||
if (window.AndroidTTS && window.AndroidTTS.isAvailable()) {
|
||||
this.isTtsSupported = true;
|
||||
this.useAndroidTts = true;
|
||||
console.log('✅ Android TTS 已就绪(延迟检测)');
|
||||
} else {
|
||||
this.fallbackToBrowserTts();
|
||||
}
|
||||
}, 500);
|
||||
// 先启用按钮,避免用户等待
|
||||
this.isTtsSupported = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 浏览器环境
|
||||
this.fallbackToBrowserTts();
|
||||
}
|
||||
```
|
||||
|
||||
**特性**:
|
||||
- ✅ 调用 `isAvailable()` 验证TTS可用性
|
||||
- ✅ 500ms延迟重试处理异步初始化
|
||||
- ✅ 立即启用按钮,提升用户体验
|
||||
- ✅ 自动降级到浏览器TTS
|
||||
|
||||
#### 3. `questionnaire/taking.vue` - 问卷答题页面
|
||||
**文件位置**:`xinli-ui/src/views/psychology/questionnaire/taking.vue`
|
||||
|
||||
**改动**:与测评页面保持一致的检测逻辑
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署步骤
|
||||
|
||||
### 第1步:重新构建前端
|
||||
|
||||
```bash
|
||||
cd c:\Users\Administrator\Desktop\Project\xinli\xinli-ui
|
||||
npm run build:prod
|
||||
```
|
||||
|
||||
**耗时**:约2-5分钟
|
||||
|
||||
### 第2步:部署前端到服务器
|
||||
|
||||
将 `xinli-ui/dist` 目录的所有文件复制到服务器的Web根目录。
|
||||
|
||||
### 第3步:打包Android APK
|
||||
|
||||
```bash
|
||||
cd c:\Users\Administrator\Desktop\Project\xinli\xinli-App
|
||||
.\打包正式版APK.bat
|
||||
```
|
||||
|
||||
**耗时**:约3-5分钟
|
||||
|
||||
### 第4步:查找生成的APK
|
||||
|
||||
```bash
|
||||
.\查找APK.bat
|
||||
```
|
||||
|
||||
APK文件位置:
|
||||
```
|
||||
xinli-App\app\build\outputs\apk\release\app-release.apk
|
||||
```
|
||||
|
||||
### 第5步:部署到设备
|
||||
|
||||
#### 重要:必须先卸载旧版本!
|
||||
|
||||
**方法1:通过手机设置卸载**
|
||||
1. 设置 → 应用管理 → 找到"心理测评"App
|
||||
2. 点击"卸载"
|
||||
|
||||
**方法2:使用ADB命令**
|
||||
```bash
|
||||
adb uninstall com.xinli.app
|
||||
```
|
||||
|
||||
#### 安装新版APK
|
||||
|
||||
**方法1:直接安装**
|
||||
1. 将APK文件复制到手机
|
||||
2. 点击文件安装
|
||||
|
||||
**方法2:使用ADB安装**
|
||||
```bash
|
||||
adb install app\build\outputs\apk\release\app-release.apk
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 验证测试
|
||||
|
||||
### 测试清单
|
||||
|
||||
#### 基础功能测试
|
||||
- [ ] App能正常启动,无崩溃
|
||||
- [ ] 能正常登录
|
||||
- [ ] 进入答题页面正常
|
||||
|
||||
#### TTS功能测试
|
||||
- [ ] 朗读按钮**不是灰色**(可点击)
|
||||
- [ ] 点击"朗读全部"能听到声音
|
||||
- [ ] 点击"朗读题干"能听到声音
|
||||
- [ ] 点击选项旁的朗读按钮能听到声音
|
||||
- [ ] 可以正常停止朗读
|
||||
- [ ] 切换题目后朗读功能正常
|
||||
|
||||
#### ANR问题测试
|
||||
- [ ] **没有出现"应用无响应"对话框**
|
||||
- [ ] 长时间使用不会卡死
|
||||
- [ ] 快速点击朗读按钮不会崩溃
|
||||
|
||||
### 查看日志(可选)
|
||||
|
||||
使用ADB查看TTS日志:
|
||||
|
||||
```bash
|
||||
adb logcat | findstr TtsHelper
|
||||
```
|
||||
|
||||
**预期日志**:
|
||||
```
|
||||
TtsHelper: ✅ Android原生TTS初始化成功(离线)
|
||||
TtsHelper: isAvailable: true
|
||||
TtsHelper: ✅ 开始朗读: ...
|
||||
```
|
||||
|
||||
**前端控制台日志**:
|
||||
```
|
||||
✅ 使用Android原生TTS
|
||||
```
|
||||
或
|
||||
```
|
||||
⏳ Android TTS尚未就绪,延迟检查...
|
||||
✅ Android TTS 已就绪(延迟检测)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 故障排除
|
||||
|
||||
### 问题1:按钮仍然是灰色
|
||||
|
||||
**原因**:未卸载旧版App,代码未更新
|
||||
|
||||
**解决**:
|
||||
1. 完全卸载旧版App
|
||||
2. 清除应用数据
|
||||
3. 重新安装新APK
|
||||
4. 重启App
|
||||
|
||||
### 问题2:没有声音
|
||||
|
||||
**检查项**:
|
||||
1. **手机媒体音量**:确保不是0(注意不是铃声音量)
|
||||
2. **TTS引擎**:检查系统是否安装了中文TTS引擎
|
||||
- 设置 → 辅助功能 → 文字转语音
|
||||
- 确认有可用的TTS引擎
|
||||
3. **查看日志**:运行 `adb logcat | findstr TTS` 查看错误信息
|
||||
|
||||
**改善音质**:
|
||||
- 在手机设置中安装高质量TTS引擎
|
||||
- 推荐:Google文字转语音引擎
|
||||
|
||||
### 问题3:仍然出现ANR
|
||||
|
||||
**检查**:
|
||||
1. 确认安装的是新版APK(查看APK生成时间)
|
||||
2. 确认前端代码已更新(检查dist目录修改时间)
|
||||
3. 查看日志确认使用的是原生TTS:
|
||||
```bash
|
||||
adb logcat | findstr "Android原生TTS"
|
||||
```
|
||||
|
||||
### 问题4:部分设备不支持中文TTS
|
||||
|
||||
**解决**:
|
||||
1. 引导用户安装中文TTS引擎
|
||||
2. 或在App中集成离线TTS SDK(如讯飞、百度)
|
||||
|
||||
---
|
||||
|
||||
## 📊 修复对比
|
||||
|
||||
| 项目 | 修复前 | 修复后 |
|
||||
|------|--------|--------|
|
||||
| **朗读按钮状态** | ❌ 灰色禁用 | ✅ 正常可用 |
|
||||
| **网络依赖** | ❌ 需要外网访问百度API | ✅ 完全离线 |
|
||||
| **ANR问题** | ❌ 内网环境会ANR | ✅ 不会ANR |
|
||||
| **TTS引擎** | ❌ 在线API | ✅ Android原生引擎 |
|
||||
| **音质** | 一般(百度API) | 良好(取决于设备) |
|
||||
| **检测逻辑** | ❌ 只检查对象存在 | ✅ 调用isAvailable()验证 |
|
||||
| **降级策略** | ❌ 无 | ✅ 自动降级到浏览器TTS |
|
||||
| **初始化处理** | ❌ 无延迟重试 | ✅ 500ms延迟重试 |
|
||||
|
||||
---
|
||||
|
||||
## 🎉 技术优势
|
||||
|
||||
### 1. 完全离线
|
||||
- 不依赖任何外部API
|
||||
- 内网环境正常工作
|
||||
- 无网络超时风险
|
||||
|
||||
### 2. 高可用性
|
||||
- 使用系统内置TTS引擎
|
||||
- 双重保障:原生TTS + 浏览器TTS
|
||||
- 异步初始化不阻塞UI
|
||||
|
||||
### 3. 用户体验
|
||||
- 朗读按钮立即可用
|
||||
- 无需等待网络响应
|
||||
- 不会出现ANR
|
||||
|
||||
### 4. 维护性
|
||||
- 代码简洁清晰
|
||||
- 统一的检测逻辑
|
||||
- 完善的日志输出
|
||||
|
||||
---
|
||||
|
||||
## 📚 技术细节
|
||||
|
||||
### Android原生TTS工作流程
|
||||
|
||||
```
|
||||
1. App启动
|
||||
↓
|
||||
2. 初始化TextToSpeech引擎(异步)
|
||||
↓
|
||||
3. onInit回调 → 设置中文语言
|
||||
↓
|
||||
4. 设置isReady = true
|
||||
↓
|
||||
5. 前端调用isAvailable() → 返回true
|
||||
↓
|
||||
6. 前端启用朗读按钮
|
||||
↓
|
||||
7. 用户点击朗读 → 调用speak()
|
||||
↓
|
||||
8. TTS引擎合成语音并播放
|
||||
```
|
||||
|
||||
### 前端检测流程
|
||||
|
||||
```
|
||||
1. 页面加载 → initTts()
|
||||
↓
|
||||
2. 检查window.AndroidTTS是否存在
|
||||
↓
|
||||
3. 调用AndroidTTS.isAvailable()
|
||||
↓
|
||||
4. 如果返回false → 延迟500ms重试
|
||||
↓
|
||||
5. 如果仍然false → 降级到浏览器TTS
|
||||
↓
|
||||
6. 设置isTtsSupported = true
|
||||
↓
|
||||
7. 朗读按钮可用
|
||||
```
|
||||
|
||||
### 为什么需要延迟重试?
|
||||
|
||||
Android的TextToSpeech初始化是**异步**的:
|
||||
- `new TextToSpeech()` 立即返回
|
||||
- 实际初始化在后台进行
|
||||
- `onInit()` 回调可能需要100-500ms
|
||||
- 前端页面加载可能比TTS初始化更快
|
||||
|
||||
**解决方案**:
|
||||
- 第一次检查失败 → 延迟500ms重试
|
||||
- 同时先启用按钮,提升用户体验
|
||||
- 用户点击时如果还未就绪 → 内部再延迟重试
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. 必须卸载旧版
|
||||
新APK的签名可能与旧版不同,直接安装可能失败或代码不更新。
|
||||
|
||||
### 2. 前端也需要更新
|
||||
只更新APK不够,前端检测逻辑也必须更新,否则按钮仍然是灰色。
|
||||
|
||||
### 3. 设备TTS引擎差异
|
||||
不同品牌手机的TTS引擎音质有差异:
|
||||
- **华为/小米/OPPO**:通常有高质量中文TTS
|
||||
- **三星**:音质良好
|
||||
- **低端设备**:可能音质较差或不支持中文
|
||||
|
||||
### 4. 浏览器版本不受影响
|
||||
- Web版(PC浏览器)继续使用浏览器的Web Speech API
|
||||
- 修改仅影响Android App
|
||||
|
||||
---
|
||||
|
||||
## 🔄 未来优化方向
|
||||
|
||||
### 短期(可选)
|
||||
1. **集成高质量离线TTS SDK**
|
||||
- 讯飞语音SDK
|
||||
- 百度语音SDK
|
||||
- 提供更好的音质和更多音色选择
|
||||
|
||||
### 长期(可选)
|
||||
2. **支持语速/音调调节**
|
||||
- 在App中添加TTS设置界面
|
||||
- 让用户自定义语音参数
|
||||
|
||||
3. **多语言支持**
|
||||
- 检测题目语言
|
||||
- 自动切换TTS语言
|
||||
|
||||
---
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如果遇到问题:
|
||||
|
||||
1. **查看日志**
|
||||
```bash
|
||||
adb logcat | findstr "TtsHelper\|AndroidTTS"
|
||||
```
|
||||
|
||||
2. **检查TTS引擎**
|
||||
- 手机设置 → 辅助功能 → 文字转语音
|
||||
- 确认有可用的中文TTS引擎
|
||||
|
||||
3. **验证安装**
|
||||
```bash
|
||||
# 查看已安装App版本
|
||||
adb shell pm list packages -f | findstr xinli
|
||||
|
||||
# 查看APK信息
|
||||
adb shell dumpsys package com.xinli.app | findstr version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**修复完成日期**:2025-01-27
|
||||
**测试状态**:✅ 待部署验证
|
||||
**适用环境**:内网/外网均可使用
|
||||
|
|
@ -1,253 +0,0 @@
|
|||
-- ========================================
|
||||
-- 删除今天导入的用户数据 SQL脚本
|
||||
-- 创建时间: 2025-12-01
|
||||
-- 警告: 执行前请务必备份数据库!
|
||||
-- ========================================
|
||||
|
||||
-- ========================================
|
||||
-- 第一步: 查询今天导入的用户(先确认数量)
|
||||
-- ========================================
|
||||
|
||||
-- 1. 查看今天创建的用户档案数量
|
||||
SELECT
|
||||
COUNT(*) AS '今天创建的用户档案数',
|
||||
MIN(create_time) AS '最早创建时间',
|
||||
MAX(create_time) AS '最晚创建时间'
|
||||
FROM psy_user_profile
|
||||
WHERE DATE(create_time) = CURDATE();
|
||||
|
||||
-- 2. 查看今天创建的用户档案详情(前10条,检查是否是要删除的数据)
|
||||
SELECT
|
||||
profile_id,
|
||||
user_id,
|
||||
info_number,
|
||||
user_name,
|
||||
prison_area,
|
||||
create_time,
|
||||
create_by
|
||||
FROM psy_user_profile
|
||||
WHERE DATE(create_time) = CURDATE()
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 10;
|
||||
|
||||
-- 3. 查看今天创建的系统用户数量
|
||||
SELECT
|
||||
COUNT(*) AS '今天创建的系统用户数',
|
||||
MIN(create_time) AS '最早创建时间',
|
||||
MAX(create_time) AS '最晚创建时间'
|
||||
FROM sys_user
|
||||
WHERE DATE(create_time) = CURDATE()
|
||||
AND user_id > 1; -- 排除管理员账户
|
||||
|
||||
-- ========================================
|
||||
-- 第二步: 数据备份(强烈推荐!)
|
||||
-- ========================================
|
||||
|
||||
-- 创建备份表
|
||||
CREATE TABLE IF NOT EXISTS psy_user_profile_backup_20251201 AS
|
||||
SELECT * FROM psy_user_profile
|
||||
WHERE DATE(create_time) = CURDATE();
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sys_user_backup_20251201 AS
|
||||
SELECT * FROM sys_user
|
||||
WHERE DATE(create_time) = CURDATE()
|
||||
AND user_id > 1;
|
||||
|
||||
-- 验证备份
|
||||
SELECT COUNT(*) AS '备份的用户档案数' FROM psy_user_profile_backup_20251201;
|
||||
SELECT COUNT(*) AS '备份的系统用户数' FROM sys_user_backup_20251201;
|
||||
|
||||
-- ========================================
|
||||
-- 第三步: 删除关联数据(外键关联)
|
||||
-- ========================================
|
||||
|
||||
-- 3.1 删除今天创建用户的测评记录
|
||||
-- 警告:这会删除这些用户的所有测评数据!
|
||||
DELETE FROM psy_assessment_answer
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
WHERE DATE(p.create_time) = CURDATE()
|
||||
);
|
||||
|
||||
DELETE FROM psy_factor_score
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
WHERE DATE(p.create_time) = CURDATE()
|
||||
);
|
||||
|
||||
DELETE FROM psy_assessment_warning
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
WHERE DATE(p.create_time) = CURDATE()
|
||||
);
|
||||
|
||||
DELETE FROM psy_assessment_report
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
WHERE DATE(p.create_time) = CURDATE()
|
||||
);
|
||||
|
||||
DELETE FROM psy_assessment
|
||||
WHERE target_user_id IN (
|
||||
SELECT user_id FROM psy_user_profile
|
||||
WHERE DATE(create_time) = CURDATE()
|
||||
);
|
||||
|
||||
-- 3.2 删除量表权限
|
||||
DELETE FROM psy_scale_permission
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM psy_user_profile
|
||||
WHERE DATE(create_time) = CURDATE()
|
||||
);
|
||||
|
||||
-- 3.3 删除问卷答题记录
|
||||
DELETE FROM psy_questionnaire_answer_detail
|
||||
WHERE answer_id IN (
|
||||
SELECT qa.answer_id
|
||||
FROM psy_questionnaire_answer qa
|
||||
INNER JOIN psy_user_profile p ON qa.user_id = p.user_id
|
||||
WHERE DATE(p.create_time) = CURDATE()
|
||||
);
|
||||
|
||||
DELETE FROM psy_questionnaire_answer
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM psy_user_profile
|
||||
WHERE DATE(create_time) = CURDATE()
|
||||
);
|
||||
|
||||
-- 3.4 删除用户角色关联
|
||||
DELETE FROM sys_user_role
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM psy_user_profile
|
||||
WHERE DATE(create_time) = CURDATE()
|
||||
);
|
||||
|
||||
-- 3.5 删除用户岗位关联
|
||||
DELETE FROM sys_user_post
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM psy_user_profile
|
||||
WHERE DATE(create_time) = CURDATE()
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- 第四步: 删除用户档案和系统用户
|
||||
-- ========================================
|
||||
|
||||
-- 4.1 删除用户档案
|
||||
DELETE FROM psy_user_profile
|
||||
WHERE DATE(create_time) = CURDATE();
|
||||
|
||||
-- 4.2 删除系统用户(对应的sys_user表)
|
||||
DELETE FROM sys_user
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM psy_user_profile_backup_20251201
|
||||
)
|
||||
AND user_id > 1; -- 保护管理员账户
|
||||
|
||||
-- ========================================
|
||||
-- 第五步: 验证删除结果
|
||||
-- ========================================
|
||||
|
||||
-- 验证今天创建的用户是否已删除
|
||||
SELECT COUNT(*) AS '剩余今天创建的用户档案数'
|
||||
FROM psy_user_profile
|
||||
WHERE DATE(create_time) = CURDATE();
|
||||
|
||||
SELECT COUNT(*) AS '剩余今天创建的系统用户数'
|
||||
FROM sys_user
|
||||
WHERE DATE(create_time) = CURDATE()
|
||||
AND user_id > 1;
|
||||
|
||||
-- 查看备份表中的数据(确认备份成功)
|
||||
SELECT COUNT(*) AS '备份的记录数' FROM psy_user_profile_backup_20251201;
|
||||
SELECT * FROM psy_user_profile_backup_20251201 LIMIT 5;
|
||||
|
||||
-- ========================================
|
||||
-- 恢复数据脚本(如果需要回滚)
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
-- 恢复用户档案
|
||||
INSERT INTO psy_user_profile
|
||||
SELECT * FROM psy_user_profile_backup_20251201;
|
||||
|
||||
-- 恢复系统用户
|
||||
INSERT INTO sys_user
|
||||
SELECT * FROM sys_user_backup_20251201;
|
||||
|
||||
-- 注意:恢复后需要手动恢复关联数据,建议在删除前做完整数据库备份!
|
||||
*/
|
||||
|
||||
-- ========================================
|
||||
-- 清理备份表(确认数据无误后可执行)
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
DROP TABLE IF EXISTS psy_user_profile_backup_20251201;
|
||||
DROP TABLE IF EXISTS sys_user_backup_20251201;
|
||||
*/
|
||||
|
||||
-- ========================================
|
||||
-- 执行说明
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
执行顺序:
|
||||
1. 先执行"第一步"的查询,确认是否是要删除的数据
|
||||
2. 执行"第二步"创建备份(必须!)
|
||||
3. 验证备份数据
|
||||
4. 执行"第三步"删除关联数据
|
||||
5. 执行"第四步"删除主表数据
|
||||
6. 执行"第五步"验证删除结果
|
||||
7. 如果出错,使用恢复脚本回滚
|
||||
|
||||
注意事项:
|
||||
1. 务必在测试环境先执行一遍
|
||||
2. 生产环境执行前做完整数据库备份
|
||||
3. 建议在业务低峰期执行
|
||||
4. 执行前通知相关人员
|
||||
5. 保留备份表至少7天
|
||||
6. 如果有其他关联表,需要补充删除语句
|
||||
*/
|
||||
|
||||
-- ========================================
|
||||
-- 更安全的分步执行方案
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
-- 方案A:只删除特定时间段的用户(更精确)
|
||||
-- 例如:删除今天上午10点到11点导入的用户
|
||||
|
||||
SELECT COUNT(*) FROM psy_user_profile
|
||||
WHERE create_time >= '2025-12-01 10:00:00'
|
||||
AND create_time < '2025-12-01 11:00:00';
|
||||
|
||||
-- 然后修改上面的WHERE条件为:
|
||||
-- WHERE create_time >= '2025-12-01 10:00:00' AND create_time < '2025-12-01 11:00:00'
|
||||
*/
|
||||
|
||||
/*
|
||||
-- 方案B:只删除特定创建人导入的用户(更精确)
|
||||
SELECT COUNT(*) FROM psy_user_profile
|
||||
WHERE DATE(create_time) = CURDATE()
|
||||
AND create_by = 'admin'; -- 替换为实际的创建人用户名
|
||||
|
||||
-- 然后在WHERE条件中增加:
|
||||
-- AND create_by = 'admin'
|
||||
*/
|
||||
|
||||
/*
|
||||
-- 方案C:根据信息编号范围删除(如果有规律)
|
||||
SELECT COUNT(*) FROM psy_user_profile
|
||||
WHERE info_number LIKE 'XXX%'; -- 替换为实际的编号前缀
|
||||
|
||||
-- 然后修改WHERE条件
|
||||
*/
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
-- ========================================
|
||||
-- 删除姓名为纯数字的用户数据 SQL脚本
|
||||
-- 创建时间: 2025-12-01
|
||||
-- 警告: 执行前请务必备份数据库!
|
||||
-- ========================================
|
||||
|
||||
-- ========================================
|
||||
-- 第一步: 查询姓名为纯数字的用户(先确认数量)
|
||||
-- ========================================
|
||||
|
||||
-- 1. 查看姓名为纯数字的用户档案数量
|
||||
SELECT
|
||||
COUNT(*) AS '姓名为纯数字的用户数',
|
||||
MIN(p.create_time) AS '最早创建时间',
|
||||
MAX(p.create_time) AS '最晚创建时间'
|
||||
FROM psy_user_profile p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$';
|
||||
|
||||
-- 2. 查看姓名为纯数字的用户档案详情(检查是否是要删除的数据)
|
||||
SELECT
|
||||
profile_id,
|
||||
user_id,
|
||||
info_number,
|
||||
user_name,
|
||||
prison_area,
|
||||
create_time,
|
||||
create_by
|
||||
FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]+$'
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 20;
|
||||
|
||||
-- 3. 查看这些用户在sys_user表中的对应记录
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.user_name,
|
||||
u.nick_name,
|
||||
u.create_time
|
||||
FROM sys_user u
|
||||
INNER JOIN psy_user_profile p ON u.user_id = p.user_id
|
||||
WHERE p.user_name REGEXP '^[0-9]+$'
|
||||
AND u.user_id > 1; -- 排除管理员账户
|
||||
|
||||
-- ========================================
|
||||
-- 第二步: 数据备份(强烈推荐!)
|
||||
-- ========================================
|
||||
|
||||
-- 创建备份表
|
||||
CREATE TABLE IF NOT EXISTS psy_user_profile_backup_numeric_names AS
|
||||
SELECT * FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]+$';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sys_user_backup_numeric_names AS
|
||||
SELECT u.* FROM sys_user u
|
||||
INNER JOIN psy_user_profile p ON u.user_id = p.user_id
|
||||
WHERE p.user_name REGEXP '^[0-9]+$'
|
||||
AND u.user_id > 1;
|
||||
|
||||
-- 验证备份
|
||||
SELECT COUNT(*) AS '备份的用户档案数' FROM psy_user_profile_backup_numeric_names;
|
||||
SELECT COUNT(*) AS '备份的系统用户数' FROM sys_user_backup_numeric_names;
|
||||
|
||||
-- 查看备份的具体数据
|
||||
SELECT profile_id, user_id, user_name, info_number FROM psy_user_profile_backup_numeric_names LIMIT 10;
|
||||
|
||||
-- ========================================
|
||||
-- 第三步: 删除关联数据(外键关联)
|
||||
-- ========================================
|
||||
|
||||
-- 3.1 删除测评答题详情
|
||||
DELETE FROM psy_assessment_answer
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
WHERE p.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.2 删除因子得分
|
||||
DELETE FROM psy_factor_score
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
WHERE p.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.3 删除测评预警
|
||||
DELETE FROM psy_assessment_warning
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
WHERE p.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.4 删除测评报告
|
||||
DELETE FROM psy_assessment_report
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
WHERE p.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.5 删除测评记录
|
||||
DELETE FROM psy_assessment
|
||||
WHERE target_user_id IN (
|
||||
SELECT user_id FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.6 删除量表权限
|
||||
DELETE FROM psy_scale_permission
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.7 删除问卷答题详情
|
||||
DELETE FROM psy_questionnaire_answer_detail
|
||||
WHERE answer_id IN (
|
||||
SELECT qa.answer_id
|
||||
FROM psy_questionnaire_answer qa
|
||||
INNER JOIN psy_user_profile p ON qa.user_id = p.user_id
|
||||
WHERE p.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.8 删除问卷答题记录
|
||||
DELETE FROM psy_questionnaire_answer
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.9 删除用户角色关联
|
||||
DELETE FROM sys_user_role
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.10 删除用户岗位关联
|
||||
DELETE FROM sys_user_post
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- 第四步: 删除用户档案和系统用户
|
||||
-- ========================================
|
||||
|
||||
-- 4.1 删除用户档案
|
||||
DELETE FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]+$';
|
||||
|
||||
-- 4.2 删除系统用户(使用备份表中的user_id)
|
||||
DELETE FROM sys_user
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM sys_user_backup_numeric_names
|
||||
)
|
||||
AND user_id > 1; -- 保护管理员账户
|
||||
|
||||
-- ========================================
|
||||
-- 第五步: 验证删除结果
|
||||
-- ========================================
|
||||
|
||||
-- 验证姓名为纯数字的用户是否已删除
|
||||
SELECT COUNT(*) AS '剩余姓名为数字的用户档案数'
|
||||
FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]+$';
|
||||
|
||||
SELECT COUNT(*) AS '剩余姓名为数字的系统用户数'
|
||||
FROM sys_user u
|
||||
INNER JOIN psy_user_profile p ON u.user_id = p.user_id
|
||||
WHERE p.user_name REGEXP '^[0-9]+$'
|
||||
AND u.user_id > 1;
|
||||
|
||||
-- 查看备份表中的数据(确认备份成功)
|
||||
SELECT COUNT(*) AS '备份的档案记录数' FROM psy_user_profile_backup_numeric_names;
|
||||
SELECT COUNT(*) AS '备份的系统用户记录数' FROM sys_user_backup_numeric_names;
|
||||
|
||||
-- 查看备份的示例数据
|
||||
SELECT * FROM psy_user_profile_backup_numeric_names LIMIT 5;
|
||||
SELECT * FROM sys_user_backup_numeric_names LIMIT 5;
|
||||
|
||||
-- ========================================
|
||||
-- 恢复数据脚本(如果需要回滚)
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
-- 恢复用户档案
|
||||
INSERT INTO psy_user_profile
|
||||
SELECT * FROM psy_user_profile_backup_numeric_names;
|
||||
|
||||
-- 恢复系统用户
|
||||
INSERT INTO sys_user
|
||||
SELECT * FROM sys_user_backup_numeric_names;
|
||||
|
||||
-- 注意:恢复后需要手动恢复关联数据,建议在删除前做完整数据库备份!
|
||||
*/
|
||||
|
||||
-- ========================================
|
||||
-- 清理备份表(确认数据无误后可执行)
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
DROP TABLE IF EXISTS psy_user_profile_backup_numeric_names;
|
||||
DROP TABLE IF EXISTS sys_user_backup_numeric_names;
|
||||
*/
|
||||
|
||||
-- ========================================
|
||||
-- 执行说明
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
执行顺序:
|
||||
1. 先执行"第一步"的查询,确认是否是要删除的数据(检查姓名字段)
|
||||
2. 执行"第二步"创建备份(必须!)
|
||||
3. 验证备份数据
|
||||
4. 执行"第三步"删除关联数据(一条一条执行,观察影响行数)
|
||||
5. 执行"第四步"删除主表数据
|
||||
6. 执行"第五步"验证删除结果
|
||||
7. 如果出错,使用恢复脚本回滚
|
||||
|
||||
注意事项:
|
||||
1. 务必在测试环境先执行一遍
|
||||
2. 生产环境执行前做完整数据库备份
|
||||
3. 建议在业务低峰期执行
|
||||
4. 执行前通知相关人员
|
||||
5. 保留备份表至少7天
|
||||
6. 正则表达式 '^[0-9]+$' 匹配纯数字(只包含0-9的字符串)
|
||||
7. 如果有其他关联表,需要补充删除语句
|
||||
|
||||
判断规则:
|
||||
- '^[0-9]+$' 表示从开头到结尾都是数字
|
||||
- 例如:'123'、'456' 会被匹配
|
||||
- 例如:'张三'、'123abc'、'李4' 不会被匹配
|
||||
*/
|
||||
|
||||
-- ========================================
|
||||
-- 更精确的匹配方案(可选)
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
-- 方案A:只删除纯数字且长度在特定范围的姓名
|
||||
-- 例如:只删除3-10位纯数字的姓名
|
||||
SELECT COUNT(*) FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]{3,10}$';
|
||||
|
||||
-- 方案B:排除某些特定的数字姓名(如果有合法的数字姓名)
|
||||
SELECT COUNT(*) FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]+$'
|
||||
AND user_name NOT IN ('001', '002'); -- 排除这些合法的数字姓名
|
||||
|
||||
-- 方案C:只删除特定区域的数字姓名用户
|
||||
SELECT COUNT(*) FROM psy_user_profile
|
||||
WHERE user_name REGEXP '^[0-9]+$'
|
||||
AND prison_area = '某监区'; -- 替换为实际的监区名称
|
||||
*/
|
||||
|
|
@ -1,287 +0,0 @@
|
|||
-- ========================================
|
||||
-- 删除姓名为纯数字的用户数据 SQL脚本(修正版)
|
||||
-- 创建时间: 2025-12-01
|
||||
-- 警告: 执行前请务必备份数据库!
|
||||
-- 说明: user_name字段在sys_user表中,不在psy_user_profile表中
|
||||
-- ========================================
|
||||
|
||||
-- ========================================
|
||||
-- 第一步: 查询姓名为纯数字的用户(先确认数量)
|
||||
-- ========================================
|
||||
|
||||
-- 1. 查看姓名为纯数字的用户档案数量
|
||||
SELECT
|
||||
COUNT(*) AS '姓名为纯数字的用户数',
|
||||
MIN(p.create_time) AS '最早创建时间',
|
||||
MAX(p.create_time) AS '最晚创建时间'
|
||||
FROM psy_user_profile p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$';
|
||||
|
||||
-- 2. 查看姓名为纯数字的用户档案详情(检查是否是要删除的数据)
|
||||
SELECT
|
||||
p.profile_id,
|
||||
p.user_id,
|
||||
p.info_number,
|
||||
u.user_name,
|
||||
p.prison_area,
|
||||
p.create_time,
|
||||
p.create_by
|
||||
FROM psy_user_profile p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
ORDER BY p.create_time DESC
|
||||
LIMIT 20;
|
||||
|
||||
-- 3. 查看这些用户在sys_user表中的对应记录
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.user_name,
|
||||
u.nick_name,
|
||||
u.create_time,
|
||||
p.info_number
|
||||
FROM sys_user u
|
||||
INNER JOIN psy_user_profile p ON u.user_id = p.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
AND u.user_id > 1; -- 排除管理员账户
|
||||
|
||||
-- ========================================
|
||||
-- 第二步: 数据备份(强烈推荐!)
|
||||
-- ========================================
|
||||
|
||||
-- 创建备份表
|
||||
CREATE TABLE IF NOT EXISTS psy_user_profile_backup_numeric_names AS
|
||||
SELECT p.* FROM psy_user_profile p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sys_user_backup_numeric_names AS
|
||||
SELECT u.* FROM sys_user u
|
||||
INNER JOIN psy_user_profile p ON u.user_id = p.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
AND u.user_id > 1;
|
||||
|
||||
-- 验证备份
|
||||
SELECT COUNT(*) AS '备份的用户档案数' FROM psy_user_profile_backup_numeric_names;
|
||||
SELECT COUNT(*) AS '备份的系统用户数' FROM sys_user_backup_numeric_names;
|
||||
|
||||
-- 查看备份的具体数据
|
||||
SELECT profile_id, user_id, info_number FROM psy_user_profile_backup_numeric_names LIMIT 10;
|
||||
SELECT user_id, user_name, nick_name FROM sys_user_backup_numeric_names LIMIT 10;
|
||||
|
||||
-- ========================================
|
||||
-- 第三步: 删除关联数据(外键关联)
|
||||
-- ========================================
|
||||
|
||||
-- 3.1 删除测评答题详情
|
||||
DELETE FROM psy_assessment_answer
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.2 删除因子得分
|
||||
DELETE FROM psy_factor_score
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.3 删除测评预警
|
||||
DELETE FROM psy_assessment_warning
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.4 删除测评报告
|
||||
DELETE FROM psy_assessment_report
|
||||
WHERE assessment_id IN (
|
||||
SELECT a.assessment_id
|
||||
FROM psy_assessment a
|
||||
INNER JOIN psy_user_profile p ON a.target_user_id = p.user_id
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.5 删除测评记录
|
||||
DELETE FROM psy_assessment
|
||||
WHERE target_user_id IN (
|
||||
SELECT p.user_id FROM psy_user_profile p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.6 删除量表权限
|
||||
DELETE FROM psy_scale_permission
|
||||
WHERE user_id IN (
|
||||
SELECT p.user_id FROM psy_user_profile p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.7 删除问卷答题详情
|
||||
DELETE FROM psy_questionnaire_answer_detail
|
||||
WHERE answer_id IN (
|
||||
SELECT qa.answer_id
|
||||
FROM psy_questionnaire_answer qa
|
||||
INNER JOIN psy_user_profile p ON qa.user_id = p.user_id
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.8 删除问卷答题记录
|
||||
DELETE FROM psy_questionnaire_answer
|
||||
WHERE user_id IN (
|
||||
SELECT p.user_id FROM psy_user_profile p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.9 删除用户角色关联
|
||||
DELETE FROM sys_user_role
|
||||
WHERE user_id IN (
|
||||
SELECT p.user_id FROM psy_user_profile p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- 3.10 删除用户岗位关联
|
||||
DELETE FROM sys_user_post
|
||||
WHERE user_id IN (
|
||||
SELECT p.user_id FROM psy_user_profile p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- 第四步: 删除用户档案和系统用户
|
||||
-- ========================================
|
||||
|
||||
-- 4.1 删除用户档案
|
||||
DELETE FROM psy_user_profile
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM sys_user_backup_numeric_names
|
||||
);
|
||||
|
||||
-- 4.2 删除系统用户(使用备份表中的user_id)
|
||||
DELETE FROM sys_user
|
||||
WHERE user_id IN (
|
||||
SELECT user_id FROM sys_user_backup_numeric_names
|
||||
)
|
||||
AND user_id > 1; -- 保护管理员账户
|
||||
|
||||
-- ========================================
|
||||
-- 第五步: 验证删除结果
|
||||
-- ========================================
|
||||
|
||||
-- 验证姓名为纯数字的用户是否已删除
|
||||
SELECT COUNT(*) AS '剩余姓名为数字的用户档案数'
|
||||
FROM psy_user_profile p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$';
|
||||
|
||||
SELECT COUNT(*) AS '剩余姓名为数字的系统用户数'
|
||||
FROM sys_user
|
||||
WHERE user_name REGEXP '^[0-9]+$'
|
||||
AND user_id > 1;
|
||||
|
||||
-- 查看备份表中的数据(确认备份成功)
|
||||
SELECT COUNT(*) AS '备份的档案记录数' FROM psy_user_profile_backup_numeric_names;
|
||||
SELECT COUNT(*) AS '备份的系统用户记录数' FROM sys_user_backup_numeric_names;
|
||||
|
||||
-- 查看备份的示例数据
|
||||
SELECT * FROM psy_user_profile_backup_numeric_names LIMIT 5;
|
||||
SELECT * FROM sys_user_backup_numeric_names LIMIT 5;
|
||||
|
||||
-- ========================================
|
||||
-- 恢复数据脚本(如果需要回滚)
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
-- 恢复用户档案
|
||||
INSERT INTO psy_user_profile
|
||||
SELECT * FROM psy_user_profile_backup_numeric_names;
|
||||
|
||||
-- 恢复系统用户
|
||||
INSERT INTO sys_user
|
||||
SELECT * FROM sys_user_backup_numeric_names;
|
||||
|
||||
-- 注意:恢复后需要手动恢复关联数据,建议在删除前做完整数据库备份!
|
||||
*/
|
||||
|
||||
-- ========================================
|
||||
-- 清理备份表(确认数据无误后可执行)
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
DROP TABLE IF EXISTS psy_user_profile_backup_numeric_names;
|
||||
DROP TABLE IF EXISTS sys_user_backup_numeric_names;
|
||||
*/
|
||||
|
||||
-- ========================================
|
||||
-- 执行说明
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
执行顺序:
|
||||
1. 先执行"第一步"的查询,确认是否是要删除的数据(检查姓名字段)
|
||||
2. 执行"第二步"创建备份(必须!)
|
||||
3. 验证备份数据
|
||||
4. 执行"第三步"删除关联数据(一条一条执行,观察影响行数)
|
||||
5. 执行"第四步"删除主表数据
|
||||
6. 执行"第五步"验证删除结果
|
||||
7. 如果出错,使用恢复脚本回滚
|
||||
|
||||
重要发现:
|
||||
- user_name 字段在 sys_user 表中,不在 psy_user_profile 表中
|
||||
- 需要通过 INNER JOIN 关联两个表进行查询
|
||||
- psy_user_profile.user_id = sys_user.user_id
|
||||
|
||||
注意事项:
|
||||
1. 务必在测试环境先执行一遍
|
||||
2. 生产环境执行前做完整数据库备份
|
||||
3. 建议在业务低峰期执行
|
||||
4. 执行前通知相关人员
|
||||
5. 保留备份表至少7天
|
||||
6. 正则表达式 '^[0-9]+$' 匹配纯数字(只包含0-9的字符串)
|
||||
7. 如果有其他关联表,需要补充删除语句
|
||||
|
||||
判断规则:
|
||||
- '^[0-9]+$' 表示从开头到结尾都是数字
|
||||
- 例如:'123'、'456' 会被匹配
|
||||
- 例如:'张三'、'123abc'、'李4' 不会被匹配
|
||||
*/
|
||||
|
||||
-- ========================================
|
||||
-- 更精确的匹配方案(可选)
|
||||
-- ========================================
|
||||
|
||||
/*
|
||||
-- 方案A:只删除纯数字且长度在特定范围的姓名
|
||||
-- 例如:只删除3-10位纯数字的姓名
|
||||
SELECT COUNT(*) FROM sys_user u
|
||||
INNER JOIN psy_user_profile p ON u.user_id = p.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]{3,10}$';
|
||||
|
||||
-- 方案B:排除某些特定的数字姓名(如果有合法的数字姓名)
|
||||
SELECT COUNT(*) FROM sys_user u
|
||||
INNER JOIN psy_user_profile p ON u.user_id = p.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
AND u.user_name NOT IN ('001', '002'); -- 排除这些合法的数字姓名
|
||||
|
||||
-- 方案C:只删除特定区域的数字姓名用户
|
||||
SELECT COUNT(*) FROM sys_user u
|
||||
INNER JOIN psy_user_profile p ON u.user_id = p.user_id
|
||||
WHERE u.user_name REGEXP '^[0-9]+$'
|
||||
AND p.prison_area = '某监区'; -- 替换为实际的监区名称
|
||||
*/
|
||||
191
完整打包流程.bat
191
完整打包流程.bat
|
|
@ -1,191 +0,0 @@
|
|||
@echo off
|
||||
chcp 65001 >nul
|
||||
echo ========================================
|
||||
echo 心理测评系统 - 完整打包流程
|
||||
echo ========================================
|
||||
echo.
|
||||
echo 此脚本将完成以下操作:
|
||||
echo 1. 重新构建前端(包含最新的 TTS 修复)
|
||||
echo 2. 清理并重新打包 Android APK
|
||||
echo.
|
||||
echo 预计耗时:5-10 分钟
|
||||
echo.
|
||||
pause
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo [阶段 1/2] 重新构建前端
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
cd /d "%~dp0xinli-ui"
|
||||
|
||||
echo 当前目录:%CD%
|
||||
echo.
|
||||
|
||||
echo [1.1] 检查 Node.js 环境...
|
||||
call node -v >nul 2>&1
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
echo ✗ Node.js 未安装或未配置到 PATH
|
||||
echo 请先安装 Node.js
|
||||
cd /d "%~dp0"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo ✓ Node.js 已安装
|
||||
call node -v
|
||||
echo.
|
||||
|
||||
echo [1.2] 检查 package.json...
|
||||
if not exist "package.json" (
|
||||
echo ✗ 未找到 package.json
|
||||
echo 请确认在正确的目录
|
||||
cd /d "%~dp0"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo ✓ package.json 存在
|
||||
echo.
|
||||
|
||||
echo [1.3] 清理旧的构建文件...
|
||||
if exist "dist" (
|
||||
echo 删除 dist 目录...
|
||||
rd /s /q "dist"
|
||||
)
|
||||
echo ✓ 清理完成
|
||||
echo.
|
||||
|
||||
echo [1.4] 构建生产版本...
|
||||
echo 开始构建,请耐心等待...
|
||||
echo.
|
||||
call npm run build:prod
|
||||
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
echo.
|
||||
echo ✗ 前端构建失败
|
||||
cd /d "%~dp0"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ✓ 前端构建成功
|
||||
echo.
|
||||
|
||||
cd /d "%~dp0"
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo [阶段 2/2] 打包 Android APK
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
cd /d "%~dp0xinli-App"
|
||||
|
||||
echo 当前目录:%CD%
|
||||
echo.
|
||||
|
||||
echo [2.1] 清理旧的构建文件...
|
||||
if exist "app\build" (
|
||||
echo 删除 app\build 目录...
|
||||
rd /s /q "app\build"
|
||||
)
|
||||
if exist "build" (
|
||||
echo 删除根目录 build 文件夹...
|
||||
rd /s /q "build"
|
||||
)
|
||||
if exist ".gradle" (
|
||||
echo 删除 .gradle 缓存...
|
||||
rd /s /q ".gradle"
|
||||
)
|
||||
echo ✓ 清理完成
|
||||
echo.
|
||||
|
||||
echo [2.2] 清理 Gradle 缓存...
|
||||
call gradlew.bat clean
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
echo ✗ Gradle clean 失败
|
||||
cd /d "%~dp0"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo ✓ Gradle clean 完成
|
||||
echo.
|
||||
|
||||
echo [2.3] 检查 Java 环境...
|
||||
if not defined JAVA_HOME (
|
||||
echo ✗ JAVA_HOME 未设置
|
||||
echo 请设置 JAVA_HOME 环境变量
|
||||
cd /d "%~dp0"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo JAVA_HOME: %JAVA_HOME%
|
||||
"%JAVA_HOME%\bin\java.exe" -version
|
||||
echo ✓ Java 环境正常
|
||||
echo.
|
||||
|
||||
echo [2.4] 构建 Release APK...
|
||||
echo 开始构建,请耐心等待...
|
||||
echo.
|
||||
call gradlew.bat assembleRelease --stacktrace
|
||||
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
echo.
|
||||
echo ✗ APK 构建失败
|
||||
cd /d "%~dp0"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ✓ APK 构建成功
|
||||
echo.
|
||||
|
||||
cd /d "%~dp0"
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo ✓✓✓ 完整打包流程完成!✓✓✓
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
set APK_PATH=%~dp0xinli-App\app\build\outputs\apk\release\app-release.apk
|
||||
if exist "%APK_PATH%" (
|
||||
echo APK 文件位置:
|
||||
echo %APK_PATH%
|
||||
echo.
|
||||
|
||||
echo 文件大小:
|
||||
dir "%APK_PATH%" | findstr "app-release.apk"
|
||||
echo.
|
||||
|
||||
echo ========================================
|
||||
echo 📱 安装说明
|
||||
echo ========================================
|
||||
echo.
|
||||
echo 1. ⚠️ 先卸载手机上的旧版本 App(重要!)
|
||||
echo - 设置 → 应用管理 → 心理测评 → 卸载
|
||||
echo.
|
||||
echo 2. 📲 安装新的 APK
|
||||
echo - 将 APK 传输到手机
|
||||
echo - 点击安装
|
||||
echo.
|
||||
echo 3. ✅ 测试功能
|
||||
echo - 登录系统
|
||||
echo - 进入量表/问卷答题页面
|
||||
echo - 测试朗读功能(应该不再灰色)
|
||||
echo - 测试权限过滤(普通用户只能看到授权的问卷)
|
||||
echo.
|
||||
echo ========================================
|
||||
echo 🎉 打包完成!
|
||||
echo ========================================
|
||||
) else (
|
||||
echo ✗ 未找到 APK 文件
|
||||
echo.
|
||||
echo 搜索 APK 文件...
|
||||
dir /s /b "%~dp0xinli-App\*.apk" 2>nul
|
||||
)
|
||||
|
||||
echo.
|
||||
pause
|
||||
552
局域网TTS工具.html
552
局域网TTS工具.html
|
|
@ -1,552 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>局域网文字转语音工具</title>
|
||||
<!-- 引入Element UI样式 -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
color: #333;
|
||||
font-size: 28px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header p {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.input-section label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.input-section textarea {
|
||||
width: 100%;
|
||||
min-height: 120px;
|
||||
padding: 12px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
resize: vertical;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.input-section textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.preset-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.preset-btn {
|
||||
padding: 8px 16px;
|
||||
background: #f0f0f0;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.preset-btn:hover {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.control-section {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.control-item {
|
||||
background: #f8f9fa;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.control-item label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.control-item .slider-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.slider {
|
||||
flex: 1;
|
||||
height: 6px;
|
||||
background: #e0e0e0;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slider-track {
|
||||
height: 100%;
|
||||
background: #667eea;
|
||||
border-radius: 3px;
|
||||
transition: width 0.2s;
|
||||
}
|
||||
|
||||
.slider-thumb {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background: white;
|
||||
border: 2px solid #667eea;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
cursor: grab;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.slider-thumb:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.value-display {
|
||||
min-width: 60px;
|
||||
text-align: right;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 30px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: #5568d3;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #f56565;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover:not(:disabled) {
|
||||
background: #e53e3e;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(245, 101, 101, 0.4);
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.status {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.status.info {
|
||||
background: #e3f2fd;
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
.status.warning {
|
||||
background: #fff3e0;
|
||||
color: #f57c00;
|
||||
}
|
||||
|
||||
.status.success {
|
||||
background: #e8f5e9;
|
||||
color: #388e3c;
|
||||
}
|
||||
|
||||
.status.error {
|
||||
background: #ffebee;
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.control-section {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>🎤 局域网文字转语音工具</h1>
|
||||
<p>基于浏览器 Web Speech API,无需后端服务,纯前端实现</p>
|
||||
</div>
|
||||
|
||||
<div class="input-section">
|
||||
<label for="textInput">📝 输入要朗读的文字:</label>
|
||||
<textarea id="textInput" placeholder="请输入要转换为语音的文字内容..."></textarea>
|
||||
<div class="preset-buttons">
|
||||
<button class="preset-btn" onclick="setPresetText('欢迎使用AI心理健康测评系统,请仔细阅读题目并选择您的答案。')">
|
||||
欢迎语
|
||||
</button>
|
||||
<button class="preset-btn" onclick="setPresetText('请根据您的实际情况,选择最符合的选项。')">
|
||||
答题提示
|
||||
</button>
|
||||
<button class="preset-btn" onclick="setPresetText('感谢您的参与,测评已完成,请查看您的测评报告。')">
|
||||
完成提示
|
||||
</button>
|
||||
<button class="preset-btn" onclick="setPresetText('请注意,本次测评结果仅供参考,如有疑问请咨询专业心理医生。')">
|
||||
免责声明
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-section">
|
||||
<div class="control-item">
|
||||
<label>🔊 音量:<span id="volumeValue">100%</span></label>
|
||||
<div class="slider-container">
|
||||
<div class="slider" id="volumeSlider">
|
||||
<div class="slider-track" id="volumeTrack"></div>
|
||||
<div class="slider-thumb" id="volumeThumb"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-item">
|
||||
<label>⚡ 语速:<span id="rateValue">1.0x</span></label>
|
||||
<div class="slider-container">
|
||||
<div class="slider" id="rateSlider">
|
||||
<div class="slider-track" id="rateTrack"></div>
|
||||
<div class="slider-thumb" id="rateThumb"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-item">
|
||||
<label>🎵 音调:<span id="pitchValue">1.0</span></label>
|
||||
<div class="slider-container">
|
||||
<div class="slider" id="pitchSlider">
|
||||
<div class="slider-track" id="pitchTrack"></div>
|
||||
<div class="slider-thumb" id="pitchThumb"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button class="btn btn-primary" id="speakBtn" onclick="speakText()">
|
||||
▶️ 开始朗读
|
||||
</button>
|
||||
<button class="btn btn-danger" id="stopBtn" onclick="stopSpeaking()" disabled>
|
||||
⏹️ 停止朗读
|
||||
</button>
|
||||
<button class="btn btn-primary" onclick="clearText()">
|
||||
🗑️ 清空文本
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="status info" id="status">
|
||||
💡 提示:请在 Chrome、Edge 或 Safari 浏览器中使用以获得最佳体验
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 全局变量
|
||||
let synth = null;
|
||||
let utterance = null;
|
||||
let isSpeaking = false;
|
||||
|
||||
// 参数值
|
||||
let volume = 1.0; // 0-1
|
||||
let rate = 1.0; // 0.5-2
|
||||
let pitch = 1.0; // 0.5-2
|
||||
|
||||
// 初始化
|
||||
window.onload = function() {
|
||||
initTts();
|
||||
initSliders();
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化 TTS
|
||||
*/
|
||||
function initTts() {
|
||||
if ('speechSynthesis' in window) {
|
||||
synth = window.speechSynthesis;
|
||||
updateStatus('success', '✅ 语音合成功能已就绪');
|
||||
} else {
|
||||
updateStatus('error', '❌ 您的浏览器不支持语音合成功能,请使用 Chrome、Edge 或 Safari 浏览器');
|
||||
document.getElementById('speakBtn').disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化滑块
|
||||
*/
|
||||
function initSliders() {
|
||||
initSlider('volume', 0, 1, 1.0, updateVolume);
|
||||
initSlider('rate', 0.5, 2, 1.0, updateRate);
|
||||
initSlider('pitch', 0.5, 2, 1.0, updatePitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化单个滑块
|
||||
*/
|
||||
function initSlider(name, min, max, value, callback) {
|
||||
const slider = document.getElementById(name + 'Slider');
|
||||
const thumb = document.getElementById(name + 'Thumb');
|
||||
const track = document.getElementById(name + 'Track');
|
||||
const valueDisplay = document.getElementById(name + 'Value');
|
||||
|
||||
let isDragging = false;
|
||||
|
||||
function updateSlider(clientX) {
|
||||
const rect = slider.getBoundingClientRect();
|
||||
let percent = (clientX - rect.left) / rect.width;
|
||||
percent = Math.max(0, Math.min(1, percent));
|
||||
|
||||
const newValue = min + percent * (max - min);
|
||||
const displayValue = name === 'volume'
|
||||
? Math.round(newValue * 100) + '%'
|
||||
: name === 'rate'
|
||||
? newValue.toFixed(1) + 'x'
|
||||
: newValue.toFixed(1);
|
||||
|
||||
valueDisplay.textContent = displayValue;
|
||||
thumb.style.left = percent * 100 + '%';
|
||||
track.style.width = percent * 100 + '%';
|
||||
|
||||
callback(newValue);
|
||||
}
|
||||
|
||||
slider.addEventListener('mousedown', (e) => {
|
||||
isDragging = true;
|
||||
updateSlider(e.clientX);
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
if (isDragging) {
|
||||
updateSlider(e.clientX);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
isDragging = false;
|
||||
});
|
||||
|
||||
// 初始化位置
|
||||
const percent = (value - min) / (max - min);
|
||||
thumb.style.left = percent * 100 + '%';
|
||||
track.style.width = percent * 100 + '%';
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新音量
|
||||
*/
|
||||
function updateVolume(value) {
|
||||
volume = value;
|
||||
if (utterance) {
|
||||
utterance.volume = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新语速
|
||||
*/
|
||||
function updateRate(value) {
|
||||
rate = value;
|
||||
if (utterance) {
|
||||
utterance.rate = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新音调
|
||||
*/
|
||||
function updatePitch(value) {
|
||||
pitch = value;
|
||||
if (utterance) {
|
||||
utterance.pitch = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 朗读文本
|
||||
*/
|
||||
function speakText() {
|
||||
const text = document.getElementById('textInput').value.trim();
|
||||
|
||||
if (!text) {
|
||||
updateStatus('warning', '⚠️ 请输入要朗读的文字');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!synth) {
|
||||
updateStatus('error', '❌ 语音合成功能不可用');
|
||||
return;
|
||||
}
|
||||
|
||||
// 停止之前的朗读
|
||||
stopSpeaking();
|
||||
|
||||
// 创建语音合成对象
|
||||
utterance = new SpeechSynthesisUtterance(text);
|
||||
|
||||
// 设置参数
|
||||
utterance.lang = 'zh-CN';
|
||||
utterance.volume = volume;
|
||||
utterance.rate = rate;
|
||||
utterance.pitch = pitch;
|
||||
|
||||
// 选择中文语音
|
||||
const voices = synth.getVoices();
|
||||
const chineseVoice = voices.find(voice =>
|
||||
voice.lang.includes('zh') || voice.lang.includes('CN')
|
||||
);
|
||||
if (chineseVoice) {
|
||||
utterance.voice = chineseVoice;
|
||||
}
|
||||
|
||||
// 监听事件
|
||||
utterance.onstart = () => {
|
||||
isSpeaking = true;
|
||||
document.getElementById('speakBtn').disabled = true;
|
||||
document.getElementById('stopBtn').disabled = false;
|
||||
updateStatus('success', '🔊 正在朗读...');
|
||||
};
|
||||
|
||||
utterance.onend = () => {
|
||||
isSpeaking = false;
|
||||
document.getElementById('speakBtn').disabled = false;
|
||||
document.getElementById('stopBtn').disabled = true;
|
||||
updateStatus('info', '✅ 朗读完成');
|
||||
};
|
||||
|
||||
utterance.onerror = (event) => {
|
||||
isSpeaking = false;
|
||||
document.getElementById('speakBtn').disabled = false;
|
||||
document.getElementById('stopBtn').disabled = true;
|
||||
updateStatus('error', '❌ 朗读失败: ' + event.error);
|
||||
};
|
||||
|
||||
// 开始朗读
|
||||
synth.speak(utterance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止朗读
|
||||
*/
|
||||
function stopSpeaking() {
|
||||
if (synth && synth.speaking) {
|
||||
synth.cancel();
|
||||
isSpeaking = false;
|
||||
document.getElementById('speakBtn').disabled = false;
|
||||
document.getElementById('stopBtn').disabled = true;
|
||||
updateStatus('info', '⏹️ 已停止朗读');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空文本
|
||||
*/
|
||||
function clearText() {
|
||||
document.getElementById('textInput').value = '';
|
||||
stopSpeaking();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置预设文本
|
||||
*/
|
||||
function setPresetText(text) {
|
||||
document.getElementById('textInput').value = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新状态提示
|
||||
*/
|
||||
function updateStatus(type, message) {
|
||||
const statusEl = document.getElementById('status');
|
||||
statusEl.className = 'status ' + type;
|
||||
statusEl.textContent = message;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
398
心理测评问题修复说明.md
398
心理测评问题修复说明.md
|
|
@ -1,398 +0,0 @@
|
|||
# 心理测评问题修复说明
|
||||
|
||||
## 修复日期
|
||||
2025年12月1日
|
||||
|
||||
## 问题清单
|
||||
|
||||
### 问题1:用户档案与量表权限管理用户数量不一致 ✅
|
||||
|
||||
**问题描述**:
|
||||
- 用户档案显示 3014 个用户
|
||||
- 量表权限管理显示 3016 个用户
|
||||
- 数据不一致导致分配权限时出现困惑
|
||||
|
||||
**根本原因**:
|
||||
两个页面使用了不同的API接口:
|
||||
- **用户档案页面**:使用 `listStudentProfile` 接口(`/psychology/profile/student/list`)
|
||||
- 只查询拥有**学员角色**的用户 → 3014个
|
||||
- **量表权限管理**:使用 `listProfile` 接口(`/psychology/profile/list`)
|
||||
- 查询**所有用户档案** → 3016个(包括非学员用户)
|
||||
|
||||
**修复方案**:
|
||||
修改量表权限管理的用户查询逻辑,统一使用 `listStudentProfile` 接口,只查询学员用户,与用户档案页面保持一致。
|
||||
|
||||
**修改文件**:
|
||||
- `xinli-ui/src/views/psychology/permission/index.vue`
|
||||
|
||||
**修改内容**:
|
||||
|
||||
1. **导入接口更改** (第300行)
|
||||
```javascript
|
||||
// 修改前
|
||||
import { listProfile } from "@/api/psychology/profile";
|
||||
|
||||
// 修改后
|
||||
import { listStudentProfile } from "@/api/psychology/profile";
|
||||
```
|
||||
|
||||
2. **加载监区选项** (第556行)
|
||||
```javascript
|
||||
// 修改前
|
||||
listProfile({ pageNum: 1, pageSize: 10000, status: undefined }).then(response => {
|
||||
|
||||
// 修改后
|
||||
listStudentProfile({ pageNum: 1, pageSize: 10000 }).then(response => {
|
||||
```
|
||||
|
||||
3. **获取用户选择列表** (第599行)
|
||||
```javascript
|
||||
// 修改前
|
||||
return listProfile(query).then(response => {
|
||||
|
||||
// 修改后
|
||||
return listStudentProfile(query).then(response => {
|
||||
```
|
||||
|
||||
4. **批量获取所有用户** (第675行)
|
||||
```javascript
|
||||
// 修改前
|
||||
const response = await listProfile({
|
||||
|
||||
// 修改后
|
||||
const response = await listStudentProfile({
|
||||
```
|
||||
|
||||
5. **保留查询参数配置** (第346-353行)
|
||||
```javascript
|
||||
userQueryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
infoNumber: undefined,
|
||||
userName: undefined,
|
||||
prisonArea: undefined,
|
||||
status: undefined // 不限制状态
|
||||
}
|
||||
```
|
||||
|
||||
**修复效果**:
|
||||
- ✅ 量表权限管理和用户档案显示相同数量的用户(都是3014)
|
||||
- ✅ 只显示学员角色的用户,数据更准确
|
||||
- ✅ 数据统计保持一致性
|
||||
|
||||
---
|
||||
|
||||
### 问题2:测评状态显示和管理 ✅
|
||||
|
||||
**问题描述**:
|
||||
1. 用户退出测评后,状态显示为"进行中"而不是"已暂停"
|
||||
2. 希望区分"进行中"(正在答题)、"已暂停"(退出或暂停)、"已完成"(提交完成)
|
||||
3. 暂停和退出都要保留用户填写的内容
|
||||
|
||||
**状态定义**:
|
||||
```
|
||||
status = '0' - 进行中(正在答题,未退出)
|
||||
status = '1' - 已完成(已提交)
|
||||
status = '2' - 已作废(删除或作废)
|
||||
status = '3' - 已暂停(用户暂停或退出)
|
||||
```
|
||||
|
||||
**现有逻辑验证**:
|
||||
|
||||
#### ✅ 暂停功能
|
||||
**前端** (`xinli-ui/src/views/psychology/assessment/taking.vue` 第494-503行):
|
||||
```javascript
|
||||
handlePause() {
|
||||
this.$modal.confirm('确定要暂停测评吗?您可以稍后继续完成。').then(() => {
|
||||
pauseAssessment(this.assessmentId).then(() => {
|
||||
this.$modal.msgSuccess("测评已暂停");
|
||||
// 跳转回列表页
|
||||
this.$router.push(isStudent ? '/student/tests' : '/psychology/assessment');
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**后端** (`PsyAssessmentMapper.xml` 第142-152行):
|
||||
```xml
|
||||
<update id="pauseAssessment">
|
||||
update psy_assessment
|
||||
<set>
|
||||
pause_time = sysdate(),
|
||||
pause_count = pause_count + 1,
|
||||
status = '3', <!-- 设置为已暂停 -->
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where assessment_id = #{assessmentId}
|
||||
</update>
|
||||
```
|
||||
|
||||
#### ✅ 退出功能
|
||||
**前端** (`xinli-ui/src/views/psychology/assessment/taking.vue` 第506-528行):
|
||||
```javascript
|
||||
handleExit() {
|
||||
this.$modal.confirm('确定要退出测评吗?已答题目将会保存。').then(() => {
|
||||
this.loading = true;
|
||||
// 先等待一小段时间,确保最后的答案保存请求发出
|
||||
setTimeout(() => {
|
||||
// 退出前先暂停测评,保存进度
|
||||
pauseAssessment(this.assessmentId).then(() => { // ✅ 调用暂停接口
|
||||
this.loading = false;
|
||||
this.$modal.msgSuccess("测评进度已保存");
|
||||
// 跳转回列表页
|
||||
this.$router.push(isStudent ? '/student/tests' : '/psychology/assessment');
|
||||
});
|
||||
}, 500); // 等待500ms,确保答案保存请求已发送
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**说明**: 退出功能也调用 `pauseAssessment`,会将状态设为 `'3'`(已暂停)
|
||||
|
||||
#### ✅ 提交功能
|
||||
**前端** (`xinli-ui/src/views/psychology/assessment/taking.vue` 第531-560行):
|
||||
```javascript
|
||||
handleSubmit() {
|
||||
if (!this.isComplete) {
|
||||
const remaining = this.itemList.length - this.answeredCount;
|
||||
this.$modal.msgError(`还有 ${remaining} 道题未作答,请完成后再提交`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$modal.confirm('确定要提交测评吗?提交后将不能修改。').then(() => {
|
||||
this.loading = true;
|
||||
submitAssessment(this.assessmentId).then(response => {
|
||||
this.loading = false;
|
||||
this.$modal.msgSuccess("测评已提交,报告正在生成中...");
|
||||
// 跳转到报告页面
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**后端** (`PsyAssessmentController.java` 第436-445行):
|
||||
```java
|
||||
// 更新测评状态为已完成
|
||||
assessment.setStatus("1"); // ✅ 设置为已完成
|
||||
assessment.setSubmitTime(new Date());
|
||||
if (assessment.getStartTime() != null) {
|
||||
long completeSeconds = (System.currentTimeMillis() - assessment.getStartTime().getTime()) / 1000;
|
||||
assessment.setCompleteTime((int) completeSeconds);
|
||||
}
|
||||
assessmentService.updateAssessment(assessment);
|
||||
```
|
||||
|
||||
#### ✅ 继续答题功能
|
||||
**前端** (`xinli-ui/src/views/psychology/assessment/index.vue` 第103-106行):
|
||||
```vue
|
||||
<el-button
|
||||
@click="handleContinue(scope.row)"
|
||||
v-if="scope.row.status === '0' || scope.row.status === '3'"
|
||||
>继续答题</el-button>
|
||||
```
|
||||
|
||||
**说明**: 只有"进行中"(`status='0'`)或"已暂停"(`status='3'`)状态才显示"继续答题"按钮
|
||||
|
||||
#### ✅ 恢复测评
|
||||
**后端** (`PsyAssessmentMapper.xml` 第154-163行):
|
||||
```xml
|
||||
<update id="resumeAssessment">
|
||||
update psy_assessment
|
||||
<set>
|
||||
resume_time = sysdate(),
|
||||
status = '0', <!-- 恢复为进行中 -->
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where assessment_id = #{assessmentId}
|
||||
</update>
|
||||
```
|
||||
|
||||
**说明**: 点击"继续答题"时会调用 `resumeAssessment`,将状态从 `'3'` 改为 `'0'`
|
||||
|
||||
#### ✅ 答案自动保存
|
||||
**前端** (`xinli-ui/src/views/psychology/assessment/taking.vue`):
|
||||
```javascript
|
||||
// 每次选择答案时自动保存
|
||||
handleAnswerChange() {
|
||||
// ... 保存逻辑
|
||||
this.saveCurrentAnswer();
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 用户每选择一个答案都会自动保存到数据库
|
||||
- 退出或暂停时不会丢失已答题目
|
||||
- 继续答题时可以从上次位置继续
|
||||
|
||||
### 测评状态流转图
|
||||
|
||||
```
|
||||
开始测评 ──> status='0'(进行中)
|
||||
│
|
||||
├──> 点击"暂停" ──> status='3'(已暂停)──> 点击"继续答题" ──> status='0'
|
||||
│
|
||||
├──> 点击"退出" ──> status='3'(已暂停)──> 点击"继续答题" ──> status='0'
|
||||
│
|
||||
└──> 点击"提交" ──> status='1'(已完成)
|
||||
```
|
||||
|
||||
### 测评管理列表显示
|
||||
|
||||
**文件**: `xinli-ui/src/views/psychology/assessment/index.vue`
|
||||
|
||||
**状态显示** (第81-87行):
|
||||
```vue
|
||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.status === '0'" type="info">进行中</el-tag>
|
||||
<el-tag v-else-if="scope.row.status === '1'" type="success">已完成</el-tag>
|
||||
<el-tag v-else-if="scope.row.status === '2'" type="danger">已作废</el-tag>
|
||||
<el-tag v-else-if="scope.row.status === '3'" type="warning">已暂停</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
```
|
||||
|
||||
**状态筛选** (第22-28行):
|
||||
```vue
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="状态" clearable>
|
||||
<el-option label="进行中" value="0" />
|
||||
<el-option label="已完成" value="1" />
|
||||
<el-option label="已作废" value="2" />
|
||||
<el-option label="已暂停" value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
```
|
||||
|
||||
## 修复验证
|
||||
|
||||
### 验证步骤
|
||||
|
||||
#### 问题1验证:
|
||||
1. 登录系统
|
||||
2. 进入"心理 → 用户档案",查看总数 → 应显示 **3014**(学员用户)
|
||||
3. 进入"心理 → 量表权限管理",点击"分配用户"
|
||||
4. 不添加任何筛选条件,查看用户列表总数 → 应显示 **3014**(与用户档案一致)
|
||||
5. ✅ 数量一致,都显示学员角色用户
|
||||
|
||||
**说明**: 修改后,两个页面都只显示拥有学员角色的用户(3014个),不再显示其他角色的用户(如管理员等2个用户)。
|
||||
|
||||
#### 问题2验证:
|
||||
|
||||
**测试场景1:暂停测评**
|
||||
1. 开始一个测评
|
||||
2. 答几道题
|
||||
3. 点击"暂停"按钮
|
||||
4. 返回测评管理列表
|
||||
5. ✅ 该测评显示为"已暂停"状态(黄色标签)
|
||||
6. 点击"继续答题"
|
||||
7. ✅ 之前答的题目答案都保留
|
||||
|
||||
**测试场景2:退出测评**
|
||||
1. 开始一个测评
|
||||
2. 答几道题
|
||||
3. 点击"退出"按钮
|
||||
4. 返回测评管理列表
|
||||
5. ✅ 该测评显示为"已暂停"状态(黄色标签)
|
||||
6. 点击"继续答题"
|
||||
7. ✅ 之前答的题目答案都保留
|
||||
|
||||
**测试场景3:完成测评**
|
||||
1. 开始一个测评
|
||||
2. 答完所有题目
|
||||
3. 点击"提交测评"
|
||||
4. 返回测评管理列表
|
||||
5. ✅ 该测评显示为"已完成"状态(绿色标签)
|
||||
6. ✅ 不再显示"继续答题"按钮
|
||||
7. ✅ 显示"查看答题"和"查看报告"按钮
|
||||
|
||||
## 技术说明
|
||||
|
||||
### 数据库表结构
|
||||
```sql
|
||||
-- psy_assessment 测评表
|
||||
CREATE TABLE psy_assessment (
|
||||
assessment_id BIGINT PRIMARY KEY,
|
||||
scale_id BIGINT,
|
||||
assessee_name VARCHAR(100),
|
||||
status CHAR(1), -- 0:进行中 1:已完成 2:已作废 3:已暂停
|
||||
start_time DATETIME,
|
||||
pause_time DATETIME,
|
||||
resume_time DATETIME,
|
||||
submit_time DATETIME,
|
||||
pause_count INT DEFAULT 0,
|
||||
complete_time INT,
|
||||
total_score DECIMAL(10,2),
|
||||
-- ... 其他字段
|
||||
);
|
||||
```
|
||||
|
||||
### API接口
|
||||
- `POST /psychology/assessment/pause/{assessmentId}` - 暂停测评
|
||||
- `POST /psychology/assessment/resume/{assessmentId}` - 恢复测评
|
||||
- `POST /psychology/assessment/submit/{assessmentId}` - 提交测评
|
||||
- `POST /psychology/assessment/answer/save` - 保存答案
|
||||
|
||||
## 总结
|
||||
|
||||
### 问题1:用户数量不一致 ✅
|
||||
**修复方式**: 统一使用学员档案接口(`listStudentProfile`),只查询学员角色用户
|
||||
|
||||
**影响范围**: 量表权限管理的用户选择对话框
|
||||
|
||||
**预期效果**:
|
||||
- 用户档案和量表权限管理显示相同数量的用户(都是3014)
|
||||
- 只显示学员用户,不包括其他角色的用户
|
||||
- 数据来源统一,避免混淆
|
||||
|
||||
### 问题2:测评状态管理 ✅
|
||||
**验证结果**: 现有逻辑已正确实现
|
||||
|
||||
**核心功能**:
|
||||
1. ✅ 退出测评会设置 status='3'(已暂停)
|
||||
2. ✅ 暂停测评会设置 status='3'(已暂停)
|
||||
3. ✅ 提交测评会设置 status='1'(已完成)
|
||||
4. ✅ 继续答题会恢复 status='0'(进行中)
|
||||
5. ✅ 所有答案都会自动保存,不会丢失
|
||||
6. ✅ 测评列表正确显示各种状态
|
||||
|
||||
**如果用户仍然遇到"退出显示进行中"的问题,请检查**:
|
||||
1. 浏览器缓存是否清除
|
||||
2. 后端服务是否重启
|
||||
3. 数据库中 psy_assessment 表的 status 字段值
|
||||
4. 网络请求是否成功(F12查看Network)
|
||||
5. 是否有其他地方修改了状态
|
||||
|
||||
## 部署说明
|
||||
|
||||
1. **前端部署**:
|
||||
- 修改文件: `xinli-ui/src/views/psychology/permission/index.vue`
|
||||
- 重新编译: `npm run build`
|
||||
- 部署到服务器
|
||||
|
||||
2. **无需后端修改**:
|
||||
- 后端逻辑已经正确实现
|
||||
- 无需重新部署
|
||||
|
||||
3. **验证步骤**:
|
||||
- 清除浏览器缓存
|
||||
- 刷新页面
|
||||
- 按照验证步骤测试
|
||||
|
||||
## 附录:常见问题
|
||||
|
||||
**Q: 为什么要查询所有状态的用户?**
|
||||
A: 因为即使用户被停用或删除,其历史测评记录和权限仍然存在,需要能够管理和查看。
|
||||
|
||||
**Q: 状态'0'和'3'有什么区别?**
|
||||
A:
|
||||
- status='0'(进行中): 用户正在答题页面答题
|
||||
- status='3'(已暂停): 用户点击了暂停或退出按钮,离开了答题页面
|
||||
|
||||
**Q: 如何区分正在答题和已退出?**
|
||||
A:
|
||||
- 在测评列表中,status='0'和'3'都显示"继续答题"按钮
|
||||
- 区别在于:status='0'可能是打开了答题页面但没有答题,status='3'是明确点击了暂停/退出
|
||||
|
||||
**Q: 答案什么时候保存?**
|
||||
A: 每次选择答案后都会立即自动保存到数据库,不需要手动保存。
|
||||
55
数据生成说明.md
55
数据生成说明.md
|
|
@ -1,55 +0,0 @@
|
|||
# 用户数据生成工具
|
||||
|
||||
## 功能说明
|
||||
这个Python脚本可以生成3000条符合导入模版格式的测试数据。
|
||||
|
||||
## 生成的数据字段
|
||||
- **信息编号(必填)**: 10000-12999
|
||||
- **姓名(必填)**: 随机生成的中文姓名
|
||||
- **监区(必填)**: 第一监区、第二监区、第三监区、A区、B区、C区、D区
|
||||
- **性别**: 男/女
|
||||
- **出生日期**: 1950-2005年之间
|
||||
- **民族**: 汉、汉族、回族、满族、蒙古族、维吾尔族、苗族、壮族
|
||||
- **文化程度**: 小学、初中、高中、中专、大专、本科、研究生
|
||||
- **罪名**: 18种常见罪名(抢劫罪、盗窃罪、诈骗罪等)
|
||||
- **刑期**: 随机生成(X年/X月/无期)
|
||||
- **刑期起日**: 2015-2024年之间的随机日期
|
||||
- **刑期止日**: 根据刑期起日和刑期长度计算
|
||||
- **入监时间**: 与刑期起日相同
|
||||
- **状态**: 在押
|
||||
|
||||
## 安装依赖
|
||||
|
||||
```powershell
|
||||
pip install -r requirements_data_generation.txt
|
||||
```
|
||||
|
||||
或者单独安装:
|
||||
|
||||
```powershell
|
||||
pip install pandas openpyxl
|
||||
```
|
||||
|
||||
## 运行脚本
|
||||
|
||||
```powershell
|
||||
python generate_test_data.py
|
||||
```
|
||||
|
||||
## 输出文件
|
||||
脚本运行后会在当前目录生成:
|
||||
- `用户导入测试数据_3000条.xlsx` - 包含3000条测试数据的Excel文件
|
||||
|
||||
## 自定义生成数量
|
||||
如需修改生成数量,请编辑脚本中的 `NUM_RECORDS` 变量:
|
||||
|
||||
```python
|
||||
NUM_RECORDS = 3000 # 修改为你需要的数量
|
||||
```
|
||||
|
||||
## 数据示例
|
||||
|
||||
| 信息编号 | 姓名 | 监区 | 性别 | 出生日期 | 民族 | 文化程度 | 罪名 | 刑期 | 刑期起日 | 刑期止日 | 入监时间 | 状态 |
|
||||
|---------|------|------|------|---------|------|---------|------|------|---------|---------|---------|------|
|
||||
| 10000 | 张伟强 | 第一监区 | 男 | 1985-03-15 | 汉 | 高中 | 抢劫罪 | 5年 | 2020-01-10 | 2025-01-10 | 2020-01-10 | 在押 |
|
||||
| 10001 | 李芳娜 | A区 | 女 | 1992-08-22 | 汉族 | 本科 | 诈骗罪 | 3年 | 2022-05-20 | 2025-05-20 | 2022-05-20 | 在押 |
|
||||
283
最终权限修复说明.md
283
最终权限修复说明.md
|
|
@ -1,283 +0,0 @@
|
|||
# 🔧 最终权限修复说明
|
||||
|
||||
## 📋 明确的需求
|
||||
|
||||
**用户需求**:无论任何时候,用户只能显示被分配权限的量表或问卷。
|
||||
|
||||
## ✅ 已完成的修复
|
||||
|
||||
### 1. 统一的权限逻辑
|
||||
|
||||
**简化后的逻辑**:
|
||||
```java
|
||||
// 用户只能看到分配给自己的量表和问卷
|
||||
if (scaleId != null && allowedScaleIds.contains(scaleId))
|
||||
{
|
||||
filtered.add(scale); // 有权限,添加
|
||||
}
|
||||
// 没有权限的,直接跳过
|
||||
```
|
||||
|
||||
**特殊情况**:
|
||||
- **管理员**(userId=1 或有管理权限):看到所有内容
|
||||
- **普通用户**:只看到分配给自己的内容
|
||||
|
||||
### 2. 修改的文件
|
||||
|
||||
1. **PsyScaleController.java**
|
||||
- `filterScaleListByPermission()` - 简化为单一判断
|
||||
- `resolveAllowedScaleIdsForCurrentUser()` - 添加日志输出
|
||||
|
||||
2. **PsyQuestionnaireController.java**
|
||||
- `filterQuestionnaireListByPermission()` - 简化为单一判断
|
||||
- `resolveAllowedScaleIdsForCurrentUser()` - 添加日志输出
|
||||
|
||||
### 3. 添加的调试日志
|
||||
|
||||
```java
|
||||
// 会输出以下日志:
|
||||
logger.info("用户 userId={} 的权限列表: {}", currentUserId, scaleIds);
|
||||
logger.info("权限过滤结果: 总数={}, 过滤后={}, 用户权限数={}", ...);
|
||||
logger.debug("权限过滤: 允许访问 scaleId={}, scaleName={}", ...);
|
||||
logger.debug("权限过滤: 拒绝访问 scaleId={}, scaleName={}", ...);
|
||||
```
|
||||
|
||||
## 🔍 排查"不能正常显示"的问题
|
||||
|
||||
### 可能的原因
|
||||
|
||||
#### 原因 1: 权限数据不正确
|
||||
|
||||
**问题**:数据库中的权限记录可能有问题
|
||||
|
||||
**检查方法**:
|
||||
```sql
|
||||
-- 查看用户的权限列表
|
||||
SELECT * FROM psy_scale_permission
|
||||
WHERE user_id = 你的用户ID AND status = '0';
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- 量表使用**正数ID**(如:1, 2, 3)
|
||||
- 问卷使用**负数ID**(如:-1, -2, -3)
|
||||
|
||||
#### 原因 2: 用户没有被分配权限
|
||||
|
||||
**问题**:用户在权限表中没有记录
|
||||
|
||||
**解决方法**:为用户分配权限
|
||||
```sql
|
||||
-- 为用户ID=2分配量表ID=1的权限
|
||||
INSERT INTO psy_scale_permission (user_id, scale_id, status, create_by, create_time)
|
||||
VALUES (2, 1, '0', 'admin', NOW());
|
||||
|
||||
-- 为用户ID=2分配问卷ID=1的权限(注意使用负数)
|
||||
INSERT INTO psy_scale_permission (user_id, scale_id, status, create_by, create_time)
|
||||
VALUES (2, -1, '0', 'admin', NOW());
|
||||
```
|
||||
|
||||
#### 原因 3: ID映射错误
|
||||
|
||||
**问题**:前端显示的问卷,在权限表中没有对应的负数ID
|
||||
|
||||
**检查方法**:
|
||||
```sql
|
||||
-- 检查问卷ID和权限表中的ID是否匹配
|
||||
SELECT
|
||||
q.questionnaire_id,
|
||||
q.questionnaire_name,
|
||||
-q.questionnaire_id as 权限表应该用的ID,
|
||||
p.scale_id as 权限表实际的ID
|
||||
FROM psy_questionnaire q
|
||||
LEFT JOIN psy_scale_permission p ON -q.questionnaire_id = p.scale_id
|
||||
WHERE p.user_id = 你的用户ID;
|
||||
```
|
||||
|
||||
#### 原因 4: 用户被识别为管理员
|
||||
|
||||
**问题**:用户ID=1 或有 `psychology:scale:list` 权限,导致看到所有内容
|
||||
|
||||
**检查方法**:
|
||||
```sql
|
||||
-- 检查用户ID
|
||||
SELECT user_id FROM sys_user WHERE user_name = '你的用户名';
|
||||
|
||||
-- 检查用户权限
|
||||
SELECT r.role_name, m.perms
|
||||
FROM sys_user u
|
||||
INNER JOIN sys_user_role ur ON u.user_id = ur.user_id
|
||||
INNER JOIN sys_role r ON ur.role_id = r.role_id
|
||||
LEFT JOIN sys_role_menu rm ON r.role_id = rm.role_id
|
||||
LEFT JOIN sys_menu m ON rm.menu_id = m.menu_id
|
||||
WHERE u.user_name = '你的用户名';
|
||||
```
|
||||
|
||||
## 📝 使用提供的工具
|
||||
|
||||
### 1. SQL检查脚本
|
||||
|
||||
运行 `检查权限数据.sql` 中的查询:
|
||||
|
||||
```sql
|
||||
-- 1. 查看用户权限数量
|
||||
SELECT u.user_id, u.user_name, COUNT(*) FROM ...
|
||||
|
||||
-- 2. 查看用户的详细权限
|
||||
SELECT * FROM psy_scale_permission WHERE user_id = X ...
|
||||
|
||||
-- 3. 查看量表权限分配情况
|
||||
SELECT s.scale_id, s.scale_name, COUNT(*) FROM ...
|
||||
|
||||
-- 10. 快速诊断
|
||||
WITH user_permissions AS (...) ...
|
||||
```
|
||||
|
||||
### 2. 后端日志
|
||||
|
||||
重新编译部署后,查看日志文件:
|
||||
|
||||
```bash
|
||||
# 查找权限相关日志
|
||||
tail -f logs/xinli.log | grep "权限"
|
||||
|
||||
# 应该看到类似输出:
|
||||
# 用户 userId=2 的权限列表: [1, 3, -1, -2]
|
||||
# 权限过滤结果: 总数=10, 过滤后=4, 用户权限数=4
|
||||
```
|
||||
|
||||
## 🚀 部署步骤
|
||||
|
||||
### 1. 重新编译后端
|
||||
```bash
|
||||
cd c:\Users\Administrator\Desktop\Project\xinli
|
||||
mvn clean package -DskipTests
|
||||
```
|
||||
|
||||
### 2. 备份当前jar
|
||||
```bash
|
||||
# 备份当前运行的jar文件
|
||||
copy ry-xinli-admin\target\ry-xinli-admin.jar ry-xinli-admin.jar.backup
|
||||
```
|
||||
|
||||
### 3. 停止服务
|
||||
```bash
|
||||
# 根据你的部署方式停止服务
|
||||
# systemctl stop xinli
|
||||
# 或手动kill进程
|
||||
```
|
||||
|
||||
### 4. 替换jar文件
|
||||
```bash
|
||||
# 将新编译的jar复制到部署目录
|
||||
```
|
||||
|
||||
### 5. 启动服务
|
||||
```bash
|
||||
# 启动新服务
|
||||
# systemctl start xinli
|
||||
```
|
||||
|
||||
### 6. 查看日志
|
||||
```bash
|
||||
# 检查启动日志
|
||||
tail -f logs/xinli.log
|
||||
```
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 测试步骤
|
||||
|
||||
1. **清除浏览器缓存**
|
||||
- 按 Ctrl+Shift+Delete
|
||||
- 清除所有缓存和Cookie
|
||||
|
||||
2. **使用普通用户登录**
|
||||
- 不要使用admin或管理员账号
|
||||
|
||||
3. **查看问卷/量表列表**
|
||||
- 记录看到的数量
|
||||
|
||||
4. **对比数据库**
|
||||
- 运行SQL检查脚本
|
||||
- 对比数量是否一致
|
||||
|
||||
### 预期结果
|
||||
|
||||
假设:
|
||||
- 数据库中有 10 个量表、8 个问卷
|
||||
- 用户被分配了 3 个量表、2 个问卷的权限
|
||||
|
||||
**应该看到**:
|
||||
- 量表列表:3 个
|
||||
- 问卷列表:2 个
|
||||
- 合计列表(包含问卷):5 个
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
### Q1: 管理员看不到所有内容?
|
||||
|
||||
**A**: 检查用户ID是否为1,或是否有管理权限
|
||||
```sql
|
||||
SELECT user_id FROM sys_user WHERE user_name = 'admin';
|
||||
```
|
||||
|
||||
### Q2: 普通用户看到所有内容?
|
||||
|
||||
**A**: 可能被误判为管理员,检查权限配置
|
||||
|
||||
### Q3: 用户看不到任何内容?
|
||||
|
||||
**A**: 检查权限表中是否有该用户的记录
|
||||
```sql
|
||||
SELECT COUNT(*) FROM psy_scale_permission
|
||||
WHERE user_id = X AND status = '0';
|
||||
```
|
||||
|
||||
### Q4: 数量不一致?
|
||||
|
||||
**A**:
|
||||
1. 检查前端是否缓存
|
||||
2. 检查后端日志中的过滤数量
|
||||
3. 对比数据库中的实际权限数
|
||||
|
||||
## 📊 数据一致性检查清单
|
||||
|
||||
- [ ] 权限表中的 scale_id 正确(量表用正数,问卷用负数)
|
||||
- [ ] 用户有对应的权限记录
|
||||
- [ ] 权限记录的 status = '0'(启用状态)
|
||||
- [ ] 量表/问卷确实存在(没有孤立的权限记录)
|
||||
- [ ] 用户不是管理员(除非确实需要看所有内容)
|
||||
|
||||
## 🎯 快速诊断命令
|
||||
|
||||
```sql
|
||||
-- 一键诊断用户权限(替换 USER_ID)
|
||||
WITH user_info AS (
|
||||
SELECT
|
||||
2 as uid, -- 替换为实际用户ID
|
||||
(SELECT COUNT(*) FROM psy_scale_permission WHERE user_id = 2 AND status = '0') as perm_count
|
||||
),
|
||||
expected AS (
|
||||
SELECT
|
||||
COUNT(DISTINCT s.scale_id) as scale_count,
|
||||
COUNT(DISTINCT q.questionnaire_id) as quest_count
|
||||
FROM user_info ui
|
||||
CROSS JOIN psy_scale s
|
||||
INNER JOIN psy_scale_permission p1 ON s.scale_id = p1.scale_id AND p1.user_id = ui.uid
|
||||
CROSS JOIN psy_questionnaire q
|
||||
INNER JOIN psy_scale_permission p2 ON -q.questionnaire_id = p2.scale_id AND p2.user_id = ui.uid
|
||||
)
|
||||
SELECT
|
||||
ui.uid as 用户ID,
|
||||
ui.perm_count as 总权限数,
|
||||
e.scale_count as 应看到量表数,
|
||||
e.quest_count as 应看到问卷数,
|
||||
(e.scale_count + e.quest_count) as 应看到总数
|
||||
FROM user_info ui, expected e;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**修复完成时间**:2024年12月2日
|
||||
**版本**:v3.0 - 最终简化版
|
||||
**下一步**:重新编译部署 + 使用SQL脚本检查数据
|
||||
243
朗读功能修复说明.md
243
朗读功能修复说明.md
|
|
@ -1,243 +0,0 @@
|
|||
# 🔧 Android App 朗读功能修复说明
|
||||
|
||||
## 📋 问题描述
|
||||
|
||||
在 Android App 中,用户登录后进行量表测评时,朗读功能按钮呈灰色禁用状态,无法使用。
|
||||
|
||||
## 🔍 问题根源
|
||||
|
||||
### 核心问题:TTS 异步初始化导致的时序问题
|
||||
|
||||
1. **Android 端**:`TtsHelper.java` 中的 TTS 引擎初始化是**异步**的
|
||||
- TTS 引擎通过 `OnInitListener` 回调初始化
|
||||
- `isReady` 标志只有在回调成功后才设置为 `true`
|
||||
- 初始化需要一定时间(通常 100-500ms)
|
||||
|
||||
2. **前端端**:页面在 `created()` 生命周期钩子中立即检查 TTS 可用性
|
||||
- 调用 `window.AndroidTTS.isAvailable()`
|
||||
- 此时 TTS 可能还未初始化完成
|
||||
- 返回 `false`,导致 `isTtsSupported = false`
|
||||
|
||||
3. **结果**:所有朗读按钮被禁用
|
||||
- 按钮属性:`:disabled="!isTtsSupported"`
|
||||
- 用户看到的是灰色不可点击的按钮
|
||||
|
||||
## ✅ 修复方案
|
||||
|
||||
### 1. Android 端修复 (TtsHelper.java)
|
||||
|
||||
#### 修改点 1: isAvailable() 方法
|
||||
**修改前**:
|
||||
```java
|
||||
@JavascriptInterface
|
||||
public boolean isAvailable() {
|
||||
return isReady; // 等待异步初始化完成
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```java
|
||||
@JavascriptInterface
|
||||
public boolean isAvailable() {
|
||||
// 只要TTS对象存在就返回true,不等待异步初始化完成
|
||||
// 这样前端可以立即启用朗读按钮
|
||||
return tts != null;
|
||||
}
|
||||
```
|
||||
|
||||
#### 修改点 2: speak() 方法增加延迟重试机制
|
||||
**新增功能**:
|
||||
- 如果 TTS 未就绪,自动延迟 500ms 后重试
|
||||
- 避免因初始化未完成而静默失败
|
||||
- 提供更好的用户体验
|
||||
|
||||
### 2. 前端端修复
|
||||
|
||||
#### 2.1 测评答题页面 (assessment/taking.vue)
|
||||
|
||||
**新增延迟检查逻辑**:
|
||||
```javascript
|
||||
initTts() {
|
||||
if (window.AndroidTTS && typeof window.AndroidTTS.isAvailable === 'function') {
|
||||
if (window.AndroidTTS.isAvailable()) {
|
||||
this.isTtsSupported = true;
|
||||
console.log('✅ 使用Android原生TTS');
|
||||
return;
|
||||
} else {
|
||||
// TTS可能还在初始化中,延迟重试
|
||||
setTimeout(() => {
|
||||
if (window.AndroidTTS && window.AndroidTTS.isAvailable()) {
|
||||
this.isTtsSupported = true;
|
||||
console.log('✅ Android TTS 已就绪(延迟检测)');
|
||||
} else {
|
||||
this.fallbackToBrowserTts();
|
||||
}
|
||||
}, 500);
|
||||
// 先启用按钮,避免用户等待
|
||||
this.isTtsSupported = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.fallbackToBrowserTts();
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 问卷答题页面 (questionnaire/taking.vue)
|
||||
|
||||
**新增 Android TTS 支持**:
|
||||
- 之前只支持浏览器 TTS
|
||||
- 现在添加了与测评页面相同的 Android TTS 支持
|
||||
- 确保所有答题页面功能一致
|
||||
|
||||
## 📝 修改文件清单
|
||||
|
||||
### Android 代码
|
||||
- ✅ `xinli-App/app/src/main/java/com/xinli/app/TtsHelper.java`
|
||||
- 修改 `isAvailable()` 方法
|
||||
- 改进 `speak()` 方法,添加延迟重试
|
||||
|
||||
### 前端代码
|
||||
- ✅ `xinli-ui/src/views/psychology/assessment/taking.vue`
|
||||
- 改进 `initTts()` 方法
|
||||
- 新增 `fallbackToBrowserTts()` 方法
|
||||
|
||||
- ✅ `xinli-ui/src/views/psychology/questionnaire/taking.vue`
|
||||
- 添加 Android TTS 支持
|
||||
- 统一 `initTts()` 逻辑
|
||||
- 更新 `speakText()` 和 `stopSpeaking()` 方法
|
||||
|
||||
## 🚀 重新打包和部署步骤
|
||||
|
||||
### 步骤 1: 重新构建前端
|
||||
```bash
|
||||
cd c:\Users\Administrator\Desktop\Project\xinli\xinli-ui
|
||||
npm run build:prod
|
||||
```
|
||||
|
||||
### 步骤 2: 将构建好的前端文件复制到服务器
|
||||
将 `xinli-ui/dist` 目录的文件部署到服务器的 Web 目录。
|
||||
|
||||
### 步骤 3: 打包 Android App
|
||||
```bash
|
||||
cd c:\Users\Administrator\Desktop\Project\xinli\xinli-App
|
||||
.\打包正式版APK.bat
|
||||
```
|
||||
|
||||
或者在 Android Studio 中:
|
||||
1. 打开项目
|
||||
2. 点击 `Build` → `Build Bundle(s) / APK(s)` → `Build APK(s)`
|
||||
3. 等待构建完成
|
||||
|
||||
### 步骤 4: 查找生成的 APK
|
||||
运行:
|
||||
```bash
|
||||
.\查找APK.bat
|
||||
```
|
||||
|
||||
APK 通常位于:
|
||||
- `xinli-App/app/build/outputs/apk/release/app-release.apk`
|
||||
|
||||
### 步骤 5: 安装测试
|
||||
1. 卸载旧版本 App(重要!)
|
||||
2. 安装新的 APK
|
||||
3. 打开 App,登录账号
|
||||
4. 进入量表测评或问卷答题页面
|
||||
5. 测试朗读功能
|
||||
|
||||
## ✅ 测试检查清单
|
||||
|
||||
### 测评答题页面
|
||||
- [ ] 朗读按钮不再是灰色
|
||||
- [ ] 点击"朗读全部"能听到声音
|
||||
- [ ] 点击"朗读题干"能听到声音
|
||||
- [ ] 点击选项旁的朗读按钮能听到声音
|
||||
- [ ] 声音清晰,中文标准
|
||||
- [ ] 可以正常停止朗读
|
||||
- [ ] 切换题目后朗读功能正常
|
||||
|
||||
### 问卷答题页面
|
||||
- [ ] 朗读按钮不再是灰色
|
||||
- [ ] "朗读全部"功能正常
|
||||
- [ ] "朗读题干"功能正常
|
||||
- [ ] 选项朗读功能正常
|
||||
- [ ] 各种题型(单选、多选、填空等)的朗读功能都正常
|
||||
|
||||
### 控制台日志检查
|
||||
打开 Chrome DevTools(如果是调试版),查看控制台:
|
||||
- ✅ 应该看到:`✅ 使用Android原生TTS`
|
||||
- ✅ 或者:`✅ Android TTS 已就绪(延迟检测)`
|
||||
- ❌ 不应该看到:朗读按钮被禁用的警告
|
||||
|
||||
## 🎯 技术改进说明
|
||||
|
||||
### 优化点 1: 消除时序依赖
|
||||
- **之前**:前端必须等待 Android TTS 完全初始化
|
||||
- **现在**:立即启用按钮,TTS 在后台完成初始化
|
||||
|
||||
### 优化点 2: 双重保障机制
|
||||
- **第一层**:Android 端延迟重试(500ms)
|
||||
- **第二层**:前端端延迟检查(500ms)
|
||||
- 确保 TTS 功能高可用性
|
||||
|
||||
### 优化点 3: 降级策略
|
||||
- Android TTS 不可用时自动降级到浏览器 TTS
|
||||
- 确保功能在所有环境都能工作
|
||||
|
||||
### 优化点 4: 统一体验
|
||||
- 测评和问卷两个页面使用相同的 TTS 逻辑
|
||||
- 代码一致性提高,便于维护
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
### Q1: 重新安装后还是没声音?
|
||||
**A**: 检查以下几点:
|
||||
1. 确认已**完全卸载旧版本** App
|
||||
2. 检查手机**媒体音量**(不是铃声音量)
|
||||
3. 确保不是静音模式
|
||||
4. 重启 App
|
||||
5. 查看控制台日志,确认使用了 Android TTS
|
||||
|
||||
### Q2: 声音很奇怪/机器人声音?
|
||||
**A**:
|
||||
- 某些手机的默认 TTS 引擎音质较差
|
||||
- 可以在手机设置中更换 TTS 引擎:
|
||||
- 系统设置 → 辅助功能 → 文字转语音
|
||||
- 推荐安装:Google 文字转语音引擎
|
||||
|
||||
### Q3: 浏览器中还能用吗?
|
||||
**A**:
|
||||
- ✅ 完全兼容!
|
||||
- 浏览器中自动使用浏览器 TTS
|
||||
- PC 端功能不受影响
|
||||
|
||||
### Q4: 如何确认使用的是哪种 TTS?
|
||||
**A**:
|
||||
打开 Chrome DevTools,查看控制台日志:
|
||||
- `✅ 使用Android原生TTS` - Android 原生
|
||||
- `✅ 使用浏览器TTS` - 浏览器 TTS
|
||||
|
||||
## 📊 修复效果
|
||||
|
||||
| 项目 | 修复前 | 修复后 |
|
||||
|------|--------|--------|
|
||||
| 朗读按钮状态 | ❌ 灰色禁用 | ✅ 正常可用 |
|
||||
| TTS 初始化时间 | 等待完成(阻塞) | 异步加载(不阻塞) |
|
||||
| 用户体验 | 功能不可用 | 立即可用 |
|
||||
| 降级策略 | ❌ 无 | ✅ 自动降级到浏览器TTS |
|
||||
| 问卷答题支持 | ❌ 仅浏览器TTS | ✅ Android原生TTS |
|
||||
| 代码一致性 | 测评和问卷不同 | 统一实现 |
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
此次修复完全解决了朗读功能在 Android App 中被禁用的问题:
|
||||
|
||||
1. ✅ **根本问题已修复**:TTS 异步初始化不再阻塞功能使用
|
||||
2. ✅ **用户体验改善**:朗读按钮立即可用,无需等待
|
||||
3. ✅ **功能完整性**:测评和问卷页面都支持朗读
|
||||
4. ✅ **健壮性提升**:双重保障+自动降级,确保高可用性
|
||||
5. ✅ **代码质量**:统一实现,易于维护
|
||||
|
||||
---
|
||||
|
||||
**修复日期**: 2024年12月2日
|
||||
**修复版本**: v1.1
|
||||
261
权限逻辑修复说明.md
261
权限逻辑修复说明.md
|
|
@ -1,261 +0,0 @@
|
|||
# 🔧 权限逻辑修复说明
|
||||
|
||||
## 📋 问题描述
|
||||
|
||||
**用户反馈**:
|
||||
- ❌ 分配了权限的问卷/量表**看不到**
|
||||
- ✅ 没有分配权限的问卷/量表**却能看到**
|
||||
- 权限逻辑完全相反!
|
||||
|
||||
## 🔍 问题根源
|
||||
|
||||
### 原来的错误逻辑
|
||||
|
||||
```java
|
||||
// 错误逻辑(已修复)
|
||||
if ("questionnaire".equalsIgnoreCase(sourceType))
|
||||
{
|
||||
boolean restricted = restrictedScaleIds != null && restrictedScaleIds.contains(scaleId);
|
||||
if (!restricted) // 如果未配置权限,就显示 ❌
|
||||
{
|
||||
filtered.add(scale);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (scaleId != null && allowedScaleIds.contains(scaleId)) // 如果有权限,才显示
|
||||
{
|
||||
filtered.add(scale);
|
||||
}
|
||||
```
|
||||
|
||||
**问题**:
|
||||
1. 未配置权限的问卷/量表 → 对所有人开放(错误!)
|
||||
2. 已配置权限的问卷/量表 → 只有授权用户能看到
|
||||
3. 结果:用户看到的都是"没分配权限"的内容
|
||||
|
||||
### 正确的逻辑
|
||||
|
||||
```java
|
||||
// 正确逻辑(已修复)
|
||||
if (scaleId != null && allowedScaleIds.contains(scaleId))
|
||||
{
|
||||
filtered.add(scale);
|
||||
}
|
||||
```
|
||||
|
||||
**修复后**:
|
||||
- ✅ 用户只能看到**分配给自己**的问卷/量表
|
||||
- ❌ 未分配的问卷/量表**完全看不到**
|
||||
|
||||
## ✅ 已修复内容
|
||||
|
||||
### 修改文件
|
||||
|
||||
1. **PsyQuestionnaireController.java**
|
||||
- 修改 `filterQuestionnaireListByPermission()` 方法
|
||||
- 移除错误的"公开未配置权限问卷"逻辑
|
||||
- 改为:只显示用户有权限的问卷
|
||||
|
||||
2. **PsyScaleController.java**
|
||||
- 修改 `filterScaleListByPermission()` 方法
|
||||
- 统一量表和问卷的权限逻辑
|
||||
- 移除 `resolveRestrictedScaleIds()` 方法(不再需要)
|
||||
|
||||
### 核心修改
|
||||
|
||||
#### 修改前
|
||||
```java
|
||||
// 如果该问卷未配置权限,对所有人开放 ❌
|
||||
boolean restricted = restrictedScaleIds != null && restrictedScaleIds.contains(scaleId);
|
||||
if (!restricted)
|
||||
{
|
||||
filtered.add(questionnaire);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果配置了权限,检查用户是否有权限
|
||||
if (allowedScaleIds.contains(scaleId))
|
||||
{
|
||||
filtered.add(questionnaire);
|
||||
}
|
||||
```
|
||||
|
||||
#### 修改后
|
||||
```java
|
||||
// 只显示用户有权限的问卷 ✅
|
||||
if (allowedScaleIds.contains(scaleId))
|
||||
{
|
||||
filtered.add(questionnaire);
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 修复后的行为
|
||||
|
||||
### 场景 1: 管理员(userId = 1 或有管理权限)
|
||||
- ✅ 看到**所有**问卷和量表
|
||||
- 理由:`allowedScaleIds = null`,不进行权限过滤
|
||||
|
||||
### 场景 2: 普通用户(student 角色)
|
||||
|
||||
**假设**:
|
||||
- 系统中有 5 个问卷:A、B、C、D、E
|
||||
- 用户被分配了问卷 A、B 的权限
|
||||
|
||||
**修复前**:
|
||||
- ❌ 能看到:C、D、E(未分配权限的)
|
||||
- ❌ 看不到:A、B(已分配权限的)
|
||||
|
||||
**修复后**:
|
||||
- ✅ 能看到:A、B(已分配权限的)
|
||||
- ✅ 看不到:C、D、E(未分配权限的)
|
||||
|
||||
## 📊 权限分配说明
|
||||
|
||||
### 如何为用户分配问卷权限
|
||||
|
||||
#### 方法 1: 通过用户管理界面
|
||||
1. 进入 **系统管理** → **用户管理**
|
||||
2. 找到目标用户,点击 **编辑**
|
||||
3. 在"量表权限"选项卡中勾选该用户可访问的问卷
|
||||
4. 保存
|
||||
|
||||
#### 方法 2: 通过 SQL 直接插入
|
||||
```sql
|
||||
-- 为用户分配问卷权限
|
||||
-- 注意:问卷ID使用负数(-questionnaireId)
|
||||
INSERT INTO psy_scale_permission (user_id, scale_id, status, create_by, create_time)
|
||||
VALUES
|
||||
(用户ID, -问卷ID, '0', 'admin', NOW());
|
||||
|
||||
-- 示例:为用户ID=2 分配问卷ID=1 的权限
|
||||
INSERT INTO psy_scale_permission (user_id, scale_id, status, create_by, create_time)
|
||||
VALUES (2, -1, '0', 'admin', NOW());
|
||||
|
||||
-- 为用户ID=2 分配量表ID=3 的权限
|
||||
INSERT INTO psy_scale_permission (user_id, scale_id, status, create_by, create_time)
|
||||
VALUES (2, 3, '0', 'admin', NOW());
|
||||
```
|
||||
|
||||
### 问卷ID的特殊标识
|
||||
|
||||
**重要**:在权限表中,问卷使用**负数ID**!
|
||||
|
||||
- **量表**:使用正数 ID (1, 2, 3, ...)
|
||||
- **问卷**:使用负数 ID (-1, -2, -3, ...)
|
||||
|
||||
**原因**:量表和问卷使用同一张权限表,用负数区分
|
||||
|
||||
**示例**:
|
||||
```
|
||||
问卷ID = 5 → 权限表中存储为 scale_id = -5
|
||||
量表ID = 5 → 权限表中存储为 scale_id = 5
|
||||
```
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 测试步骤
|
||||
|
||||
#### 准备数据
|
||||
```sql
|
||||
-- 1. 创建测试用户(假设ID=100)
|
||||
INSERT INTO sys_user (user_name, nick_name, password, status)
|
||||
VALUES ('test_user', '测试用户', '加密后的密码', '0');
|
||||
|
||||
-- 2. 分配学员角色
|
||||
-- (根据你的系统具体操作)
|
||||
|
||||
-- 3. 分配权限:只分配问卷ID=1和问卷ID=2的权限
|
||||
INSERT INTO psy_scale_permission (user_id, scale_id, status, create_by, create_time)
|
||||
VALUES
|
||||
(100, -1, '0', 'admin', NOW()),
|
||||
(100, -2, '0', 'admin', NOW());
|
||||
```
|
||||
|
||||
#### 测试登录
|
||||
1. 使用 test_user 登录系统
|
||||
2. 进入"心理测评"或"学员测试"页面
|
||||
3. **预期结果**:
|
||||
- ✅ 只能看到问卷1和问卷2
|
||||
- ❌ 看不到其他问卷(3、4、5...)
|
||||
|
||||
#### 测试管理员
|
||||
1. 使用管理员账号登录
|
||||
2. 进入"心理测评"页面
|
||||
3. **预期结果**:
|
||||
- ✅ 能看到所有问卷和量表
|
||||
|
||||
## 🚀 部署步骤
|
||||
|
||||
### 1. 重新编译后端
|
||||
```bash
|
||||
cd c:\Users\Administrator\Desktop\Project\xinli
|
||||
mvn clean package -DskipTests
|
||||
```
|
||||
|
||||
### 2. 部署新的 jar 文件
|
||||
```bash
|
||||
# 停止旧服务
|
||||
# 根据你的部署方式(systemd、supervisor、手动等)
|
||||
|
||||
# 部署新 jar
|
||||
# 位置:ry-xinli-admin/target/ry-xinli-admin.jar
|
||||
|
||||
# 启动新服务
|
||||
```
|
||||
|
||||
### 3. 验证修复
|
||||
1. 清除浏览器缓存
|
||||
2. 使用普通用户登录
|
||||
3. 查看问卷列表
|
||||
4. 确认只能看到已分配权限的问卷
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
### 1. 前端无需修改
|
||||
- ✅ 前端代码不需要改动
|
||||
- ✅ 权限过滤在后端完成
|
||||
- ✅ 前端调用 API 保持不变
|
||||
|
||||
### 2. 数据库无需修改
|
||||
- ✅ 使用现有的 `psy_scale_permission` 表
|
||||
- ✅ 不需要执行 SQL 脚本
|
||||
- ✅ 现有权限数据继续有效
|
||||
|
||||
### 3. 向后兼容性
|
||||
- ⚠️ **行为变化**:修复后,未分配权限的用户看不到任何问卷
|
||||
- ⚠️ 需要为现有用户重新分配权限
|
||||
- ⚠️ 建议先在测试环境验证
|
||||
|
||||
### 4. 批量分配权限
|
||||
如果需要为多个用户批量分配权限:
|
||||
|
||||
```sql
|
||||
-- 为所有学员角色的用户分配问卷1的权限
|
||||
INSERT INTO psy_scale_permission (user_id, scale_id, status, create_by, create_time)
|
||||
SELECT u.user_id, -1, '0', 'admin', NOW()
|
||||
FROM sys_user u
|
||||
INNER JOIN sys_user_role ur ON u.user_id = ur.user_id
|
||||
INNER JOIN sys_role r ON ur.role_id = r.role_id
|
||||
WHERE r.role_key = 'student'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM psy_scale_permission p
|
||||
WHERE p.user_id = u.user_id AND p.scale_id = -1
|
||||
);
|
||||
```
|
||||
|
||||
## ✅ 修复总结
|
||||
|
||||
| 项目 | 修复前 | 修复后 |
|
||||
|------|--------|--------|
|
||||
| **权限逻辑** | ❌ 相反 | ✅ 正确 |
|
||||
| **用户看到的** | 未分配的内容 | 已分配的内容 |
|
||||
| **管理员** | ✅ 看所有 | ✅ 看所有 |
|
||||
| **普通用户** | ❌ 看错误内容 | ✅ 只看授权内容 |
|
||||
| **代码复杂度** | 复杂(双重判断) | 简化(单一判断) |
|
||||
|
||||
---
|
||||
|
||||
**修复时间**:2024年12月2日
|
||||
**修复人员**:Cascade AI
|
||||
**影响范围**:问卷和量表列表的权限控制
|
||||
**部署状态**:⏳ 待重新编译部署后端
|
||||
187
检查二维码URL的方法.md
187
检查二维码URL的方法.md
|
|
@ -1,187 +0,0 @@
|
|||
# 检查二维码URL的方法
|
||||
|
||||
## 方法一:在二维码管理页面查看(推荐)
|
||||
|
||||
### 步骤:
|
||||
1. 登录管理系统
|
||||
2. 进入"二维码管理"页面
|
||||
3. 找到要检查的二维码记录
|
||||
4. 点击"查看二维码"按钮
|
||||
5. 在二维码预览对话框中,可以看到二维码图片
|
||||
6. **查看浏览器控制台**:
|
||||
- 按 `F12` 打开开发者工具
|
||||
- 切换到 `Console`(控制台)标签
|
||||
- 在控制台中输入以下代码查看二维码URL:
|
||||
```javascript
|
||||
// 查看当前页面的二维码数据
|
||||
console.log('二维码列表数据:', document.querySelectorAll('.el-table__body tr'));
|
||||
```
|
||||
|
||||
### 或者直接查看网络请求:
|
||||
1. 打开浏览器开发者工具(F12)
|
||||
2. 切换到 `Network`(网络)标签
|
||||
3. 刷新二维码管理页面
|
||||
4. 找到获取二维码列表的请求(通常是 `/psychology/qrcode/list`)
|
||||
5. 查看响应数据,找到 `qrcodeUrl` 字段
|
||||
6. 或者找到获取二维码图片的请求(通常是 `/psychology/qrcode/image/{qrcodeId}`)
|
||||
7. 查看响应数据中的URL
|
||||
|
||||
## 方法二:解码二维码查看内容
|
||||
|
||||
### 使用在线工具:
|
||||
1. 访问在线二维码解码工具,例如:
|
||||
- https://www.qrcode-monkey.com/qr-code-decoder/
|
||||
- https://zxing.org/w/decode.jspx
|
||||
2. 上传二维码图片或输入二维码图片URL
|
||||
3. 查看解码后的文本内容,这就是二维码的URL
|
||||
|
||||
### 使用手机扫码:
|
||||
1. 使用手机扫描二维码
|
||||
2. 查看手机浏览器地址栏中的URL
|
||||
3. 检查URL是否正确:
|
||||
- ✅ 正确格式:`http://192.168.1.183/psychology/qrcode/scan/xxx`
|
||||
- ❌ 错误格式:`http://192.168.1.183:30081//psychology/qrcode/scan/xxx`
|
||||
|
||||
## 方法三:检查后端日志
|
||||
|
||||
### 步骤:
|
||||
1. 查看后端控制台日志
|
||||
2. 找到二维码生成相关的日志
|
||||
3. 查找包含 `buildServerAddress` 或 `buildQrcodeContent` 的日志
|
||||
4. 查看实际生成的URL
|
||||
|
||||
### 添加调试日志(可选):
|
||||
如果需要更详细的日志,可以在代码中添加:
|
||||
|
||||
```java
|
||||
// 在 PsyQrcodeController.java 的 enrichQrcodeWithBase64() 方法中
|
||||
String serverAddress = buildServerAddress();
|
||||
log.info("构建的服务器地址: {}", serverAddress);
|
||||
|
||||
// 在 PsyQrcodeServiceImpl.java 的 buildQrcodeContent() 方法中
|
||||
String content = buildQrcodeContent(qrcode, serverAddress);
|
||||
log.info("生成的二维码URL: {}", content);
|
||||
```
|
||||
|
||||
## 方法四:直接测试URL访问
|
||||
|
||||
### 步骤:
|
||||
1. 从二维码管理页面获取二维码编码(`qrcodeCode`)
|
||||
2. 手动构建URL:`http://192.168.1.183/psychology/qrcode/scan/{qrcodeCode}`
|
||||
3. 在浏览器中直接访问该URL
|
||||
4. 检查是否能正常访问:
|
||||
- ✅ 能访问:URL正确
|
||||
- ❌ 不能访问:检查URL格式和网络连接
|
||||
|
||||
## 方法五:使用浏览器开发者工具检查
|
||||
|
||||
### 步骤:
|
||||
1. 打开二维码管理页面
|
||||
2. 按 `F12` 打开开发者工具
|
||||
3. 切换到 `Network`(网络)标签
|
||||
4. 点击"查看二维码"按钮
|
||||
5. 在Network标签中找到获取二维码图片的请求
|
||||
6. 查看请求URL和响应数据
|
||||
7. 检查响应中的 `qrcodeUrl` 字段
|
||||
|
||||
### 或者使用控制台:
|
||||
在浏览器控制台中输入:
|
||||
```javascript
|
||||
// 获取二维码图片的Base64数据
|
||||
const qrcodeImage = document.querySelector('img[src^="data:image"]');
|
||||
if (qrcodeImage) {
|
||||
console.log('二维码图片URL:', qrcodeImage.src);
|
||||
// Base64数据很长,可以截取前100个字符查看
|
||||
console.log('二维码URL前缀:', qrcodeImage.src.substring(0, 100));
|
||||
}
|
||||
```
|
||||
|
||||
## 检查URL正确性的标准
|
||||
|
||||
### ✅ 正确的URL格式:
|
||||
- `http://192.168.1.183/psychology/qrcode/scan/xxx`
|
||||
- `http://localhost/psychology/qrcode/scan/xxx`
|
||||
- 不包含 `:30081` 端口号
|
||||
- 不包含双斜杠 `//`
|
||||
- 路径格式正确:`/psychology/qrcode/scan/{qrcodeCode}`
|
||||
|
||||
### ❌ 错误的URL格式:
|
||||
- `http://192.168.1.183:30081/psychology/qrcode/scan/xxx`(包含30081端口)
|
||||
- `http://192.168.1.183//psychology/qrcode/scan/xxx`(包含双斜杠)
|
||||
- `http://192.168.1.183:30081//psychology/qrcode/scan/xxx`(包含端口和双斜杠)
|
||||
|
||||
## 快速检查脚本
|
||||
|
||||
在浏览器控制台中运行以下脚本,可以快速检查所有二维码的URL:
|
||||
|
||||
```javascript
|
||||
// 检查二维码列表中的URL
|
||||
fetch('/dev-api/psychology/qrcode/list', {
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + localStorage.getItem('token')
|
||||
}
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200 && data.rows) {
|
||||
console.log('二维码列表:');
|
||||
data.rows.forEach((qrcode, index) => {
|
||||
console.log(`${index + 1}. 编码: ${qrcode.qrcodeCode}`);
|
||||
console.log(` 类型: ${qrcode.qrcodeType}`);
|
||||
// 如果有qrcodeUrl,检查URL格式
|
||||
if (qrcode.qrcodeUrl) {
|
||||
const urlMatch = qrcode.qrcodeUrl.match(/http[s]?:\/\/[^"']+/);
|
||||
if (urlMatch) {
|
||||
const url = urlMatch[0];
|
||||
console.log(` URL: ${url}`);
|
||||
// 检查是否有问题
|
||||
if (url.includes(':30081')) {
|
||||
console.warn(' ⚠️ 警告:URL包含30081端口!');
|
||||
}
|
||||
if (url.includes('//')) {
|
||||
console.warn(' ⚠️ 警告:URL包含双斜杠!');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 常见问题排查
|
||||
|
||||
### 问题1:URL包含30081端口
|
||||
**原因**:`buildServerAddress()` 方法没有正确识别后端端口
|
||||
**解决**:检查后端日志,确认 `serverPort` 是否为30081
|
||||
|
||||
### 问题2:URL包含双斜杠
|
||||
**原因**:路径拼接时产生了双斜杠
|
||||
**解决**:检查 `contextPath` 是否为 `/`,如果是则应该被忽略
|
||||
|
||||
### 问题3:URL无法访问
|
||||
**原因**:
|
||||
- URL格式错误
|
||||
- 网络连接问题
|
||||
- 前端服务未运行
|
||||
**解决**:
|
||||
- 检查URL格式
|
||||
- 确认前端服务运行在80端口
|
||||
- 确认手机和电脑在同一局域网内
|
||||
|
||||
## 推荐检查流程
|
||||
|
||||
1. **生成二维码后立即检查**:
|
||||
- 在二维码管理页面查看二维码
|
||||
- 使用浏览器开发者工具查看网络请求
|
||||
- 检查URL格式是否正确
|
||||
|
||||
2. **测试扫码功能**:
|
||||
- 使用手机扫描二维码
|
||||
- 检查是否能正常访问
|
||||
- 查看手机浏览器地址栏中的URL
|
||||
|
||||
3. **如果发现问题**:
|
||||
- 检查后端日志
|
||||
- 重新生成二维码
|
||||
- 确认修复后再次测试
|
||||
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
-- ============================================
|
||||
-- 检查和清理"所有用户"权限记录
|
||||
-- ============================================
|
||||
-- 说明:在权限系统中,如果 psy_scale_permission 表中存在
|
||||
-- user_id、role_id、dept_id 都为空的记录,则表示"所有用户"都有权限
|
||||
-- 这可能导致用户看到未明确授权的量表/问卷
|
||||
-- ============================================
|
||||
|
||||
-- 1. 查看所有"所有用户"权限记录
|
||||
SELECT
|
||||
p.permission_id,
|
||||
p.scale_id,
|
||||
CASE
|
||||
WHEN p.scale_id < 0 THEN (SELECT questionnaire_name FROM psy_questionnaire WHERE questionnaire_id = -p.scale_id)
|
||||
ELSE (SELECT scale_name FROM psy_scale WHERE scale_id = p.scale_id)
|
||||
END AS scale_name,
|
||||
CASE
|
||||
WHEN p.scale_id < 0 THEN '问卷'
|
||||
ELSE '量表'
|
||||
END AS type,
|
||||
p.start_time,
|
||||
p.end_time,
|
||||
p.status,
|
||||
p.create_time,
|
||||
p.remark
|
||||
FROM psy_scale_permission p
|
||||
WHERE p.user_id IS NULL
|
||||
AND p.role_id IS NULL
|
||||
AND p.dept_id IS NULL;
|
||||
|
||||
-- 2. 统计"所有用户"权限的数量
|
||||
SELECT
|
||||
COUNT(*) AS all_users_permission_count,
|
||||
SUM(CASE WHEN status = '0' THEN 1 ELSE 0 END) AS active_count,
|
||||
SUM(CASE WHEN status = '1' THEN 1 ELSE 0 END) AS inactive_count
|
||||
FROM psy_scale_permission
|
||||
WHERE user_id IS NULL
|
||||
AND role_id IS NULL
|
||||
AND dept_id IS NULL;
|
||||
|
||||
-- 3. 查看哪些量表/问卷被设置为"所有用户"可见
|
||||
SELECT
|
||||
CASE
|
||||
WHEN p.scale_id < 0 THEN '问卷'
|
||||
ELSE '量表'
|
||||
END AS type,
|
||||
CASE
|
||||
WHEN p.scale_id < 0 THEN (SELECT questionnaire_name FROM psy_questionnaire WHERE questionnaire_id = -p.scale_id)
|
||||
ELSE (SELECT scale_name FROM psy_scale WHERE scale_id = p.scale_id)
|
||||
END AS name,
|
||||
p.scale_id,
|
||||
p.status,
|
||||
p.start_time,
|
||||
p.end_time
|
||||
FROM psy_scale_permission p
|
||||
WHERE p.user_id IS NULL
|
||||
AND p.role_id IS NULL
|
||||
AND p.dept_id IS NULL
|
||||
AND p.status = '0' -- 只看有效的
|
||||
ORDER BY type, name;
|
||||
|
||||
-- ============================================
|
||||
-- 清理操作(谨慎执行!)
|
||||
-- ============================================
|
||||
|
||||
-- 4. 【可选】禁用所有"所有用户"权限(将状态改为无效)
|
||||
-- UPDATE psy_scale_permission
|
||||
-- SET status = '1',
|
||||
-- update_time = NOW(),
|
||||
-- remark = CONCAT(IFNULL(remark, ''), ' [已禁用-原为所有用户权限]')
|
||||
-- WHERE user_id IS NULL
|
||||
-- AND role_id IS NULL
|
||||
-- AND dept_id IS NULL
|
||||
-- AND status = '0';
|
||||
|
||||
-- 5. 【可选】删除所有"所有用户"权限记录
|
||||
-- DELETE FROM psy_scale_permission
|
||||
-- WHERE user_id IS NULL
|
||||
-- AND role_id IS NULL
|
||||
-- AND dept_id IS NULL;
|
||||
|
||||
-- 6. 【可选】删除指定量表/问卷的"所有用户"权限
|
||||
-- 将 YOUR_SCALE_ID 替换为实际的量表ID
|
||||
-- DELETE FROM psy_scale_permission
|
||||
-- WHERE scale_id = YOUR_SCALE_ID
|
||||
-- AND user_id IS NULL
|
||||
-- AND role_id IS NULL
|
||||
-- AND dept_id IS NULL;
|
||||
180
检查权限数据.sql
180
检查权限数据.sql
|
|
@ -1,180 +0,0 @@
|
|||
-- ========================================
|
||||
-- 权限数据检查 SQL 脚本
|
||||
-- ========================================
|
||||
|
||||
-- 1. 查看所有用户及其权限数量
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.user_name,
|
||||
u.nick_name,
|
||||
COUNT(DISTINCT p.scale_id) as 权限数量
|
||||
FROM sys_user u
|
||||
LEFT JOIN psy_scale_permission p ON u.user_id = p.user_id AND p.status = '0'
|
||||
GROUP BY u.user_id, u.user_name, u.nick_name
|
||||
ORDER BY u.user_id;
|
||||
|
||||
-- 2. 查看某个用户的权限详情(替换 {USER_ID} 为实际用户ID)
|
||||
-- 示例:查看用户ID=2的权限
|
||||
SELECT
|
||||
p.permission_id,
|
||||
p.user_id,
|
||||
p.scale_id,
|
||||
CASE
|
||||
WHEN p.scale_id > 0 THEN '量表'
|
||||
WHEN p.scale_id < 0 THEN '问卷'
|
||||
ELSE '未知'
|
||||
END as 类型,
|
||||
CASE
|
||||
WHEN p.scale_id > 0 THEN s.scale_name
|
||||
WHEN p.scale_id < 0 THEN q.questionnaire_name
|
||||
ELSE NULL
|
||||
END as 名称,
|
||||
p.status,
|
||||
p.create_time
|
||||
FROM psy_scale_permission p
|
||||
LEFT JOIN psy_scale s ON p.scale_id = s.scale_id AND p.scale_id > 0
|
||||
LEFT JOIN psy_questionnaire q ON p.scale_id = -q.questionnaire_id AND p.scale_id < 0
|
||||
WHERE p.user_id = 2 -- 替换为实际用户ID
|
||||
AND p.status = '0'
|
||||
ORDER BY p.scale_id;
|
||||
|
||||
-- 3. 查看所有量表及其权限分配情况
|
||||
SELECT
|
||||
s.scale_id,
|
||||
s.scale_name,
|
||||
s.status as 量表状态,
|
||||
COUNT(DISTINCT p.user_id) as 分配用户数
|
||||
FROM psy_scale s
|
||||
LEFT JOIN psy_scale_permission p ON s.scale_id = p.scale_id AND p.status = '0'
|
||||
GROUP BY s.scale_id, s.scale_name, s.status
|
||||
ORDER BY s.scale_id;
|
||||
|
||||
-- 4. 查看所有问卷及其权限分配情况
|
||||
SELECT
|
||||
q.questionnaire_id,
|
||||
q.questionnaire_name,
|
||||
q.status as 问卷状态,
|
||||
-q.questionnaire_id as 权限表中的scale_id,
|
||||
COUNT(DISTINCT p.user_id) as 分配用户数
|
||||
FROM psy_questionnaire q
|
||||
LEFT JOIN psy_scale_permission p ON -q.questionnaire_id = p.scale_id AND p.status = '0'
|
||||
GROUP BY q.questionnaire_id, q.questionnaire_name, q.status
|
||||
ORDER BY q.questionnaire_id;
|
||||
|
||||
-- 5. 检查权限数据一致性(查找孤立的权限记录)
|
||||
-- 这些权限指向的量表/问卷不存在
|
||||
SELECT
|
||||
p.permission_id,
|
||||
p.user_id,
|
||||
p.scale_id,
|
||||
p.create_time,
|
||||
'权限记录存在但量表/问卷不存在' as 问题
|
||||
FROM psy_scale_permission p
|
||||
WHERE p.status = '0'
|
||||
AND p.scale_id > 0
|
||||
AND NOT EXISTS (SELECT 1 FROM psy_scale s WHERE s.scale_id = p.scale_id)
|
||||
UNION ALL
|
||||
SELECT
|
||||
p.permission_id,
|
||||
p.user_id,
|
||||
p.scale_id,
|
||||
p.create_time,
|
||||
'权限记录存在但问卷不存在' as 问题
|
||||
FROM psy_scale_permission p
|
||||
WHERE p.status = '0'
|
||||
AND p.scale_id < 0
|
||||
AND NOT EXISTS (SELECT 1 FROM psy_questionnaire q WHERE -q.questionnaire_id = p.scale_id);
|
||||
|
||||
-- 6. 为指定用户分配所有量表权限(示例)
|
||||
-- 注意:替换 {USER_ID} 为实际用户ID
|
||||
/*
|
||||
INSERT INTO psy_scale_permission (user_id, scale_id, status, create_by, create_time)
|
||||
SELECT
|
||||
2 as user_id, -- 替换为实际用户ID
|
||||
scale_id,
|
||||
'0',
|
||||
'admin',
|
||||
NOW()
|
||||
FROM psy_scale
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM psy_scale_permission p
|
||||
WHERE p.user_id = 2
|
||||
AND p.scale_id = psy_scale.scale_id
|
||||
);
|
||||
*/
|
||||
|
||||
-- 7. 为指定用户分配所有问卷权限(示例)
|
||||
-- 注意:使用负数ID
|
||||
/*
|
||||
INSERT INTO psy_scale_permission (user_id, scale_id, status, create_by, create_time)
|
||||
SELECT
|
||||
2 as user_id, -- 替换为实际用户ID
|
||||
-questionnaire_id as scale_id,
|
||||
'0',
|
||||
'admin',
|
||||
NOW()
|
||||
FROM psy_questionnaire
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM psy_scale_permission p
|
||||
WHERE p.user_id = 2
|
||||
AND p.scale_id = -psy_questionnaire.questionnaire_id
|
||||
);
|
||||
*/
|
||||
|
||||
-- 8. 删除某个用户的所有权限(示例)
|
||||
/*
|
||||
DELETE FROM psy_scale_permission
|
||||
WHERE user_id = 2; -- 替换为实际用户ID
|
||||
*/
|
||||
|
||||
-- 9. 检查特定量表/问卷的权限分配
|
||||
-- 量表ID=1的权限分配情况
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.user_name,
|
||||
u.nick_name,
|
||||
p.status,
|
||||
p.create_time
|
||||
FROM psy_scale_permission p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE p.scale_id = 1 -- 量表ID
|
||||
ORDER BY p.create_time DESC;
|
||||
|
||||
-- 问卷ID=1的权限分配情况(注意使用负数)
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.user_name,
|
||||
u.nick_name,
|
||||
p.status,
|
||||
p.create_time
|
||||
FROM psy_scale_permission p
|
||||
INNER JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE p.scale_id = -1 -- 问卷ID=1 使用 -1
|
||||
ORDER BY p.create_time DESC;
|
||||
|
||||
-- 10. 快速诊断:查看用户应该能看到什么
|
||||
-- 替换 {USER_ID} 为实际用户ID
|
||||
WITH user_permissions AS (
|
||||
SELECT scale_id
|
||||
FROM psy_scale_permission
|
||||
WHERE user_id = 2 -- 替换为实际用户ID
|
||||
AND status = '0'
|
||||
)
|
||||
SELECT
|
||||
'量表' as 类型,
|
||||
s.scale_id,
|
||||
s.scale_name as 名称,
|
||||
s.status as 状态,
|
||||
CASE WHEN up.scale_id IS NOT NULL THEN '有权限' ELSE '无权限' END as 权限状态
|
||||
FROM psy_scale s
|
||||
LEFT JOIN user_permissions up ON s.scale_id = up.scale_id
|
||||
UNION ALL
|
||||
SELECT
|
||||
'问卷' as 类型,
|
||||
-q.questionnaire_id as scale_id,
|
||||
q.questionnaire_name as 名称,
|
||||
q.status as 状态,
|
||||
CASE WHEN up.scale_id IS NOT NULL THEN '有权限' ELSE '无权限' END as 权限状态
|
||||
FROM psy_questionnaire q
|
||||
LEFT JOIN user_permissions up ON -q.questionnaire_id = up.scale_id
|
||||
ORDER BY 类型, scale_id;
|
||||
42
检查重复信息编号.sql
42
检查重复信息编号.sql
|
|
@ -1,42 +0,0 @@
|
|||
-- 检查重复的信息编号 SQL 脚本
|
||||
-- 用于排查用户导入时"信息编号已存在"的问题
|
||||
|
||||
-- 1. 检查这些信息编号在数据库中是否真的存在
|
||||
SELECT
|
||||
info_number AS 信息编号,
|
||||
user_name AS 姓名,
|
||||
create_time AS 创建时间,
|
||||
create_by AS 创建人
|
||||
FROM psy_user_profile
|
||||
WHERE info_number IN ('34', '99', '114', '120', '121', '122', '123', '124')
|
||||
ORDER BY info_number;
|
||||
|
||||
-- 2. 检查数据库中所有重复的信息编号
|
||||
SELECT
|
||||
info_number AS 信息编号,
|
||||
COUNT(*) AS 重复次数,
|
||||
GROUP_CONCAT(user_name) AS 姓名列表,
|
||||
GROUP_CONCAT(profile_id) AS 档案ID列表
|
||||
FROM psy_user_profile
|
||||
WHERE info_number IS NOT NULL AND info_number != ''
|
||||
GROUP BY info_number
|
||||
HAVING COUNT(*) > 1
|
||||
ORDER BY COUNT(*) DESC;
|
||||
|
||||
-- 3. 查看最近导入的用户(最近100条)
|
||||
SELECT
|
||||
info_number AS 信息编号,
|
||||
user_name AS 姓名,
|
||||
create_time AS 创建时间,
|
||||
create_by AS 创建人
|
||||
FROM psy_user_profile
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 100;
|
||||
|
||||
-- 4. 统计信息编号的使用情况
|
||||
SELECT
|
||||
COUNT(DISTINCT info_number) AS 唯一信息编号数量,
|
||||
COUNT(*) AS 总记录数量,
|
||||
(COUNT(*) - COUNT(DISTINCT info_number)) AS 重复记录数量
|
||||
FROM psy_user_profile
|
||||
WHERE info_number IS NOT NULL AND info_number != '';
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
-- ============================================
|
||||
-- 清理已删除用户的档案数据
|
||||
-- 用途:解决"信息编号已存在"但列表中看不到的问题
|
||||
-- 日期:2024-12-02
|
||||
-- ============================================
|
||||
|
||||
-- 第一步:查看要删除的数据(确认)
|
||||
SELECT
|
||||
p.profile_id AS '档案ID',
|
||||
p.info_number AS '信息编号',
|
||||
p.user_id AS '关联用户ID',
|
||||
u.user_name AS '用户账号',
|
||||
u.del_flag AS '删除标记',
|
||||
CASE
|
||||
WHEN u.user_id IS NULL THEN '用户不存在'
|
||||
WHEN u.del_flag = '2' THEN '用户已删除'
|
||||
ELSE '正常'
|
||||
END AS '状态',
|
||||
p.create_time AS '创建时间'
|
||||
FROM psy_user_profile p
|
||||
LEFT JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.del_flag = '2' OR u.user_id IS NULL
|
||||
ORDER BY CAST(p.info_number AS UNSIGNED);
|
||||
|
||||
-- 第二步:备份要删除的数据(可选但推荐)
|
||||
CREATE TABLE IF NOT EXISTS psy_user_profile_deleted_backup_20241202 AS
|
||||
SELECT p.*
|
||||
FROM psy_user_profile p
|
||||
LEFT JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.del_flag = '2' OR u.user_id IS NULL;
|
||||
|
||||
-- 第三步:删除已删除用户的档案
|
||||
DELETE p FROM psy_user_profile p
|
||||
LEFT JOIN sys_user u ON p.user_id = u.user_id
|
||||
WHERE u.del_flag = '2' OR u.user_id IS NULL;
|
||||
|
||||
-- 查看删除结果
|
||||
SELECT CONCAT('已删除 ', ROW_COUNT(), ' 条档案数据') AS '执行结果';
|
||||
|
||||
-- ============================================
|
||||
-- 如果只想删除特定信息编号的档案(如114)
|
||||
-- ============================================
|
||||
-- DELETE FROM psy_user_profile WHERE info_number = '114';
|
||||
179
用户导入性能优化说明.md
179
用户导入性能优化说明.md
|
|
@ -1,179 +0,0 @@
|
|||
# 用户导入性能优化说明
|
||||
|
||||
## 问题分析
|
||||
|
||||
### 原有性能瓶颈
|
||||
1. **逐条处理**:使用for循环逐条处理每个用户
|
||||
2. **频繁数据库查询**:每条记录都要查询用户是否存在(N+1问题)
|
||||
3. **单条插入/更新**:每条记录单独执行INSERT/UPDATE操作
|
||||
4. **重复查询配置**:每次导入都查询默认密码配置
|
||||
|
||||
### 性能影响
|
||||
- 导入3000条数据需要执行 **3000+ 次数据库查询**
|
||||
- 导入3000条数据需要执行 **3000+ 次INSERT操作**
|
||||
- 预计导入时间:**15-30分钟**(取决于网络和数据库性能)
|
||||
|
||||
## 优化方案
|
||||
|
||||
### 1. 批量查询已存在用户
|
||||
**优化前**:
|
||||
```java
|
||||
// 每条记录查询一次
|
||||
SysUser u = userMapper.selectUserByUserName(user.getUserName());
|
||||
```
|
||||
|
||||
**优化后**:
|
||||
```java
|
||||
// 一次性查询所有用户名
|
||||
List<SysUser> existingUsers = userMapper.selectUsersByUserNames(userNameList);
|
||||
Map<String, SysUser> existingUserMap = existingUsers.stream()
|
||||
.collect(Collectors.toMap(SysUser::getUserName, u -> u));
|
||||
```
|
||||
|
||||
### 2. 批量插入新用户
|
||||
**优化前**:
|
||||
```java
|
||||
// 逐条插入
|
||||
for (SysUser user : userList) {
|
||||
userMapper.insertUser(user);
|
||||
}
|
||||
```
|
||||
|
||||
**优化后**:
|
||||
```java
|
||||
// 批量插入,每批500条
|
||||
int batchSize = 500;
|
||||
for (int i = 0; i < usersToInsert.size(); i += batchSize) {
|
||||
List<SysUser> batch = usersToInsert.subList(i, end);
|
||||
userMapper.batchInsertUser(batch);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 数据预处理
|
||||
- 一次性获取默认密码配置
|
||||
- 数据验证前置,减少无效数据的数据库操作
|
||||
- 使用Map缓存已存在用户,避免重复查询
|
||||
|
||||
### 4. 失败降级机制
|
||||
批量插入失败时,自动降级为逐条插入,确保数据完整性
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 新增Mapper方法
|
||||
|
||||
**SysUserMapper.java**
|
||||
```java
|
||||
/**
|
||||
* 批量新增用户信息
|
||||
*/
|
||||
public int batchInsertUser(@Param("list") List<SysUser> userList);
|
||||
|
||||
/**
|
||||
* 批量查询用户名是否存在
|
||||
*/
|
||||
public List<SysUser> selectUsersByUserNames(@Param("userNames") List<String> userNames);
|
||||
```
|
||||
|
||||
**SysUserMapper.xml**
|
||||
```xml
|
||||
<!-- 批量插入用户 -->
|
||||
<insert id="batchInsertUser" parameterType="java.util.List">
|
||||
insert into sys_user(...) values
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.deptId}, #{item.userName}, ...)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- 批量查询用户名 -->
|
||||
<select id="selectUsersByUserNames" resultMap="SysUserResult">
|
||||
<include refid="selectUserVo"/>
|
||||
where u.user_name in
|
||||
<foreach collection="userNames" item="userName" open="(" separator="," close=")">
|
||||
#{userName}
|
||||
</foreach>
|
||||
</select>
|
||||
```
|
||||
|
||||
## 性能提升
|
||||
|
||||
### 数据库操作次数对比
|
||||
|
||||
| 操作 | 优化前 | 优化后 | 减少比例 |
|
||||
|------|--------|--------|----------|
|
||||
| 查询用户是否存在 | 3000次 | 1次 | **99.97%** |
|
||||
| 插入用户记录 | 3000次 | 6次(500条/批) | **99.8%** |
|
||||
| 查询默认密码 | 3000次 | 1次 | **99.97%** |
|
||||
|
||||
### 预计性能提升
|
||||
|
||||
- **导入3000条数据**:
|
||||
- 优化前:15-30分钟
|
||||
- 优化后:**10-30秒**
|
||||
- **性能提升:30-180倍**
|
||||
|
||||
- **导入10000条数据**:
|
||||
- 优化前:50-100分钟
|
||||
- 优化后:**30-60秒**
|
||||
- **性能提升:50-200倍**
|
||||
|
||||
## 优化特性
|
||||
|
||||
### 1. 事务安全
|
||||
- 批量操作使用数据库事务
|
||||
- 失败时自动回滚,保证数据一致性
|
||||
|
||||
### 2. 错误处理
|
||||
- 批量插入失败时降级为逐条插入
|
||||
- 详细记录每条失败数据的错误信息
|
||||
- 部分失败不影响其他数据导入
|
||||
|
||||
### 3. 内存优化
|
||||
- 分批处理,每批500条
|
||||
- 避免大量数据一次性加载到内存
|
||||
- 适合超大数据量导入
|
||||
|
||||
### 4. 向后兼容
|
||||
- 保留原有API接口
|
||||
- 不需要修改前端代码
|
||||
- 完全透明的性能升级
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 导入步骤
|
||||
1. 准备Excel文件(支持.xls/.xlsx格式)
|
||||
2. 点击"导入"按钮
|
||||
3. 选择文件并上传
|
||||
4. 系统自动批量处理
|
||||
|
||||
### 注意事项
|
||||
1. **批量大小**:默认每批500条,可根据服务器性能调整
|
||||
2. **超时设置**:大量数据导入建议增加请求超时时间
|
||||
3. **数据格式**:确保Excel格式符合模板要求
|
||||
4. **唯一性检查**:用户名必须唯一
|
||||
|
||||
## 监控建议
|
||||
|
||||
### 日志监控
|
||||
```java
|
||||
log.info("开始导入用户,总数:{}", userList.size());
|
||||
log.info("批量插入用户成功,本批数量:{}", batch.size());
|
||||
log.warn("批量插入失败,降级为逐条插入", e);
|
||||
```
|
||||
|
||||
### 性能指标
|
||||
- 导入总耗时
|
||||
- 数据库操作次数
|
||||
- 成功/失败条数
|
||||
- 平均处理速度(条/秒)
|
||||
|
||||
## 未来优化方向
|
||||
|
||||
1. **异步导入**:大数据量导入改为异步任务
|
||||
2. **进度展示**:实时显示导入进度
|
||||
3. **并行处理**:使用多线程并行处理数据验证
|
||||
4. **Redis缓存**:缓存用户存在性检查结果
|
||||
5. **文件预检查**:上传前验证文件格式和数据有效性
|
||||
|
||||
## 总结
|
||||
|
||||
通过本次优化,用户导入速度提升了 **30-200倍**,从分钟级降低到秒级,显著改善了用户体验。优化采用批量处理、预查询、分批插入等技术手段,在保证数据完整性的同时大幅提升了性能。
|
||||
|
|
@ -1,217 +0,0 @@
|
|||
# 用户导入登录账号被错误修改问题修复说明
|
||||
|
||||
## 🐛 问题描述
|
||||
|
||||
导入用户时,出现了奇怪的登录账号,例如:
|
||||
```sql
|
||||
-- 日志显示
|
||||
updateUserName - Parameters: 这是导入的(String), 6571(Long)
|
||||
|
||||
-- 正常应该是
|
||||
updateUserName - Parameters: 13466(String), 13466(String), 6671(Long)
|
||||
```
|
||||
|
||||
**问题现象:**
|
||||
- Excel中某些行的**姓名列**填写了异常数据(如"这是导入的")
|
||||
- 更新用户档案时,系统把姓名同步到了**登录账号**(userName)
|
||||
- **用户无法登录**,因为登录账号被改成了姓名
|
||||
|
||||
## 🔍 问题根源
|
||||
|
||||
### 系统设计
|
||||
|
||||
**登录账号(userName)** = 信息编号(如"3466")
|
||||
**昵称(nickName)** = 姓名(如"张三")
|
||||
|
||||
### 代码逻辑
|
||||
|
||||
**1. 创建新用户时(正确):**
|
||||
```java
|
||||
// autoCreateUserByProfile
|
||||
user.setUserName(信息编号); // 登录账号 = 信息编号 ✅
|
||||
user.setNickName(Excel姓名); // 昵称 = Excel姓名 ✅
|
||||
```
|
||||
|
||||
**2. 更新用户时(错误):**
|
||||
```java
|
||||
// updateProfile → syncUserName
|
||||
userService.updateUserName(userId, userName, userName);
|
||||
// 同时更新了登录账号和昵称为Excel姓名 ❌
|
||||
```
|
||||
|
||||
### 问题流程
|
||||
|
||||
```
|
||||
导入Excel → profile.userName = "这是导入的"(来自Excel姓名列)
|
||||
↓
|
||||
updateProfile() → syncUserName(userId, "这是导入的")
|
||||
↓
|
||||
updateUserName(userId, "这是导入的", "这是导入的")
|
||||
↓
|
||||
sys_user表:
|
||||
user_name (登录账号) = "这是导入的" ← 错误!应该保持为信息编号
|
||||
nick_name (昵称) = "这是导入的" ← 正确
|
||||
```
|
||||
|
||||
## ✅ 修复方案
|
||||
|
||||
### 修改内容
|
||||
|
||||
**文件:** `PsyUserProfileServiceImpl.java` 第448-461行
|
||||
|
||||
**修改前:**
|
||||
```java
|
||||
private void syncUserName(Long userId, String userName)
|
||||
{
|
||||
if (userId == null || StringUtils.isEmpty(userName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
userService.updateUserName(userId, userName, userName);
|
||||
}
|
||||
```
|
||||
|
||||
**修改后:**
|
||||
```java
|
||||
private void syncUserName(Long userId, String userName)
|
||||
{
|
||||
if (userId == null || StringUtils.isEmpty(userName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 只更新昵称(nickName),不修改登录账号(userName保持为信息编号)
|
||||
// 获取当前用户,保持原有的登录账号不变
|
||||
SysUser user = userService.selectUserById(userId);
|
||||
if (user != null)
|
||||
{
|
||||
userService.updateUserName(userId, user.getUserName(), userName);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 修复效果
|
||||
|
||||
**修复前:**
|
||||
```
|
||||
更新用户 → user_name和nick_name都变成Excel姓名 ❌
|
||||
```
|
||||
|
||||
**修复后:**
|
||||
```
|
||||
更新用户 → user_name保持为信息编号,nick_name变成Excel姓名 ✅
|
||||
```
|
||||
|
||||
## 🔧 影响范围
|
||||
|
||||
### 已影响的用户
|
||||
|
||||
执行SQL查询受影响的用户:
|
||||
|
||||
```sql
|
||||
-- 查找登录账号不是纯数字的用户(异常)
|
||||
SELECT
|
||||
user_id,
|
||||
user_name AS 登录账号,
|
||||
nick_name AS 昵称,
|
||||
create_time AS 创建时间
|
||||
FROM sys_user
|
||||
WHERE user_name REGEXP '[^0-9]' -- 包含非数字字符
|
||||
AND del_flag = '0';
|
||||
|
||||
-- 查找对应的档案信息
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.user_name AS 当前登录账号,
|
||||
u.nick_name AS 昵称,
|
||||
p.info_number AS 正确的信息编号
|
||||
FROM sys_user u
|
||||
LEFT JOIN psy_user_profile p ON u.user_id = p.user_id
|
||||
WHERE u.user_name REGEXP '[^0-9]'
|
||||
AND u.del_flag = '0';
|
||||
```
|
||||
|
||||
### 修复受影响的用户
|
||||
|
||||
**手动修复SQL:**
|
||||
```sql
|
||||
-- 将登录账号恢复为信息编号
|
||||
UPDATE sys_user u
|
||||
INNER JOIN psy_user_profile p ON u.user_id = p.user_id
|
||||
SET u.user_name = p.info_number
|
||||
WHERE u.user_name REGEXP '[^0-9]' -- 包含非数字字符
|
||||
AND u.del_flag = '0'
|
||||
AND p.info_number IS NOT NULL
|
||||
AND p.info_number REGEXP '^[0-9]+$'; -- 信息编号是纯数字
|
||||
|
||||
-- 验证修复结果
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.user_name AS 登录账号,
|
||||
u.nick_name AS 昵称,
|
||||
p.info_number AS 信息编号
|
||||
FROM sys_user u
|
||||
LEFT JOIN psy_user_profile p ON u.user_id = p.user_id
|
||||
WHERE u.user_id IN (
|
||||
SELECT user_id FROM sys_user
|
||||
WHERE user_name REGEXP '[^0-9]'
|
||||
);
|
||||
```
|
||||
|
||||
## 📦 部署步骤
|
||||
|
||||
### 1. 备份数据库
|
||||
```sql
|
||||
-- 备份sys_user表
|
||||
CREATE TABLE sys_user_backup_20251202 AS SELECT * FROM sys_user;
|
||||
```
|
||||
|
||||
### 2. 修复已影响的用户(可选)
|
||||
运行上面的修复SQL,将异常的登录账号恢复为信息编号。
|
||||
|
||||
### 3. 重新编译后端
|
||||
```bash
|
||||
cd c:\Users\Administrator\Desktop\Project\xinli
|
||||
mvn clean package -DskipTests
|
||||
```
|
||||
|
||||
### 4. 重启后端服务
|
||||
重启后,新的更新操作将不再修改登录账号。
|
||||
|
||||
## 📝 预防措施
|
||||
|
||||
### 1. Excel数据验证
|
||||
导入前检查Excel数据:
|
||||
- **姓名列**:只能包含汉字和数字
|
||||
- **信息编号列**:只能包含数字
|
||||
- 使用Excel条件格式标记异常数据
|
||||
|
||||
### 2. 用户登录规则
|
||||
**记住:**
|
||||
- **登录账号** = 信息编号(纯数字)
|
||||
- **昵称** = 姓名(显示用)
|
||||
- 更新姓名**不会影响**登录账号
|
||||
|
||||
### 3. 导入最佳实践
|
||||
- ✅ 导入前检查数据规范性
|
||||
- ✅ 小批量测试导入
|
||||
- ✅ 导入后验证用户是否能正常登录
|
||||
- ✅ 备份数据后再大批量导入
|
||||
|
||||
## 🎯 总结
|
||||
|
||||
| 项目 | 修复前 | 修复后 |
|
||||
|------|-------|-------|
|
||||
| **创建用户** | userName=信息编号 ✅ | userName=信息编号 ✅ |
|
||||
| **更新用户** | userName=姓名 ❌ | userName=信息编号(保持不变)✅ |
|
||||
| **用户能否登录** | ❌ 无法登录 | ✅ 正常登录 |
|
||||
|
||||
**核心修复:**
|
||||
更新用户档案时,只同步姓名到昵称(nickName),保持登录账号(userName)为信息编号不变。
|
||||
|
||||
**影响评估:**
|
||||
- 已导入的异常用户需要手动修复
|
||||
- 修复后不再出现此问题
|
||||
- 不影响正常的用户创建和更新
|
||||
|
||||
**风险:** 低
|
||||
**优先级:** 高(影响用户登录)
|
||||
107
用户导入重复问题说明.md
107
用户导入重复问题说明.md
|
|
@ -1,107 +0,0 @@
|
|||
# 用户导入"信息编号已存在"问题说明
|
||||
|
||||
## 📋 问题描述
|
||||
|
||||
导入 3800 条用户数据时,有 11 条失败,提示"信息编号 XX 已存在":
|
||||
- 信息编号:34, 99, 114, 120, 121, 122, 123, 124 等
|
||||
|
||||
## 🔍 问题原因
|
||||
|
||||
经过代码分析,"信息编号已存在"有**两种可能**:
|
||||
|
||||
### 1. Excel 文件内部重复 ⭐ **最可能**
|
||||
- Excel 文件中有多条记录使用了相同的信息编号
|
||||
- 第一条记录导入成功
|
||||
- 第二条相同信息编号的记录尝试导入时,发现数据库中已存在(刚刚导入的那条),导致失败
|
||||
|
||||
**示例:**
|
||||
```
|
||||
Excel 中:
|
||||
行100: 信息编号=34, 姓名=张三
|
||||
行500: 信息编号=34, 姓名=李四 ← 这条会失败,提示"34 已存在"
|
||||
```
|
||||
|
||||
### 2. 数据库中已存在
|
||||
- 这些信息编号在之前的导入中已经创建过
|
||||
- 本次导入时没有勾选"是否更新已存在数据",导致失败
|
||||
|
||||
## ✅ 已修复内容
|
||||
|
||||
### 1. 添加 Excel 内部重复检查
|
||||
现在导入时会先检查 Excel 文件内部是否有重复的信息编号,提前拦截并给出明确提示。
|
||||
|
||||
**修改位置:** `PsyUserProfileServiceImpl.java` 第 529-536 行
|
||||
|
||||
```java
|
||||
// 检查Excel文件内部是否有重复的信息编号
|
||||
if (infoNumberSet.contains(profile.getInfoNumber())) {
|
||||
failureMsg.append("、信息编号 ").append(profile.getInfoNumber())
|
||||
.append(" 在导入文件中重复出现");
|
||||
continue;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 改进错误提示
|
||||
现在错误提示更清晰,能区分两种情况:
|
||||
- **Excel 内部重复**:`信息编号 XX 在导入文件中重复出现`
|
||||
- **数据库中已存在**:`信息编号 XX 在数据库中已存在(若需更新请勾选"是否更新已存在数据")`
|
||||
|
||||
## 🔧 排查方法
|
||||
|
||||
### 方法 1:运行 SQL 脚本检查
|
||||
执行 `检查重复信息编号.sql` 脚本,查看:
|
||||
1. 这些信息编号在数据库中是否真的存在
|
||||
2. 数据库中是否有其他重复的信息编号
|
||||
3. 最近导入的记录情况
|
||||
|
||||
### 方法 2:检查 Excel 文件
|
||||
在 Excel 中使用以下步骤检查重复:
|
||||
1. 选中信息编号列
|
||||
2. 点击"条件格式" → "突出显示单元格规则" → "重复值"
|
||||
3. Excel 会自动标记重复的信息编号
|
||||
|
||||
### 方法 3:使用 Excel 公式查找重复
|
||||
在新列输入公式:
|
||||
```excel
|
||||
=COUNTIF($A$2:$A$3801, A2)
|
||||
```
|
||||
如果结果 > 1,说明该信息编号重复出现。
|
||||
|
||||
## 📝 建议
|
||||
|
||||
### 给用户的建议
|
||||
1. **导入前清理 Excel**:
|
||||
- 检查并删除重复的信息编号
|
||||
- 或修改重复的信息编号,确保唯一性
|
||||
|
||||
2. **如果需要更新已存在数据**:
|
||||
- 导入时勾选"是否更新已存在数据"选项
|
||||
- 系统会自动更新而不是提示失败
|
||||
|
||||
3. **使用 SQL 脚本检查**:
|
||||
- 导入前运行 `检查重复信息编号.sql`
|
||||
- 确认哪些信息编号已在数据库中存在
|
||||
|
||||
### 下次导入优化建议
|
||||
1. ✅ 已添加 Excel 内部重复检查
|
||||
2. ✅ 已改进错误提示信息
|
||||
3. 🔄 建议:添加导入预检查功能,在实际导入前先检查并报告所有问题
|
||||
4. 🔄 建议:提供"忽略重复"选项,跳过重复记录继续导入其他数据
|
||||
|
||||
## 🚀 部署说明
|
||||
|
||||
修改已完成,需要重新编译后端:
|
||||
|
||||
```bash
|
||||
cd c:\Users\Administrator\Desktop\Project\xinli
|
||||
mvn clean package -DskipTests
|
||||
```
|
||||
|
||||
重启后端服务后,新的检查逻辑即可生效。
|
||||
|
||||
## 📊 问题总结
|
||||
|
||||
| 问题类型 | 新错误提示 | 解决方法 |
|
||||
|---------|-----------|---------|
|
||||
| Excel 内部重复 | `信息编号 XX 在导入文件中重复出现` | 清理 Excel 中的重复记录 |
|
||||
| 数据库已存在 | `信息编号 XX 在数据库中已存在(若需更新请勾选"是否更新已存在数据")` | 勾选更新选项,或修改信息编号 |
|
||||
154
用户档案导入问题修复说明.md
154
用户档案导入问题修复说明.md
|
|
@ -1,154 +0,0 @@
|
|||
# 用户档案导入问题修复说明
|
||||
|
||||
## 问题描述
|
||||
|
||||
用户在导入用户档案数据时,明明某些用户已被删除(在系统中看不到),但系统仍提示"用户已存在",导致无法正常导入。
|
||||
|
||||
## 问题原因
|
||||
|
||||
在 `PsyUserProfileMapper.xml` 中,多个档案查询方法缺少对已删除用户的过滤条件:
|
||||
|
||||
1. **`selectProfilesByInfoNumbers`** - 批量查询档案(导入时使用)
|
||||
2. **`selectProfileByInfoNumber`** - 根据信息编号查询(唯一性验证时使用)
|
||||
3. **`selectProfileById`** - 根据档案ID查询
|
||||
4. **`selectProfileByUserId`** - 根据用户ID查询
|
||||
|
||||
这些查询没有添加 `u.del_flag = '0'` 过滤条件,导致:
|
||||
- 当用户被删除(`del_flag = '2'`)后,在列表中看不到(列表查询有过滤)
|
||||
- 但导入时仍能查到这些已删除用户的档案(导入查询无过滤)
|
||||
- 系统误判为"用户已存在",拒绝导入
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 修复 `selectProfilesByInfoNumbers`(批量查询)
|
||||
|
||||
**位置**: `PsyUserProfileMapper.xml` 第279-288行
|
||||
|
||||
```xml
|
||||
<!-- 修复前 -->
|
||||
<select id="selectProfilesByInfoNumbers" resultMap="PsyUserProfileResult">
|
||||
<include refid="selectProfileVo"/>
|
||||
from psy_user_profile p
|
||||
left join sys_user u on p.user_id = u.user_id
|
||||
where p.info_number in
|
||||
<foreach item="infoNumber" collection="list" open="(" separator="," close=")">
|
||||
#{infoNumber}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<!-- 修复后 -->
|
||||
<select id="selectProfilesByInfoNumbers" resultMap="PsyUserProfileResult">
|
||||
<include refid="selectProfileVo"/>
|
||||
from psy_user_profile p
|
||||
left join sys_user u on p.user_id = u.user_id
|
||||
where u.del_flag = '0'
|
||||
and p.info_number in
|
||||
<foreach item="infoNumber" collection="list" open="(" separator="," close=")">
|
||||
#{infoNumber}
|
||||
</foreach>
|
||||
</select>
|
||||
```
|
||||
|
||||
### 2. 修复 `selectProfileByInfoNumber`(单个查询)
|
||||
|
||||
**位置**: `PsyUserProfileMapper.xml` 第62-68行
|
||||
|
||||
```xml
|
||||
<!-- 修复前 -->
|
||||
<select id="selectProfileByInfoNumber" parameterType="String" resultMap="PsyUserProfileResult">
|
||||
<include refid="selectProfileVo"/>
|
||||
from psy_user_profile p
|
||||
left join sys_user u on p.user_id = u.user_id
|
||||
where p.info_number = #{infoNumber}
|
||||
</select>
|
||||
|
||||
<!-- 修复后 -->
|
||||
<select id="selectProfileByInfoNumber" parameterType="String" resultMap="PsyUserProfileResult">
|
||||
<include refid="selectProfileVo"/>
|
||||
from psy_user_profile p
|
||||
left join sys_user u on p.user_id = u.user_id
|
||||
where p.info_number = #{infoNumber}
|
||||
and u.del_flag = '0'
|
||||
</select>
|
||||
```
|
||||
|
||||
### 3. 修复 `selectProfileById`
|
||||
|
||||
**位置**: `PsyUserProfileMapper.xml` 第46-52行
|
||||
|
||||
```xml
|
||||
<!-- 修复后 -->
|
||||
<select id="selectProfileById" parameterType="Long" resultMap="PsyUserProfileResult">
|
||||
<include refid="selectProfileVo"/>
|
||||
from psy_user_profile p
|
||||
left join sys_user u on p.user_id = u.user_id
|
||||
where p.profile_id = #{profileId}
|
||||
and u.del_flag = '0'
|
||||
</select>
|
||||
```
|
||||
|
||||
### 4. 修复 `selectProfileByUserId`
|
||||
|
||||
**位置**: `PsyUserProfileMapper.xml` 第54-60行
|
||||
|
||||
```xml
|
||||
<!-- 修复后 -->
|
||||
<select id="selectProfileByUserId" parameterType="Long" resultMap="PsyUserProfileResult">
|
||||
<include refid="selectProfileVo"/>
|
||||
from psy_user_profile p
|
||||
left join sys_user u on p.user_id = u.user_id
|
||||
where p.user_id = #{userId}
|
||||
and u.del_flag = '0'
|
||||
</select>
|
||||
```
|
||||
|
||||
## 修复效果
|
||||
|
||||
修复后,系统在以下场景中的行为将更加合理:
|
||||
|
||||
1. **导入档案时**: 不会再将已删除用户的档案识别为"已存在",可以正常导入
|
||||
2. **信息编号唯一性验证**: 不会因已删除用户的旧信息编号而拒绝新用户使用
|
||||
3. **查询档案**: 所有档案查询统一过滤已删除用户,保持数据一致性
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 测试场景1:导入已删除用户的档案
|
||||
1. 删除一个有档案的用户(设置 `del_flag = '2'`)
|
||||
2. 准备Excel文件,包含该用户的信息编号
|
||||
3. 导入档案数据
|
||||
4. **预期结果**: 成功导入,系统自动创建新用户
|
||||
|
||||
### 测试场景2:信息编号唯一性
|
||||
1. 删除用户A(信息编号:001)
|
||||
2. 创建新档案,使用信息编号:001
|
||||
3. **预期结果**: 成功创建,不提示"信息编号已存在"
|
||||
|
||||
### 测试场景3:正常导入(未删除用户)
|
||||
1. 准备包含现有用户信息编号的Excel
|
||||
2. 不勾选"更新已存在数据"
|
||||
3. 导入档案
|
||||
4. **预期结果**: 提示"信息编号已存在"(行为不变)
|
||||
|
||||
## 影响范围
|
||||
|
||||
**修改文件**:
|
||||
- `ry-xinli-system/src/main/resources/mapper/system/psychology/PsyUserProfileMapper.xml`
|
||||
|
||||
**影响模块**:
|
||||
- 用户档案导入功能
|
||||
- 用户档案查询功能
|
||||
- 信息编号唯一性验证
|
||||
|
||||
**兼容性**: 完全向后兼容,只是增加了已删除用户的过滤条件
|
||||
|
||||
## 部署说明
|
||||
|
||||
1. 更新代码后重新编译项目
|
||||
2. 重启应用服务器
|
||||
3. 无需数据库变更
|
||||
4. 建议清理旧的已删除用户档案数据(可选)
|
||||
|
||||
---
|
||||
|
||||
**修复日期**: 2024年12月2日
|
||||
**修复人**: Cascade AI Assistant
|
||||
441
系统优化说明文档.md
441
系统优化说明文档.md
|
|
@ -1,441 +0,0 @@
|
|||
# 系统优化说明文档
|
||||
|
||||
## 优化日期
|
||||
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<String> infoNumbers = profileList.stream()
|
||||
.map(PsyUserProfile::getInfoNumber)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 一次性查询所有已存在的记录
|
||||
List<PsyUserProfile> existingProfiles =
|
||||
profileMapper.selectProfilesByInfoNumbers(infoNumbers);
|
||||
```
|
||||
|
||||
**步骤2:数据分类**
|
||||
```java
|
||||
List<PsyUserProfile> toInsertList = new ArrayList<>(); // 待插入
|
||||
List<PsyUserProfile> 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<PsyUserProfile> batch = toInsertList.subList(i, i + batchSize);
|
||||
profileMapper.batchInsertProfiles(batch); // 批量插入500条
|
||||
}
|
||||
```
|
||||
|
||||
**步骤4:批量更新(每批500条)**
|
||||
```java
|
||||
for (int i = 0; i < toUpdateList.size(); i += batchSize) {
|
||||
List<PsyUserProfile> batch = toUpdateList.subList(i, i + batchSize);
|
||||
profileMapper.batchUpdateProfiles(batch); // 批量更新500条
|
||||
}
|
||||
```
|
||||
|
||||
### 修改的文件
|
||||
|
||||
#### 1. Mapper接口 - `PsyUserProfileMapper.java`
|
||||
|
||||
**新增方法**:
|
||||
```java
|
||||
/**
|
||||
* 批量查询档案(根据信息编号列表)
|
||||
*/
|
||||
public List<PsyUserProfile> selectProfilesByInfoNumbers(List<String> infoNumbers);
|
||||
|
||||
/**
|
||||
* 批量插入档案
|
||||
*/
|
||||
public int batchInsertProfiles(List<PsyUserProfile> profileList);
|
||||
|
||||
/**
|
||||
* 批量更新档案
|
||||
*/
|
||||
public int batchUpdateProfiles(List<PsyUserProfile> profileList);
|
||||
```
|
||||
|
||||
#### 2. Mapper XML - `PsyUserProfileMapper.xml`
|
||||
|
||||
**批量查询SQL**:
|
||||
```xml
|
||||
<select id="selectProfilesByInfoNumbers" resultMap="PsyUserProfileResult">
|
||||
SELECT * FROM psy_user_profile
|
||||
WHERE info_number IN
|
||||
<foreach item="infoNumber" collection="list" open="(" separator="," close=")">
|
||||
#{infoNumber}
|
||||
</foreach>
|
||||
</select>
|
||||
```
|
||||
|
||||
**批量插入SQL**:
|
||||
```xml
|
||||
<insert id="batchInsertProfiles">
|
||||
INSERT INTO psy_user_profile(...) VALUES
|
||||
<foreach item="item" collection="list" separator=",">
|
||||
(#{item.userId}, #{item.infoNumber}, ...)
|
||||
</foreach>
|
||||
</insert>
|
||||
```
|
||||
|
||||
**批量更新SQL**:
|
||||
```xml
|
||||
<update id="batchUpdateProfiles">
|
||||
<foreach item="item" collection="list" separator=";">
|
||||
UPDATE psy_user_profile SET ... WHERE profile_id = #{item.profileId}
|
||||
</foreach>
|
||||
</update>
|
||||
```
|
||||
|
||||
#### 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<PsyUserProfile> existingProfiles =
|
||||
profileMapper.selectProfilesByInfoNumbers(infoNumbers);
|
||||
|
||||
// 2. 分类处理
|
||||
List<PsyUserProfile> toInsertList = ...;
|
||||
List<PsyUserProfile> 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:
|
||||
- 准备相同的测试数据
|
||||
- 对比优化前后的导入时间
|
||||
- 检查数据库慢查询日志
|
||||
- 监控服务器资源使用情况
|
||||
337
纯前端TTS功能使用说明.md
337
纯前端TTS功能使用说明.md
|
|
@ -1,337 +0,0 @@
|
|||
# 纯前端 TTS 功能使用说明
|
||||
|
||||
## 📋 方案概述
|
||||
|
||||
基于浏览器 **Web Speech API** 实现的纯前端文字转语音功能,**无需后端服务**,适合局域网环境使用。
|
||||
|
||||
### ✅ 优势
|
||||
|
||||
1. **零后端依赖**:完全在浏览器中运行,不需要服务器支持
|
||||
2. **即开即用**:直接打开 HTML 文件即可使用
|
||||
3. **跨平台**:支持 Windows、Linux、Mac
|
||||
4. **响应式设计**:适配桌面和移动设备
|
||||
5. **功能完整**:支持音量、语速、音调调节
|
||||
|
||||
---
|
||||
|
||||
## 🎯 使用方式
|
||||
|
||||
### 方式一:独立工具(推荐用于测试)
|
||||
|
||||
直接打开 `局域网TTS工具.html` 文件:
|
||||
|
||||
1. 双击打开 `局域网TTS工具.html`
|
||||
2. 在文本框中输入要朗读的文字
|
||||
3. 调整音量、语速、音调参数
|
||||
4. 点击"开始朗读"按钮
|
||||
|
||||
**特点**:
|
||||
- 无需任何配置
|
||||
- 适合快速测试和演示
|
||||
- 可以放在局域网服务器上供多人使用
|
||||
|
||||
### 方式二:Vue 组件集成(用于项目)
|
||||
|
||||
在 Vue 项目中使用 `TtsPlayer` 组件:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<!-- 题目内容 -->
|
||||
<div class="question-content">{{ questionText }}</div>
|
||||
|
||||
<!-- TTS 播放器 -->
|
||||
<TtsPlayer
|
||||
:text="questionText"
|
||||
lang="zh-CN"
|
||||
@start="onTtsStart"
|
||||
@end="onTtsEnd"
|
||||
@stop="onTtsStop"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TtsPlayer from '@/components/Psychology/TtsPlayer.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TtsPlayer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
questionText: '请根据您的实际情况,选择最符合的选项。'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onTtsStart() {
|
||||
console.log('开始朗读')
|
||||
},
|
||||
onTtsEnd() {
|
||||
console.log('朗读完成')
|
||||
},
|
||||
onTtsStop() {
|
||||
console.log('停止朗读')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 文件说明
|
||||
|
||||
### 1. `局域网TTS工具.html`
|
||||
|
||||
**位置**:项目根目录
|
||||
**用途**:独立的 TTS 工具,可直接在浏览器中打开使用
|
||||
|
||||
**功能**:
|
||||
- ✅ 文本输入和朗读
|
||||
- ✅ 音量、语速、音调调节
|
||||
- ✅ 预设文本快速填充
|
||||
- ✅ 响应式设计,支持移动端
|
||||
|
||||
### 2. `xinli-ui/src/components/Psychology/TtsPlayer.vue`
|
||||
|
||||
**位置**:Vue 项目组件目录
|
||||
**用途**:可复用的 TTS 组件,集成到 Vue 项目中
|
||||
|
||||
**Props**:
|
||||
- `text` (String): 要朗读的文本
|
||||
- `lang` (String): 语言设置,默认 `zh-CN`
|
||||
|
||||
**Events**:
|
||||
- `@start`: 开始朗读时触发
|
||||
- `@end`: 朗读完成时触发
|
||||
- `@stop`: 停止朗读时触发
|
||||
- `@error`: 朗读出错时触发
|
||||
|
||||
---
|
||||
|
||||
## 🔧 在问卷答题页面中集成
|
||||
|
||||
### 步骤 1:导入组件
|
||||
|
||||
在 `xinli-ui/src/views/psychology/questionnaire/taking.vue` 中:
|
||||
|
||||
```vue
|
||||
<script>
|
||||
import TtsPlayer from '@/components/Psychology/TtsPlayer.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TtsPlayer
|
||||
},
|
||||
// ...
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 步骤 2:添加 TTS 播放器
|
||||
|
||||
在题目显示区域添加 TTS 组件:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-card shadow="never" class="question-card" v-if="currentItem">
|
||||
<div class="question-number">第 {{ currentIndex + 1 }} 题</div>
|
||||
<div class="question-content">{{ currentItem.itemContent }}</div>
|
||||
|
||||
<!-- 添加 TTS 播放器 -->
|
||||
<div style="margin: 15px 0;">
|
||||
<TtsPlayer :text="currentItem.itemContent" />
|
||||
</div>
|
||||
|
||||
<!-- 其他内容... -->
|
||||
</el-card>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 步骤 3:可选 - 自动朗读
|
||||
|
||||
如果需要自动朗读题目,可以在切换题目时触发:
|
||||
|
||||
```vue
|
||||
<script>
|
||||
export default {
|
||||
watch: {
|
||||
currentItem(newItem) {
|
||||
if (newItem && this.autoRead) {
|
||||
// 延迟一下,确保组件已渲染
|
||||
this.$nextTick(() => {
|
||||
this.$refs.ttsPlayer?.speakText()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌐 浏览器兼容性
|
||||
|
||||
### ✅ 完全支持
|
||||
|
||||
- **Chrome** 33+(推荐)
|
||||
- **Edge** 14+
|
||||
- **Safari** 7+
|
||||
- **Opera** 20+
|
||||
|
||||
### ⚠️ 部分支持
|
||||
|
||||
- **Firefox**:需要手动启用 `media.webspeech.synth.enabled`
|
||||
|
||||
### ❌ 不支持
|
||||
|
||||
- **IE** 11 及以下版本
|
||||
|
||||
---
|
||||
|
||||
## 🎨 自定义配置
|
||||
|
||||
### 修改默认参数
|
||||
|
||||
在 `TtsPlayer.vue` 组件中:
|
||||
|
||||
```javascript
|
||||
data() {
|
||||
return {
|
||||
volume: 1.0, // 默认音量(0-1)
|
||||
rate: 1.0, // 默认语速(0.5-2)
|
||||
pitch: 1.0, // 默认音调(0.5-2)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 选择语音
|
||||
|
||||
```javascript
|
||||
// 获取可用语音列表
|
||||
const voices = this.synth.getVoices()
|
||||
console.log(voices)
|
||||
|
||||
// 选择特定语音
|
||||
const chineseVoice = voices.find(voice =>
|
||||
voice.name.includes('Chinese') ||
|
||||
voice.lang === 'zh-CN'
|
||||
)
|
||||
if (chineseVoice) {
|
||||
this.utterance.voice = chineseVoice
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 使用示例
|
||||
|
||||
### 示例 1:基本使用
|
||||
|
||||
```vue
|
||||
<TtsPlayer text="欢迎使用AI心理健康测评系统" />
|
||||
```
|
||||
|
||||
### 示例 2:监听事件
|
||||
|
||||
```vue
|
||||
<TtsPlayer
|
||||
:text="questionText"
|
||||
@start="handleTtsStart"
|
||||
@end="handleTtsEnd"
|
||||
/>
|
||||
```
|
||||
|
||||
### 示例 3:动态文本
|
||||
|
||||
```vue
|
||||
<TtsPlayer :text="currentQuestion.content" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **浏览器限制**:
|
||||
- 某些浏览器需要用户交互后才能播放语音
|
||||
- 建议在用户点击按钮后触发朗读
|
||||
|
||||
2. **语音质量**:
|
||||
- 不同浏览器的语音质量可能不同
|
||||
- Chrome 的中文语音质量通常较好
|
||||
|
||||
3. **网络环境**:
|
||||
- 完全离线可用,不需要网络连接
|
||||
- 适合局域网环境
|
||||
|
||||
4. **性能考虑**:
|
||||
- 长文本建议分段朗读
|
||||
- 避免同时播放多个语音
|
||||
|
||||
---
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### Q: 为什么没有声音?
|
||||
|
||||
**A**: 检查以下几点:
|
||||
1. 浏览器是否支持 Web Speech API
|
||||
2. 系统音量是否开启
|
||||
3. 浏览器是否允许自动播放音频
|
||||
4. 是否在用户交互后触发(某些浏览器要求)
|
||||
|
||||
### Q: 语音质量不好?
|
||||
|
||||
**A**:
|
||||
1. 使用 Chrome 浏览器(语音质量最好)
|
||||
2. 调整语速和音调参数
|
||||
3. 检查系统语音设置
|
||||
|
||||
### Q: 如何切换语音?
|
||||
|
||||
**A**:
|
||||
```javascript
|
||||
const voices = speechSynthesis.getVoices()
|
||||
const voice = voices.find(v => v.name.includes('Microsoft'))
|
||||
utterance.voice = voice
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 技术文档
|
||||
|
||||
### Web Speech API 参考
|
||||
|
||||
- [MDN - SpeechSynthesis](https://developer.mozilla.org/zh-CN/docs/Web/API/SpeechSynthesis)
|
||||
- [MDN - SpeechSynthesisUtterance](https://developer.mozilla.org/zh-CN/docs/Web/API/SpeechSynthesisUtterance)
|
||||
|
||||
### API 参数说明
|
||||
|
||||
| 参数 | 类型 | 范围 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `lang` | String | - | 语言代码,如 `zh-CN` |
|
||||
| `volume` | Number | 0-1 | 音量大小 |
|
||||
| `rate` | Number | 0.5-2 | 语速倍数 |
|
||||
| `pitch` | Number | 0.5-2 | 音调高低 |
|
||||
| `voice` | SpeechSynthesisVoice | - | 语音对象 |
|
||||
|
||||
---
|
||||
|
||||
## ✨ 功能特性
|
||||
|
||||
- ✅ 纯前端实现,无需后端
|
||||
- ✅ 支持中文语音合成
|
||||
- ✅ 可调节音量、语速、音调
|
||||
- ✅ 响应式设计,支持移动端
|
||||
- ✅ 预设文本快速填充
|
||||
- ✅ 事件监听支持
|
||||
- ✅ 错误处理机制
|
||||
|
||||
---
|
||||
|
||||
**创建时间**:2025-01-27
|
||||
**版本**:1.0.0
|
||||
**状态**:✅ 可用
|
||||
|
||||
258
语音读题功能优化说明.md
258
语音读题功能优化说明.md
|
|
@ -1,258 +0,0 @@
|
|||
# 语音读题功能优化说明
|
||||
|
||||
## 问题修复
|
||||
|
||||
### 1. 编译错误修复 ✅
|
||||
|
||||
**文件**: `ry-xinli-system/src/main/java/com/ddnai/system/service/impl/SysUserServiceImpl.java`
|
||||
|
||||
**问题**: 缺少 `Map` 类的导入
|
||||
|
||||
**修复**:
|
||||
```java
|
||||
import java.util.Map;
|
||||
```
|
||||
|
||||
## 功能优化
|
||||
|
||||
### 2. 语音播放参数优化 ✅
|
||||
|
||||
**问题描述**:
|
||||
- 声音很小
|
||||
- 朗读速度过慢
|
||||
|
||||
**优化方案**:
|
||||
|
||||
#### 音量提升
|
||||
```javascript
|
||||
// 优化前
|
||||
this.currentUtterance.volume = 1.0; // 已经是最大值
|
||||
|
||||
// 优化后 - 确保使用最大音量
|
||||
this.currentUtterance.volume = 1.0; // 最大音量
|
||||
```
|
||||
|
||||
#### 语速优化
|
||||
```javascript
|
||||
// 优化前
|
||||
this.currentUtterance.rate = 0.9; // 太慢
|
||||
|
||||
// 优化后
|
||||
this.currentUtterance.rate = 1.2; // 正常语速,稍快
|
||||
```
|
||||
|
||||
### 3. 新增"朗读全部"功能 ✅
|
||||
|
||||
**功能描述**:
|
||||
点击后一次性朗读题目和所有选项
|
||||
|
||||
**实现文件**:
|
||||
- `xinli-ui/src/views/psychology/assessment/taking.vue` (量表测评)
|
||||
- `xinli-ui/src/views/psychology/questionnaire/taking.vue` (问卷答题)
|
||||
|
||||
**UI设计**:
|
||||
```
|
||||
题目旁边两个按钮:
|
||||
┌────────────┬────────────┐
|
||||
│ 🎤 朗读全部 │ 🔊 朗读题干 │
|
||||
└────────────┴────────────┘
|
||||
```
|
||||
|
||||
**按钮说明**:
|
||||
- **朗读全部** (绿色图标): 朗读题目 + 所有选项
|
||||
- **朗读题干** (蓝色图标): 只朗读题目内容
|
||||
|
||||
**代码实现**:
|
||||
```javascript
|
||||
/** 朗读当前题目和所有选项 */
|
||||
speakCurrentQuestion() {
|
||||
if (!this.isTtsSupported || !this.currentItem) {
|
||||
this.$message.warning('浏览器不支持语音播放功能');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果正在播放,则停止
|
||||
if (this.isSpeaking) {
|
||||
this.stopSpeaking();
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建完整文本:题目 + 所有选项
|
||||
let fullText = this.currentItem.itemContent.trim();
|
||||
|
||||
if (this.currentOptions && this.currentOptions.length > 0) {
|
||||
fullText += '。选项:';
|
||||
this.currentOptions.forEach((option, index) => {
|
||||
const optionCode = option.optionCode || String.fromCharCode(65 + index);
|
||||
fullText += `${optionCode}、${option.optionContent}。`;
|
||||
});
|
||||
}
|
||||
|
||||
// 调用朗读
|
||||
this.speakText(fullText);
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 选项独立朗读按钮 ✅
|
||||
|
||||
**功能**: 每个选项旁边都有独立的朗读按钮
|
||||
|
||||
**特点**:
|
||||
- 点击选项旁边的按钮,只朗读该选项内容
|
||||
- 支持单选、多选、判断题等所有题型
|
||||
- 播放时按钮显示暂停图标
|
||||
|
||||
### 5. UI交互优化 ✅
|
||||
|
||||
#### 播放状态指示
|
||||
```css
|
||||
/* 播放时的脉冲动画 */
|
||||
.tts-btn-all.speaking, .tts-btn.speaking {
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.6; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 悬停效果
|
||||
```css
|
||||
.tts-btn-all:hover:not(:disabled),
|
||||
.tts-btn:hover:not(:disabled) {
|
||||
background-color: #f5f7fa;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
```
|
||||
|
||||
#### 禁用状态
|
||||
```css
|
||||
.tts-btn:disabled, .tts-btn-all:disabled {
|
||||
color: #c0c4cc;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
```
|
||||
|
||||
## 修改文件清单
|
||||
|
||||
### 后端
|
||||
1. `ry-xinli-system/src/main/java/com/ddnai/system/service/impl/SysUserServiceImpl.java`
|
||||
- 添加 `Map` 导入
|
||||
|
||||
### 前端 - 量表测评
|
||||
2. `xinli-ui/src/views/psychology/assessment/taking.vue`
|
||||
- 添加"朗读全部"按钮
|
||||
- 优化语速参数(0.9 → 1.2)
|
||||
- 新增 `speakCurrentQuestion()` 方法
|
||||
- 添加播放状态标识 `isSpeaking`
|
||||
- 优化样式布局
|
||||
|
||||
### 前端 - 问卷答题
|
||||
3. `xinli-ui/src/views/psychology/questionnaire/taking.vue`
|
||||
- 添加"朗读全部"按钮
|
||||
- 优化语速参数(1.0 → 1.2)
|
||||
- 新增 `speakCurrentQuestion()` 方法
|
||||
- 添加播放状态标识 `isSpeaking`
|
||||
- 优化样式布局
|
||||
|
||||
## 功能特性
|
||||
|
||||
### ✅ 音量优化
|
||||
- 使用最大音量 (1.0)
|
||||
- 确保声音清晰可听
|
||||
|
||||
### ✅ 语速优化
|
||||
- 从 0.9/1.0 提升到 1.2
|
||||
- 更自然流畅的阅读速度
|
||||
- 避免拖沓感
|
||||
|
||||
### ✅ 智能朗读
|
||||
- **朗读全部**: 题目 + 所有选项(完整体验)
|
||||
- **朗读题干**: 只朗读题目(快速浏览)
|
||||
- **选项朗读**: 独立朗读每个选项(精准控制)
|
||||
|
||||
### ✅ 交互友好
|
||||
- 播放时显示暂停图标
|
||||
- 点击暂停按钮停止播放
|
||||
- 悬停时按钮放大动画
|
||||
- 播放时脉冲动画提示
|
||||
|
||||
### ✅ 浏览器兼容
|
||||
- 自动检测浏览器支持
|
||||
- 优先选择中文语音引擎
|
||||
- 不支持时禁用按钮
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 量表测评/问卷答题
|
||||
|
||||
#### 方式1: 朗读全部
|
||||
1. 点击题目旁的 **"🎤 朗读全部"** 按钮
|
||||
2. 系统依次朗读:题目内容 → 选项A → 选项B → ...
|
||||
3. 再次点击可停止播放
|
||||
|
||||
#### 方式2: 朗读题干
|
||||
1. 点击题目旁的 **"🔊 朗读题干"** 按钮
|
||||
2. 系统只朗读题目内容
|
||||
3. 再次点击可停止播放
|
||||
|
||||
#### 方式3: 朗读单个选项
|
||||
1. 点击选项旁边的 **🔊** 图标
|
||||
2. 系统只朗读该选项内容
|
||||
3. 适合需要反复听某个选项的场景
|
||||
|
||||
## 技术说明
|
||||
|
||||
### 语音引擎
|
||||
使用浏览器内置的 **Web Speech API**
|
||||
|
||||
### 支持的浏览器
|
||||
- Chrome 33+
|
||||
- Edge 14+
|
||||
- Safari 7+
|
||||
- Firefox 49+
|
||||
|
||||
### 语音参数
|
||||
```javascript
|
||||
{
|
||||
lang: 'zh-CN', // 中文
|
||||
volume: 1.0, // 最大音量
|
||||
rate: 1.2, // 语速稍快
|
||||
pitch: 1.0 // 正常音调
|
||||
}
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 停止机制
|
||||
- 切换题目时自动停止当前播放
|
||||
- 点击播放按钮时先停止再播放
|
||||
- 组件销毁时自动停止
|
||||
|
||||
### 错误处理
|
||||
- 浏览器不支持时禁用功能
|
||||
- 播放失败时静默处理
|
||||
- 已开始播放后忽略非致命错误
|
||||
|
||||
## 未来优化方向
|
||||
|
||||
1. **语速调节**: 允许用户自定义语速(0.5-2.0)
|
||||
2. **音量调节**: 添加音量滑块
|
||||
3. **语音选择**: 支持多种语音引擎切换
|
||||
4. **快捷键**: 支持键盘快捷键控制(如空格键播放/暂停)
|
||||
5. **进度显示**: 显示当前朗读进度
|
||||
6. **自动播放**: 支持切换题目时自动朗读
|
||||
7. **离线语音**: 集成离线TTS引擎
|
||||
|
||||
## 总结
|
||||
|
||||
本次优化主要解决了:
|
||||
1. ✅ **编译错误** - 添加缺失的导入
|
||||
2. ✅ **音量问题** - 确保使用最大音量
|
||||
3. ✅ **语速问题** - 优化语速从慢速提升到正常偏快
|
||||
4. ✅ **功能增强** - 新增"朗读全部"功能
|
||||
5. ✅ **交互优化** - 更好的视觉反馈和用户体验
|
||||
|
||||
语音读题功能现在更加实用和流畅,用户可以根据需要选择不同的朗读方式!
|
||||
222
部署说明-Kimi版本.md
222
部署说明-Kimi版本.md
|
|
@ -1,222 +0,0 @@
|
|||
# 🚀 Kimi API版本部署说明
|
||||
|
||||
## ✅ 已完成的修改
|
||||
|
||||
### 1. 大模型切换
|
||||
**从:** Ollama本地模型 (deepseek-r1:32b)
|
||||
**到:** Kimi远程API (moonshot-v1-32k)
|
||||
|
||||
**配置信息:**
|
||||
```javascript
|
||||
API_URL: 'https://api.moonshot.cn/v1/chat/completions'
|
||||
API_KEY: 'sk-U9fdriPxwBcrpWW0Ite3N0eVtX7VxnqqqYUIBAdWd1hgEA9m'
|
||||
MODEL: 'moonshot-v1-32k'
|
||||
```
|
||||
|
||||
### 2. Android App地址修改
|
||||
**从:** `http://192.168.0.106:8090` (局域网)
|
||||
**到:** `http://1.15.149.240:20001` (云服务器)
|
||||
|
||||
---
|
||||
|
||||
## 📦 部署步骤
|
||||
|
||||
### 第一步:部署前端
|
||||
|
||||
#### 方式1:复制到服务器(推荐)
|
||||
```bash
|
||||
# 本地dist目录路径
|
||||
c:\Users\Administrator\Desktop\Project\xinli\xinli-ui\dist
|
||||
|
||||
# 服务器部署路径(根据实际情况修改)
|
||||
服务器: 1.15.149.240
|
||||
路径: /www/wwwroot/xinli_web/web/
|
||||
```
|
||||
|
||||
**操作:**
|
||||
1. 将 `xinli-ui\dist` 文件夹的**所有内容**复制
|
||||
2. 上传到服务器的前端部署目录
|
||||
3. 覆盖原有文件
|
||||
|
||||
#### 方式2:使用FTP/SFTP工具
|
||||
```
|
||||
工具:FileZilla / WinSCP
|
||||
服务器:1.15.149.240:22
|
||||
用户:根据实际情况
|
||||
路径:/www/wwwroot/xinli_web/web/
|
||||
```
|
||||
|
||||
#### 方式3:使用命令行(如果有SSH)
|
||||
```bash
|
||||
# 在本地执行(需要安装scp)
|
||||
scp -r dist/* user@1.15.149.240:/www/wwwroot/xinli_web/web/
|
||||
```
|
||||
|
||||
### 第二步:安装Android App
|
||||
|
||||
**APK位置:**
|
||||
```
|
||||
c:\Users\Administrator\Desktop\Project\xinli\xinli-App\app\build\outputs\apk\debug\app-debug.apk
|
||||
```
|
||||
|
||||
**安装到手机:**
|
||||
1. 将APK复制到手机
|
||||
2. 安装或覆盖安装旧版本
|
||||
3. 打开App,会自动访问 `http://1.15.149.240:20001`
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试步骤
|
||||
|
||||
### 1. 测试前端访问
|
||||
在浏览器访问:
|
||||
```
|
||||
http://1.15.149.240:20001
|
||||
```
|
||||
|
||||
确保:
|
||||
- ✅ 页面正常显示
|
||||
- ✅ 功能正常使用
|
||||
|
||||
### 2. 测试AI分析功能
|
||||
进入报告详情页:
|
||||
1. 点击"AI分析"按钮
|
||||
2. 等待AI生成分析结果
|
||||
3. 查看是否正常显示
|
||||
|
||||
**预期:**
|
||||
- ✅ 调用Kimi API成功
|
||||
- ✅ 生成专业的报告分析
|
||||
- ✅ 格式美观,内容完整
|
||||
|
||||
### 3. 测试App功能
|
||||
1. 在手机上打开App
|
||||
2. 检查是否能正常访问云服务器
|
||||
3. 测试答题功能
|
||||
4. **测试读题功能** 🔊
|
||||
- 进入答题页面
|
||||
- 点击读题按钮
|
||||
- 应该听到Android原生TTS朗读
|
||||
|
||||
---
|
||||
|
||||
## 🔍 常见问题
|
||||
|
||||
### Q1: AI分析失败?
|
||||
**可能原因:**
|
||||
- Kimi API密钥无效
|
||||
- 网络连接问题
|
||||
- API配额用完
|
||||
|
||||
**解决方法:**
|
||||
1. 检查控制台错误信息
|
||||
2. 确认API密钥正确
|
||||
3. 检查Kimi账户余额
|
||||
|
||||
### Q2: App无法访问?
|
||||
**可能原因:**
|
||||
- 手机无法连接外网
|
||||
- 服务器防火墙阻止
|
||||
- 端口未开放
|
||||
|
||||
**解决方法:**
|
||||
1. 确保手机有网络
|
||||
2. 在手机浏览器访问测试
|
||||
3. 检查服务器防火墙配置
|
||||
|
||||
### Q3: 前端更新没生效?
|
||||
**解决方法:**
|
||||
1. 清除浏览器缓存(Ctrl + F5)
|
||||
2. 确认服务器文件已更新
|
||||
3. 检查Nginx/Apache配置
|
||||
|
||||
---
|
||||
|
||||
## 📊 API使用说明
|
||||
|
||||
### Kimi API调用格式
|
||||
```javascript
|
||||
POST https://api.moonshot.cn/v1/chat/completions
|
||||
|
||||
Headers:
|
||||
Authorization: Bearer sk-U9fdriPxwBcrpWW0Ite3N0eVtX7VxnqqqYUIBAdWd1hgEA9m
|
||||
Content-Type: application/json
|
||||
|
||||
Body:
|
||||
{
|
||||
"model": "moonshot-v1-32k",
|
||||
"messages": [
|
||||
{"role": "system", "content": "系统提示词"},
|
||||
{"role": "user", "content": "用户问题"}
|
||||
],
|
||||
"temperature": 0.2,
|
||||
"max_tokens": 1000
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"choices": [
|
||||
{
|
||||
"message": {
|
||||
"content": "AI生成的内容"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 模型选择
|
||||
- `moonshot-v1-8k` - 8K上下文,速度快
|
||||
- `moonshot-v1-32k` - 32K上下文,推荐 ✅
|
||||
- `moonshot-v1-128k` - 128K上下文,处理长文本
|
||||
|
||||
---
|
||||
|
||||
## 🎯 回滚方案
|
||||
|
||||
如果需要切换回Ollama本地模型:
|
||||
|
||||
**1. 修改API配置**
|
||||
```javascript
|
||||
// detail.vue, index.vue, comprehensive.vue
|
||||
const API_URL = 'http://192.168.0.106:11434/api/chat';
|
||||
const API_KEY = '';
|
||||
const MODEL = 'deepseek-r1:32b';
|
||||
|
||||
// 响应解析
|
||||
data?.message?.content // Ollama格式
|
||||
```
|
||||
|
||||
**2. 修改App地址**
|
||||
```java
|
||||
// MainActivity.java
|
||||
private static final String FIXED_URL = "http://192.168.0.106:8090";
|
||||
```
|
||||
|
||||
**3. 重新构建和部署**
|
||||
|
||||
---
|
||||
|
||||
## 📝 版本信息
|
||||
|
||||
- **大模型:** Kimi API (moonshot-v1-32k)
|
||||
- **App地址:** http://1.15.149.240:20001
|
||||
- **TTS:** Android原生TTS
|
||||
- **构建时间:** 2025-12-02
|
||||
|
||||
---
|
||||
|
||||
## ✅ 部署检查清单
|
||||
|
||||
- [ ] 前端dist文件夹已上传到服务器
|
||||
- [ ] 服务器Nginx/Apache已重启
|
||||
- [ ] 浏览器访问 http://1.15.149.240:20001 正常
|
||||
- [ ] AI分析功能测试成功
|
||||
- [ ] APK已安装到测试手机
|
||||
- [ ] App能正常访问云服务器
|
||||
- [ ] 读题功能测试成功
|
||||
- [ ] 所有功能正常使用
|
||||
|
||||
---
|
||||
|
||||
**部署完成后,即可在手机和浏览器正常使用新版本!** 🎉
|
||||
329
重新打包指南.md
329
重新打包指南.md
|
|
@ -1,329 +0,0 @@
|
|||
# 🔧 重新打包指南
|
||||
|
||||
## ❓ 为什么之前的 APK 不能正常使用?
|
||||
|
||||
可能的原因:
|
||||
1. **前端资源未更新** - TTS 功能修复后需要重新构建前端
|
||||
2. **构建缓存问题** - 旧的缓存导致新代码未生效
|
||||
3. **WebView 资源问题** - App 中的 Web 资源未同步
|
||||
4. **签名问题** - 签名不一致导致安装失败
|
||||
|
||||
## ✅ 解决方案:完整重新打包
|
||||
|
||||
我已经为你创建了两个打包脚本:
|
||||
|
||||
### 方案 1:完整打包流程(推荐)
|
||||
**脚本位置**:`完整打包流程.bat`
|
||||
**包含内容**:前端构建 + Android 打包
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
.\完整打包流程.bat
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- ✅ 自动构建最新前端代码(包含 TTS 修复)
|
||||
- ✅ 清理所有缓存
|
||||
- ✅ 完整的错误检查
|
||||
- ✅ 一键完成所有步骤
|
||||
|
||||
**执行步骤**:
|
||||
1. 打开 PowerShell 或 CMD
|
||||
2. 进入项目目录:`cd c:\Users\Administrator\Desktop\Project\xinli`
|
||||
3. 执行脚本:`.\完整打包流程.bat`
|
||||
4. 等待完成(约 5-10 分钟)
|
||||
|
||||
---
|
||||
|
||||
### 方案 2:只打包 Android App
|
||||
**脚本位置**:`xinli-App\完整重新打包.bat`
|
||||
**适用场景**:前端已经构建好,只需要重新打包 App
|
||||
|
||||
```bash
|
||||
# 在 Android 项目目录执行
|
||||
cd xinli-App
|
||||
.\完整重新打包.bat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 打包流程详解
|
||||
|
||||
### 阶段 1: 前端构建(如果使用完整打包)
|
||||
|
||||
```bash
|
||||
cd xinli-ui
|
||||
npm run build:prod
|
||||
```
|
||||
|
||||
**作用**:
|
||||
- 编译 Vue.js 前端代码
|
||||
- 应用 TTS 朗读功能的修复
|
||||
- 生成优化后的静态文件到 `dist` 目录
|
||||
|
||||
**预期输出**:
|
||||
```
|
||||
Building for production...
|
||||
✓ built in XXs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 阶段 2: Android 打包
|
||||
|
||||
#### 步骤 1: 清理缓存
|
||||
```bash
|
||||
cd xinli-App
|
||||
|
||||
# 删除旧的构建文件
|
||||
rd /s /q app\build
|
||||
rd /s /q build
|
||||
rd /s /q .gradle
|
||||
|
||||
# Gradle clean
|
||||
gradlew.bat clean
|
||||
```
|
||||
|
||||
#### 步骤 2: 检查环境
|
||||
```bash
|
||||
# 检查 JAVA_HOME
|
||||
echo %JAVA_HOME%
|
||||
|
||||
# 检查 Java 版本(需要 JDK 8)
|
||||
java -version
|
||||
```
|
||||
|
||||
**要求**:
|
||||
- ✅ JAVA_HOME 已设置
|
||||
- ✅ Java 版本为 1.8.x
|
||||
- ✅ Android SDK 已安装
|
||||
|
||||
#### 步骤 3: 构建 APK
|
||||
```bash
|
||||
gradlew.bat assembleRelease
|
||||
```
|
||||
|
||||
**预期输出**:
|
||||
```
|
||||
BUILD SUCCESSFUL in XXs
|
||||
```
|
||||
|
||||
**APK 位置**:
|
||||
```
|
||||
xinli-App\app\build\outputs\apk\release\app-release.apk
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 常见问题排查
|
||||
|
||||
### 问题 1: "JAVA_HOME 未设置"
|
||||
|
||||
**解决方法**:
|
||||
```bash
|
||||
# 设置 JAVA_HOME(替换为你的 JDK 路径)
|
||||
set JAVA_HOME=D:\2_part\JAVA\JDK
|
||||
|
||||
# 或在系统环境变量中永久设置
|
||||
```
|
||||
|
||||
### 问题 2: "Node.js 未安装"
|
||||
|
||||
**解决方法**:
|
||||
1. 下载 Node.js:https://nodejs.org/
|
||||
2. 安装后重启命令行
|
||||
3. 验证:`node -v`
|
||||
|
||||
### 问题 3: "npm run build:prod 失败"
|
||||
|
||||
**解决方法**:
|
||||
```bash
|
||||
cd xinli-ui
|
||||
|
||||
# 重新安装依赖
|
||||
npm install
|
||||
|
||||
# 再次构建
|
||||
npm run build:prod
|
||||
```
|
||||
|
||||
### 问题 4: "Gradle 构建失败"
|
||||
|
||||
**常见原因**:
|
||||
1. **网络问题** - 无法下载依赖
|
||||
- 解决:检查网络,重试
|
||||
|
||||
2. **SDK 版本问题** - Build Tools 未安装
|
||||
- 解决:打开 Android Studio → SDK Manager → 安装 Build Tools 30.0.3
|
||||
|
||||
3. **内存不足**
|
||||
- 解决:修改 `gradle.properties` 中的内存设置
|
||||
|
||||
### 问题 5: APK 安装失败
|
||||
|
||||
**原因**:旧版本 App 未卸载
|
||||
|
||||
**解决方法**:
|
||||
```bash
|
||||
# 方法 1: 手动卸载
|
||||
设置 → 应用管理 → 心理测评 → 卸载
|
||||
|
||||
# 方法 2: 使用 adb(如果手机已连接电脑)
|
||||
adb uninstall com.xinli.app
|
||||
adb install app-release.apk
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📱 安装和测试步骤
|
||||
|
||||
### 1. 卸载旧版本(重要!)
|
||||
```
|
||||
⚠️ 必须先卸载旧版本,否则可能安装失败或功能异常
|
||||
```
|
||||
|
||||
**操作步骤**:
|
||||
- 手机 → 设置 → 应用管理
|
||||
- 找到"心理测评"或"xinli"
|
||||
- 点击卸载
|
||||
|
||||
### 2. 安装新 APK
|
||||
|
||||
**方法 1: 通过 USB 传输**
|
||||
1. 将 APK 复制到手机
|
||||
2. 在手机上点击 APK 文件
|
||||
3. 允许安装未知来源应用
|
||||
4. 点击安装
|
||||
|
||||
**方法 2: 通过 adb 安装**
|
||||
```bash
|
||||
# 手机连接电脑,开启 USB 调试
|
||||
adb devices
|
||||
adb install -r app-release.apk
|
||||
```
|
||||
|
||||
### 3. 测试功能
|
||||
|
||||
#### 测试 1: TTS 朗读功能
|
||||
1. 登录系统
|
||||
2. 进入量表/问卷答题页面
|
||||
3. **预期**:
|
||||
- ✅ 朗读按钮不再是灰色
|
||||
- ✅ 点击"朗读全部"能听到声音
|
||||
- ✅ 点击"朗读题干"能听到声音
|
||||
- ✅ 点击选项朗读按钮能听到声音
|
||||
|
||||
#### 测试 2: 权限过滤(如果已修复后端)
|
||||
1. 使用普通用户登录
|
||||
2. 查看问卷列表
|
||||
3. **预期**:
|
||||
- ✅ 只能看到公开问卷和已授权的问卷
|
||||
- ❌ 看不到未授权的问卷
|
||||
|
||||
#### 测试 3: 基本功能
|
||||
1. 登录功能
|
||||
2. 答题功能
|
||||
3. 提交测评
|
||||
4. 查看报告
|
||||
|
||||
---
|
||||
|
||||
## 🎯 快速打包步骤(简化版)
|
||||
|
||||
### 如果你只想快速打包 Android App:
|
||||
|
||||
```bash
|
||||
# 1. 进入 Android 项目目录
|
||||
cd c:\Users\Administrator\Desktop\Project\xinli\xinli-App
|
||||
|
||||
# 2. 清理
|
||||
rd /s /q app\build
|
||||
rd /s /q build
|
||||
|
||||
# 3. 打包
|
||||
gradlew.bat clean assembleRelease
|
||||
|
||||
# 4. 查找 APK
|
||||
dir /s app-release.apk
|
||||
```
|
||||
|
||||
APK 位置:`app\build\outputs\apk\release\app-release.apk`
|
||||
|
||||
---
|
||||
|
||||
## 📊 打包时间预估
|
||||
|
||||
| 步骤 | 预计时间 |
|
||||
|------|---------|
|
||||
| 前端构建 | 2-3 分钟 |
|
||||
| Gradle clean | 10-30 秒 |
|
||||
| APK 构建 | 3-5 分钟 |
|
||||
| **总计** | **5-10 分钟** |
|
||||
|
||||
---
|
||||
|
||||
## ✅ 打包成功标志
|
||||
|
||||
看到以下信息表示打包成功:
|
||||
|
||||
```
|
||||
========================================
|
||||
✓✓✓ 完整打包流程完成!✓✓✓
|
||||
========================================
|
||||
|
||||
APK 文件位置:
|
||||
c:\Users\Administrator\Desktop\Project\xinli\xinli-App\app\build\outputs\apk\release\app-release.apk
|
||||
|
||||
文件大小:
|
||||
XXXX MB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 验证 APK 是否包含最新代码
|
||||
|
||||
### 检查方法 1: 查看构建时间
|
||||
```bash
|
||||
# 查看 APK 文件的修改时间
|
||||
dir app\build\outputs\apk\release\app-release.apk
|
||||
```
|
||||
|
||||
应该是刚刚构建的时间(几分钟前)
|
||||
|
||||
### 检查方法 2: 安装后测试
|
||||
1. 安装 APK
|
||||
2. 打开 App
|
||||
3. 打开浏览器控制台(如果是调试版)
|
||||
4. 进入答题页面
|
||||
5. 查看控制台日志:
|
||||
- 应该看到:`✅ 使用Android原生TTS`
|
||||
- 或:`✅ Android TTS 已就绪(延迟检测)`
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
### 1. 前端资源同步
|
||||
- ⚠️ 如果修改了前端代码,**必须**重新构建前端
|
||||
- ⚠️ App 中的 WebView 加载的是服务器上的网页
|
||||
- ⚠️ 打包 APK 后,还需要将前端 `dist` 目录部署到服务器
|
||||
|
||||
### 2. 签名说明
|
||||
- 当前使用 debug 签名(测试用)
|
||||
- 正式发布需要生成正式签名
|
||||
- 签名不一致会导致安装失败
|
||||
|
||||
### 3. 版本号
|
||||
- 当前版本:`versionCode 1`, `versionName "1.0"`
|
||||
- 如需更新版本号,修改 `app/build.gradle`
|
||||
|
||||
---
|
||||
|
||||
## 🎉 完成!
|
||||
|
||||
打包完成后:
|
||||
1. ✅ 找到 APK 文件
|
||||
2. ✅ 卸载旧版本
|
||||
3. ✅ 安装新 APK
|
||||
4. ✅ 测试功能
|
||||
5. ✅ 享受修复后的应用!
|
||||
34
重新编译.bat
34
重新编译.bat
|
|
@ -1,34 +0,0 @@
|
|||
@echo off
|
||||
echo ========================================
|
||||
echo 重新编译项目(应用档案查询修复)
|
||||
echo ========================================
|
||||
|
||||
cd /d "%~dp0"
|
||||
|
||||
echo.
|
||||
echo 正在清理并编译项目...
|
||||
call mvn clean package -Dmaven.test.skip=true
|
||||
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
echo.
|
||||
echo ========================================
|
||||
echo 编译成功!
|
||||
echo ========================================
|
||||
echo.
|
||||
echo 编译后的jar文件位置:
|
||||
echo %~dp0ry-xinli-admin\target\ry-xinli-admin.jar
|
||||
echo.
|
||||
echo 请执行以下步骤:
|
||||
echo 1. 停止当前运行的服务器
|
||||
echo 2. 替换jar文件
|
||||
echo 3. 重新启动服务器
|
||||
echo 4. 然后再尝试导入数据
|
||||
echo.
|
||||
) else (
|
||||
echo.
|
||||
echo ========================================
|
||||
echo 编译失败!请检查错误信息
|
||||
echo ========================================
|
||||
)
|
||||
|
||||
pause
|
||||
195
问卷权限修复完成报告.md
195
问卷权限修复完成报告.md
|
|
@ -1,195 +0,0 @@
|
|||
# ✅ 问卷权限漏洞修复完成报告
|
||||
|
||||
## 📋 问题总结
|
||||
|
||||
**原问题**:用户登录系统后能看到所有问卷,即使没有分配任何权限。
|
||||
|
||||
**根本原因**:问卷列表接口 `/psychology/questionnaire/list` 缺少权限控制,直接返回所有数据。
|
||||
|
||||
**严重程度**:⚠️ **高危** - 权限控制完全失效
|
||||
|
||||
## ✅ 已修复内容
|
||||
|
||||
### 修改文件
|
||||
- ✅ `ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQuestionnaireController.java`
|
||||
|
||||
### 修复内容
|
||||
|
||||
#### 1. 添加权限注解
|
||||
```java
|
||||
@PreAuthorize("@ss.hasPermi('psychology:questionnaire:list') or @ss.hasAnyRoles('student')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PsyQuestionnaire questionnaire)
|
||||
```
|
||||
|
||||
#### 2. 添加权限过滤逻辑
|
||||
- 获取当前用户可访问的问卷ID列表
|
||||
- 获取所有已配置权限的问卷ID列表
|
||||
- 根据权限过滤返回数据
|
||||
|
||||
#### 3. 三个核心方法
|
||||
|
||||
**方法1**: `filterQuestionnaireListByPermission()`
|
||||
- 过滤问卷列表
|
||||
- 未配置权限的问卷对所有人开放(向后兼容)
|
||||
- 已配置权限的问卷只对有权限的用户开放
|
||||
|
||||
**方法2**: `resolveAllowedScaleIdsForCurrentUser()`
|
||||
- 获取当前用户有权限访问的问卷ID
|
||||
- 管理员返回 null(不过滤)
|
||||
- 普通用户返回其授权的ID列表
|
||||
|
||||
**方法3**: `resolveRestrictedScaleIds()`
|
||||
- 获取所有已配置权限的问卷ID
|
||||
- 用于判断哪些问卷需要权限才能访问
|
||||
|
||||
## 🎯 修复后的行为
|
||||
|
||||
### 场景 1: 管理员(userId = 1)
|
||||
- ✅ 看到所有问卷,不受权限限制
|
||||
- 理由:管理员拥有最高权限
|
||||
|
||||
### 场景 2: 有 `psychology:questionnaire:list` 权限的用户
|
||||
- ✅ 看到所有问卷,不受权限限制
|
||||
- 理由:拥有管理权限
|
||||
|
||||
### 场景 3: 普通用户(student 角色)
|
||||
**未配置权限的问卷**:
|
||||
- ✅ 所有人都能看到
|
||||
- 理由:保持向后兼容,公开问卷
|
||||
|
||||
**已配置权限的问卷**:
|
||||
- ✅ 已分配权限的用户能看到
|
||||
- ❌ 未分配权限的用户**看不到**
|
||||
- 理由:权限控制生效
|
||||
|
||||
## 📊 权限逻辑对比表
|
||||
|
||||
| 问卷状态 | 管理员 | 普通用户(有权限) | 普通用户(无权限) |
|
||||
|---------|-------|----------------|----------------|
|
||||
| **未配置任何权限** | ✅ 可见 | ✅ 可见 | ✅ 可见 |
|
||||
| **已配置权限(已授权)** | ✅ 可见 | ✅ 可见 | ❌ **不可见** |
|
||||
| **已配置权限(未授权)** | ✅ 可见 | ❌ **不可见** | ❌ **不可见** |
|
||||
|
||||
## 🔧 如何分配问卷权限
|
||||
|
||||
### 方法 1: 通过用户管理界面
|
||||
1. 进入 **系统管理** → **用户管理**
|
||||
2. 找到目标用户,点击 **编辑**
|
||||
3. 在"量表权限"选项卡中勾选该用户可访问的问卷
|
||||
4. 保存
|
||||
|
||||
### 方法 2: 通过权限管理菜单
|
||||
1. 进入 **心理测评** → **权限管理**
|
||||
2. 添加新权限
|
||||
3. 选择用户和问卷
|
||||
4. 保存
|
||||
|
||||
### 注意事项
|
||||
|
||||
#### 问卷ID的特殊标识
|
||||
- 问卷在权限表中使用**负数ID**:`-questionnaireId`
|
||||
- 例如:问卷ID=5 → 权限表中存储为 -5
|
||||
- 这样可以与量表ID区分(量表使用正数ID)
|
||||
|
||||
#### 默认行为
|
||||
- 新建的问卷如果不配置权限,默认对所有人开放
|
||||
- 一旦为某个问卷配置了权限,就只有有权限的用户才能看到
|
||||
|
||||
## 🧪 测试建议
|
||||
|
||||
### 测试步骤 1: 验证管理员权限
|
||||
1. 使用管理员账号(admin)登录
|
||||
2. 进入问卷管理页面
|
||||
3. **预期**:能看到所有问卷
|
||||
|
||||
### 测试步骤 2: 验证普通用户(无权限)
|
||||
1. 创建一个测试用户 test_user1
|
||||
2. 不给该用户分配任何问卷权限
|
||||
3. 用 test_user1 登录系统
|
||||
4. 进入学员测试页面
|
||||
5. **预期**:
|
||||
- ✅ 能看到未配置权限的问卷(公开问卷)
|
||||
- ❌ **看不到**已配置权限的问卷
|
||||
|
||||
### 测试步骤 3: 验证普通用户(有权限)
|
||||
1. 创建一个测试用户 test_user2
|
||||
2. 为 test_user2 分配问卷A、B的权限(假设问卷A、B、C都配置了权限)
|
||||
3. 用 test_user2 登录系统
|
||||
4. **预期**:
|
||||
- ✅ 能看到问卷A、B
|
||||
- ❌ **看不到**问卷C
|
||||
- ✅ 能看到未配置权限的公开问卷
|
||||
|
||||
### 测试步骤 4: 验证权限动态更新
|
||||
1. 用 test_user1 登录,记录能看到的问卷列表
|
||||
2. 管理员为 test_user1 添加问卷D的权限
|
||||
3. test_user1 刷新页面
|
||||
4. **预期**:test_user1 现在能看到问卷D
|
||||
|
||||
## 🚨 重要提醒
|
||||
|
||||
### 1. 向后兼容性
|
||||
- ✅ 修复不会影响现有功能
|
||||
- ✅ 未配置权限的问卷仍然对所有人开放
|
||||
- ✅ 只有配置了权限的问卷才会进行权限过滤
|
||||
|
||||
### 2. 需要重新编译部署
|
||||
```bash
|
||||
# 1. 进入后端项目目录
|
||||
cd c:\Users\Administrator\Desktop\Project\xinli
|
||||
|
||||
# 2. 重新打包
|
||||
mvn clean package -DskipTests
|
||||
|
||||
# 3. 找到生成的jar文件
|
||||
# 位置: ry-xinli-admin/target/ry-xinli-admin.jar
|
||||
|
||||
# 4. 停止旧服务,部署新jar
|
||||
# 根据你的部署方式执行相应操作
|
||||
```
|
||||
|
||||
### 3. 数据库无需修改
|
||||
- ✅ 不需要执行任何SQL脚本
|
||||
- ✅ 使用现有的权限表结构
|
||||
- ✅ 兼容现有数据
|
||||
|
||||
### 4. 前端无需修改
|
||||
- ✅ 前端代码无需改动
|
||||
- ✅ API调用方式不变
|
||||
- ✅ 权限过滤在后端自动完成
|
||||
|
||||
## 📝 附加说明
|
||||
|
||||
### 与量表权限的关系
|
||||
- 问卷和量表使用**同一套权限机制**
|
||||
- 在权限表 `psy_scale_permission` 中:
|
||||
- 量表使用**正数ID** (scaleId = 1, 2, 3...)
|
||||
- 问卷使用**负数ID** (scaleId = -1, -2, -3...)
|
||||
|
||||
### 统一入口推荐
|
||||
建议前端统一使用量表接口:
|
||||
```javascript
|
||||
// 推荐:同时获取量表和问卷
|
||||
listScale({ includeQuestionnaire: true })
|
||||
|
||||
// 也可以:仅获取问卷
|
||||
listQuestionnaire()
|
||||
```
|
||||
|
||||
两个接口的权限控制逻辑已经统一,效果相同。
|
||||
|
||||
## ✅ 修复完成确认
|
||||
|
||||
- ✅ 代码已修改
|
||||
- ✅ 权限控制已生效
|
||||
- ✅ 向后兼容性已保证
|
||||
- ✅ 文档已更新
|
||||
- ⏳ **待部署** - 需要重新编译打包部署
|
||||
|
||||
---
|
||||
|
||||
**修复时间**:2024年12月2日
|
||||
**修复人员**:Cascade AI
|
||||
**修复级别**:🔥 紧急修复(权限漏洞)
|
||||
**部署状态**:⏳ 等待部署
|
||||
132
问卷权限问题修复说明.md
132
问卷权限问题修复说明.md
|
|
@ -1,132 +0,0 @@
|
|||
# 问卷权限问题修复说明
|
||||
|
||||
## 问题描述
|
||||
|
||||
用户反馈:问卷明明没有设置权限,但用户却能够查看。
|
||||
|
||||
## 问题根源
|
||||
|
||||
在权限系统中存在一个**"所有用户"权限机制**:
|
||||
|
||||
### 权限查询逻辑(`PsyScalePermissionMapper.xml`)
|
||||
|
||||
```sql
|
||||
-- 或者所有用户权限(user_id, role_id, dept_id 都为空)
|
||||
or (p.user_id is null and p.role_id is null and p.dept_id is null)
|
||||
```
|
||||
|
||||
这意味着:**如果在 `psy_scale_permission` 表中存在一条记录,其 `user_id`、`role_id`、`dept_id` 都为空,那么所有用户都能看到这个量表/问卷!**
|
||||
|
||||
### 问题产生原因
|
||||
|
||||
1. 当在权限管理页面新增权限时,如果**不选择任何用户**(留空),系统会创建一条 `user_id = null` 的记录
|
||||
2. 这条记录会被解释为"所有用户都有权限"
|
||||
3. 在权限列表中,这种记录会显示为"所有用户"标签,但之前不够明显
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 前端权限管理页面优化
|
||||
|
||||
修改文件:`xinli-ui/src/views/psychology/permission/index.vue`
|
||||
|
||||
**改进内容:**
|
||||
|
||||
1. **列表显示优化**
|
||||
- 列名从"用户名称"改为"授权范围"
|
||||
- "所有用户"权限使用醒目的橙色警告标签显示
|
||||
- 显示"所有用户都可访问"的提示文字
|
||||
|
||||
2. **新增筛选功能**
|
||||
- 添加"授权范围"筛选下拉框
|
||||
- 可以快速筛选"所有用户"或"指定用户"权限
|
||||
|
||||
3. **添加明确的"所有用户"开关**
|
||||
- 新增权限时有"授权范围"选项,可以选择"所有用户"或"指定用户"
|
||||
- 启用"所有用户"时会显示警告提示
|
||||
|
||||
4. **强制用户选择**
|
||||
- 如果不启用"所有用户"开关,必须选择至少一个用户
|
||||
- 防止意外创建"所有用户"权限
|
||||
|
||||
5. **二次确认**
|
||||
- 选择"所有用户"时会弹出确认框,提醒用户这将使所有人都能看到该量表/问卷
|
||||
|
||||
### 2. 数据库检查脚本
|
||||
|
||||
创建文件:`检查和清理所有用户权限.sql`
|
||||
|
||||
提供SQL脚本用于:
|
||||
- 查看所有"所有用户"权限记录
|
||||
- 统计"所有用户"权限数量
|
||||
- 禁用或删除"所有用户"权限
|
||||
|
||||
## 如何检查现有数据
|
||||
|
||||
1. 运行以下SQL查看当前存在的"所有用户"权限:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
p.permission_id,
|
||||
p.scale_id,
|
||||
CASE
|
||||
WHEN p.scale_id < 0 THEN (SELECT questionnaire_name FROM psy_questionnaire WHERE questionnaire_id = -p.scale_id)
|
||||
ELSE (SELECT scale_name FROM psy_scale WHERE scale_id = p.scale_id)
|
||||
END AS scale_name,
|
||||
CASE
|
||||
WHEN p.scale_id < 0 THEN '问卷'
|
||||
ELSE '量表'
|
||||
END AS type,
|
||||
p.status
|
||||
FROM psy_scale_permission p
|
||||
WHERE p.user_id IS NULL
|
||||
AND p.role_id IS NULL
|
||||
AND p.dept_id IS NULL
|
||||
AND p.status = '0';
|
||||
```
|
||||
|
||||
2. 如果发现不需要的"所有用户"权限,可以:
|
||||
- 在权限管理页面找到该记录(显示"所有用户"标签),点击删除
|
||||
- 或直接在数据库中删除/禁用
|
||||
|
||||
## 如何清理不需要的权限
|
||||
|
||||
### 方法1:通过管理界面
|
||||
|
||||
1. 进入 **心理测评管理** → **量表权限管理**
|
||||
2. 找到显示"所有用户"标签的权限记录
|
||||
3. 点击"删除"按钮
|
||||
|
||||
### 方法2:通过SQL
|
||||
|
||||
```sql
|
||||
-- 禁用所有"所有用户"权限
|
||||
UPDATE psy_scale_permission
|
||||
SET status = '1', update_time = NOW()
|
||||
WHERE user_id IS NULL
|
||||
AND role_id IS NULL
|
||||
AND dept_id IS NULL
|
||||
AND status = '0';
|
||||
|
||||
-- 或者直接删除
|
||||
DELETE FROM psy_scale_permission
|
||||
WHERE user_id IS NULL
|
||||
AND role_id IS NULL
|
||||
AND dept_id IS NULL;
|
||||
```
|
||||
|
||||
## 权限优先级说明
|
||||
|
||||
系统权限检查顺序:
|
||||
|
||||
1. **用户直接权限**: `user_id = 当前用户ID`
|
||||
2. **角色权限**: `role_id = 用户的角色ID`
|
||||
3. **部门权限**: `dept_id = 用户的部门ID`
|
||||
4. **全局权限**: `user_id`, `role_id`, `dept_id` 都为空(所有用户)
|
||||
|
||||
只要满足任意一个条件,用户就能看到该量表/问卷。
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 修改后,新增权限时必须明确选择"所有用户"或指定具体用户
|
||||
2. 建议检查现有数据,清理不需要的"所有用户"权限
|
||||
3. 管理员角色(admin)不受权限限制,始终可以看到所有量表/问卷
|
||||
357
问卷权限问题分析报告.md
357
问卷权限问题分析报告.md
|
|
@ -1,357 +0,0 @@
|
|||
# 🔐 问卷权限问题分析报告
|
||||
|
||||
## 📋 问题描述
|
||||
|
||||
**用户反馈**:登录系统后能看到所有问卷,明明没有给用户分配问卷权限。
|
||||
|
||||
**影响范围**:所有普通用户都能看到系统中的所有问卷,存在严重的权限控制漏洞。
|
||||
|
||||
## 🔍 问题根源
|
||||
|
||||
### 1. 问卷列表接口缺少权限控制
|
||||
|
||||
**文件**:`ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQuestionnaireController.java`
|
||||
|
||||
```java
|
||||
/**
|
||||
* 获取问卷列表(答题用户可访问)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PsyQuestionnaire questionnaire)
|
||||
{
|
||||
startPage();
|
||||
List<PsyQuestionnaire> list = questionnaireService.selectQuestionnaireList(questionnaire);
|
||||
return getDataTable(list);
|
||||
}
|
||||
```
|
||||
|
||||
**问题点**:
|
||||
- ❌ **没有 `@PreAuthorize` 权限注解**
|
||||
- ❌ **没有权限过滤逻辑**
|
||||
- ❌ **直接返回所有问卷数据**
|
||||
- ❌ **任何登录用户都可以访问**
|
||||
|
||||
### 2. 对比:量表接口有完整的权限控制
|
||||
|
||||
**文件**:`ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyScaleController.java`
|
||||
|
||||
```java
|
||||
/**
|
||||
* 获取量表列表(包含问卷)
|
||||
* 允许管理员和学员访问
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('psychology:scale:list') or @ss.hasAnyRoles('student')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PsyScale scale, @RequestParam(required = false, defaultValue = "true") Boolean includeQuestionnaire)
|
||||
{
|
||||
Set<Long> allowedScaleIds = resolveAllowedScaleIdsForCurrentUser();
|
||||
Set<Long> restrictedScaleIds = resolveRestrictedScaleIds();
|
||||
boolean needPermissionFilter = allowedScaleIds != null;
|
||||
|
||||
// ... 权限过滤逻辑 ...
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- ✅ 有 `@PreAuthorize` 权限注解
|
||||
- ✅ 有完整的权限过滤逻辑
|
||||
- ✅ 根据用户权限返回数据
|
||||
- ✅ 支持管理员和普通用户的不同权限
|
||||
|
||||
### 3. 权限过滤逻辑说明
|
||||
|
||||
在 `PsyScaleController` 中,问卷的处理逻辑是:
|
||||
|
||||
```java
|
||||
if ("questionnaire".equalsIgnoreCase(sourceType))
|
||||
{
|
||||
boolean restricted = restrictedScaleIds != null && restrictedScaleIds.contains(scaleId);
|
||||
if (!restricted)
|
||||
{
|
||||
filtered.add(scale); // 只要不在限制列表中,就添加
|
||||
continue;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**这段代码的含义**:
|
||||
- **问卷采用黑名单机制**:默认所有人可见,除非该问卷配置了权限
|
||||
- **量表采用白名单机制**:只有分配了权限的用户才能看到
|
||||
|
||||
**问题**:
|
||||
- 即使量表接口有权限过滤,但前端可以直接调用问卷接口 `/psychology/questionnaire/list`,完全绕过权限控制
|
||||
|
||||
## 📊 权限控制对比
|
||||
|
||||
| 项目 | 问卷接口 | 量表接口 |
|
||||
|------|---------|---------|
|
||||
| **接口路径** | `/psychology/questionnaire/list` | `/psychology/scale/list` |
|
||||
| **权限注解** | ❌ 无 | ✅ `@PreAuthorize` |
|
||||
| **权限过滤** | ❌ 无 | ✅ 完整逻辑 |
|
||||
| **访问控制** | ❌ 所有人可见 | ✅ 根据权限过滤 |
|
||||
| **安全性** | ❌ 严重漏洞 | ✅ 安全 |
|
||||
|
||||
## 🔧 修复方案
|
||||
|
||||
### 方案一:为问卷接口添加权限控制(推荐)
|
||||
|
||||
修改 `PsyQuestionnaireController.java`,添加完整的权限控制逻辑。
|
||||
|
||||
**优点**:
|
||||
- ✅ 问卷和量表使用统一的权限机制
|
||||
- ✅ 安全性高,符合最小权限原则
|
||||
- ✅ 可以精确控制每个用户看到的问卷
|
||||
|
||||
**实现步骤**:
|
||||
1. 在 `PsyQuestionnaireController` 中注入 `IPsyScalePermissionService`
|
||||
2. 添加 `@PreAuthorize` 权限注解
|
||||
3. 实现权限过滤逻辑(参考 `PsyScaleController`)
|
||||
4. 修改 `/list` 接口,根据用户权限返回问卷列表
|
||||
|
||||
### 方案二:问卷永久对所有人开放(不推荐)
|
||||
|
||||
如果业务上问卷确实需要对所有人开放(不需要权限控制),需要:
|
||||
|
||||
1. 明确文档说明:问卷对所有登录用户开放
|
||||
2. 确保量表接口的权限过滤逻辑不影响问卷显示
|
||||
3. 前端统一使用量表接口(`/psychology/scale/list?includeQuestionnaire=true`)
|
||||
|
||||
**问题**:
|
||||
- ⚠️ 无法精确控制用户可见的问卷
|
||||
- ⚠️ 可能暴露敏感内容
|
||||
|
||||
## ✅ 推荐修复代码
|
||||
|
||||
### 修改 `PsyQuestionnaireController.java`
|
||||
|
||||
```java
|
||||
package com.ddnai.web.controller.psychology;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import com.ddnai.common.annotation.Log;
|
||||
import com.ddnai.common.core.controller.BaseController;
|
||||
import com.ddnai.common.core.domain.AjaxResult;
|
||||
import com.ddnai.common.core.page.TableDataInfo;
|
||||
import com.ddnai.common.enums.BusinessType;
|
||||
import com.ddnai.common.utils.SecurityUtils;
|
||||
import com.ddnai.system.domain.psychology.PsyQuestionnaire;
|
||||
import com.ddnai.system.service.psychology.IPsyQuestionnaireService;
|
||||
import com.ddnai.system.service.psychology.IPsyScalePermissionService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/psychology/questionnaire")
|
||||
public class PsyQuestionnaireController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IPsyQuestionnaireService questionnaireService;
|
||||
|
||||
@Autowired
|
||||
private IPsyScalePermissionService scalePermissionService;
|
||||
|
||||
/**
|
||||
* 获取问卷列表(带权限控制)
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('psychology:questionnaire:list') or @ss.hasAnyRoles('student')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PsyQuestionnaire questionnaire)
|
||||
{
|
||||
// 获取当前用户权限
|
||||
Set<Long> allowedScaleIds = resolveAllowedScaleIdsForCurrentUser();
|
||||
Set<Long> restrictedScaleIds = resolveRestrictedScaleIds();
|
||||
|
||||
// 查询所有问卷
|
||||
startPage();
|
||||
List<PsyQuestionnaire> list = questionnaireService.selectQuestionnaireList(questionnaire);
|
||||
|
||||
// 权限过滤
|
||||
list = filterQuestionnaireListByPermission(list, allowedScaleIds, restrictedScaleIds);
|
||||
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户权限过滤问卷列表
|
||||
*/
|
||||
private List<PsyQuestionnaire> filterQuestionnaireListByPermission(
|
||||
List<PsyQuestionnaire> list,
|
||||
Set<Long> allowedScaleIds,
|
||||
Set<Long> restrictedScaleIds)
|
||||
{
|
||||
// 管理员或无需权限过滤
|
||||
if (allowedScaleIds == null || list == null)
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
List<PsyQuestionnaire> filtered = new ArrayList<>();
|
||||
for (PsyQuestionnaire questionnaire : list)
|
||||
{
|
||||
if (questionnaire == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 使用负数ID标识问卷(与量表接口保持一致)
|
||||
Long scaleId = -questionnaire.getQuestionnaireId();
|
||||
|
||||
// 如果该问卷未配置权限,对所有人开放
|
||||
boolean restricted = restrictedScaleIds != null && restrictedScaleIds.contains(scaleId);
|
||||
if (!restricted)
|
||||
{
|
||||
filtered.add(questionnaire);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果配置了权限,检查用户是否有权限
|
||||
if (allowedScaleIds.contains(scaleId))
|
||||
{
|
||||
filtered.add(questionnaire);
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析当前用户可访问的量表/问卷ID集合
|
||||
* @return null 表示无需权限过滤(管理员);非null 表示必须过滤
|
||||
*/
|
||||
private Set<Long> resolveAllowedScaleIdsForCurrentUser()
|
||||
{
|
||||
try
|
||||
{
|
||||
Long currentUserId = SecurityUtils.getUserId();
|
||||
|
||||
// 管理员拥有所有权限
|
||||
if (currentUserId == null || currentUserId.equals(1L))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查是否有管理权限
|
||||
boolean hasManagePerm = false;
|
||||
try
|
||||
{
|
||||
hasManagePerm = SecurityUtils.hasPermi("psychology:questionnaire:list");
|
||||
}
|
||||
catch (Exception ignore)
|
||||
{
|
||||
// 忽略权限判断异常
|
||||
}
|
||||
|
||||
if (hasManagePerm)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取用户有权限访问的量表/问卷ID
|
||||
List<Long> scaleIds = scalePermissionService.selectScaleIdsByUserId(currentUserId);
|
||||
return new HashSet<>(scaleIds != null ? scaleIds : new ArrayList<>());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.warn("获取用户问卷权限失败: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已配置权限的量表/问卷ID
|
||||
*/
|
||||
private Set<Long> resolveRestrictedScaleIds()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<Long> ids = scalePermissionService.selectAllScaleIdsWithPermission();
|
||||
if (ids == null || ids.isEmpty())
|
||||
{
|
||||
return java.util.Collections.emptySet();
|
||||
}
|
||||
return new HashSet<>(ids);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.warn("解析问卷权限限制列表失败: {}", e.getMessage());
|
||||
return java.util.Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
// ... 其他方法保持不变 ...
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 测试场景 1:普通用户(未分配权限)
|
||||
|
||||
**预期**:
|
||||
- ✅ 只能看到未配置权限的问卷(公开问卷)
|
||||
- ❌ 不能看到已配置权限但未分配给该用户的问卷
|
||||
|
||||
### 测试场景 2:普通用户(已分配权限)
|
||||
|
||||
**预期**:
|
||||
- ✅ 能看到未配置权限的问卷
|
||||
- ✅ 能看到已分配权限的问卷
|
||||
- ❌ 不能看到未分配权限的其他问卷
|
||||
|
||||
### 测试场景 3:管理员
|
||||
|
||||
**预期**:
|
||||
- ✅ 能看到所有问卷(不受权限限制)
|
||||
|
||||
### 测试步骤
|
||||
|
||||
1. **准备数据**:
|
||||
- 创建3个问卷:A(未配置权限)、B(已配置权限)、C(已配置权限)
|
||||
- 创建普通用户 user1,只分配问卷B的权限
|
||||
|
||||
2. **测试 user1 登录**:
|
||||
```bash
|
||||
# 预期结果:只能看到问卷A和B,不能看到C
|
||||
GET /psychology/questionnaire/list
|
||||
```
|
||||
|
||||
3. **测试管理员登录**:
|
||||
```bash
|
||||
# 预期结果:能看到所有问卷A、B、C
|
||||
GET /psychology/questionnaire/list
|
||||
```
|
||||
|
||||
## 📝 修改文件清单
|
||||
|
||||
- ✅ `ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyQuestionnaireController.java`
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **权限分配兼容性**:
|
||||
- 问卷使用负数ID(-questionnaireId)存储在权限表中
|
||||
- 与量表接口保持一致
|
||||
|
||||
2. **默认行为**:
|
||||
- 未配置权限的问卷对所有人开放(向后兼容)
|
||||
- 已配置权限的问卷只对有权限的用户开放
|
||||
|
||||
3. **前端调用**:
|
||||
- 建议统一使用量表接口:`/psychology/scale/list?includeQuestionnaire=true`
|
||||
- 或使用修复后的问卷接口:`/psychology/questionnaire/list`
|
||||
|
||||
## 🎯 修复后的效果
|
||||
|
||||
| 用户类型 | 修复前 | 修复后 |
|
||||
|---------|-------|-------|
|
||||
| **管理员** | 看到所有问卷 | 看到所有问卷 ✅ |
|
||||
| **普通用户(无权限)** | 看到所有问卷 ❌ | 只看到公开问卷 ✅ |
|
||||
| **普通用户(有权限)** | 看到所有问卷 ❌ | 看到公开+授权问卷 ✅ |
|
||||
|
||||
---
|
||||
|
||||
**问题发现时间**:2024年12月2日
|
||||
**严重程度**:⚠️ 高危(权限控制漏洞)
|
||||
**修复优先级**:🔥 紧急
|
||||
490
项目结构分析报告.md
490
项目结构分析报告.md
|
|
@ -1,490 +0,0 @@
|
|||
# 项目结构分析报告
|
||||
|
||||
## 📋 项目概述
|
||||
|
||||
**项目名称**:AI心理健康测评系统 (ry-xinli)
|
||||
**项目类型**:心理健康测评管理系统
|
||||
**技术架构**:前后端分离 + 移动端
|
||||
**开发框架**:基于 RuoYi-Vue 框架改造
|
||||
**版本**:1.0.0
|
||||
**开发者**:wanxiubin
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 整体架构
|
||||
|
||||
### 架构分层
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 用户层 │
|
||||
│ 桌面浏览器 │ 移动浏览器 │ 微信小程序 │ 第三方系统API │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↕
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 前端应用层 │
|
||||
│ Vue 2.6 + Element UI + Vuex + Router │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↕ (HTTP/RESTful API)
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 应用服务层 │
|
||||
│ Spring Boot 2.5 + Spring Security │
|
||||
│ Controller层 → Service层 → Mapper层 (MyBatis) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↕
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 数据存储层 │
|
||||
│ ┌──────────────┬──────────────┬──────────────┐ │
|
||||
│ │ MySQL │ Redis │ 文件存储 │ │
|
||||
│ │ (数据持久) │ (缓存/会话) │ (报告/音频) │ │
|
||||
│ └──────────────┴──────────────┴──────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 后端模块结构
|
||||
|
||||
### Maven 多模块架构
|
||||
|
||||
```
|
||||
ry-xinli (根模块)
|
||||
├── ry-xinli-admin # 启动模块(Web入口)
|
||||
├── ry-xinli-system # 系统模块(业务逻辑)
|
||||
├── ry-xinli-framework # 框架模块(安全配置)
|
||||
├── ry-xinli-common # 公共模块(工具类)
|
||||
├── ry-xinli-generator # 代码生成器
|
||||
└── ry-xinli-quartz # 定时任务
|
||||
```
|
||||
|
||||
### 模块详细说明
|
||||
|
||||
#### 1. ry-xinli-admin(启动模块)
|
||||
**路径**:`ry-xinli-admin/`
|
||||
**职责**:Web 应用入口,包含 Controller 层和配置
|
||||
|
||||
**结构**:
|
||||
```
|
||||
ry-xinli-admin/
|
||||
├── src/main/java/com/ddnai/
|
||||
│ ├── XinliApplication.java # 启动类
|
||||
│ └── web/controller/ # 控制器层
|
||||
│ ├── psychology/ # 心理测评控制器(20个)
|
||||
│ │ ├── PsyScaleController.java
|
||||
│ │ ├── PsyAssessmentController.java
|
||||
│ │ ├── PsyAssessmentReportController.java
|
||||
│ │ ├── PsyFactorController.java
|
||||
│ │ ├── PsyQrcodeController.java
|
||||
│ │ ├── PsyQuestionnaireController.java
|
||||
│ │ ├── PsyUserProfileController.java
|
||||
│ │ ├── PsyWarningController.java
|
||||
│ │ └── ...
|
||||
│ ├── system/ # 系统管理控制器(15个)
|
||||
│ │ ├── SysUserController.java
|
||||
│ │ ├── SysRoleController.java
|
||||
│ │ ├── SysMenuController.java
|
||||
│ │ └── ...
|
||||
│ ├── monitor/ # 监控控制器(5个)
|
||||
│ ├── common/ # 通用控制器
|
||||
│ └── api/ # 开放API
|
||||
└── src/main/resources/
|
||||
├── application.yml # 应用配置
|
||||
└── mapper/ # MyBatis映射文件
|
||||
```
|
||||
|
||||
**关键配置**:
|
||||
- 服务端口:30081
|
||||
- 文件上传路径:`D:\wwwroot\xinli_web\web\profile\uploadPath`
|
||||
- Redis 缓存配置
|
||||
- 数据库连接配置
|
||||
|
||||
#### 2. ry-xinli-system(系统模块)
|
||||
**路径**:`ry-xinli-system/`
|
||||
**职责**:核心业务逻辑,包含 Domain、Mapper、Service
|
||||
|
||||
**结构**:
|
||||
```
|
||||
ry-xinli-system/
|
||||
├── src/main/java/com/ddnai/system/
|
||||
│ ├── domain/ # 实体类
|
||||
│ │ ├── psychology/ # 心理测评实体(20+个)
|
||||
│ │ │ ├── PsyScale.java # 量表
|
||||
│ │ │ ├── PsyScaleItem.java # 量表题目
|
||||
│ │ │ ├── PsyScaleOption.java # 题目选项
|
||||
│ │ │ ├── PsyAssessment.java # 测评记录
|
||||
│ │ │ ├── PsyAssessmentAnswer.java # 测评答案
|
||||
│ │ │ ├── PsyAssessmentReport.java # 测评报告
|
||||
│ │ │ ├── PsyFactor.java # 因子
|
||||
│ │ │ ├── PsyFactorRule.java # 因子规则
|
||||
│ │ │ ├── PsyQrcode.java # 二维码
|
||||
│ │ │ ├── PsyQuestionnaire.java # 自定义问卷
|
||||
│ │ │ ├── PsyUserProfile.java # 用户档案
|
||||
│ │ │ ├── PsyWarning.java # 危机预警
|
||||
│ │ │ └── ...
|
||||
│ │ └── vo/ # 视图对象
|
||||
│ ├── mapper/ # 数据访问层
|
||||
│ │ ├── psychology/ # 心理测评Mapper(20+个)
|
||||
│ │ └── Sys*.java # 系统Mapper
|
||||
│ └── service/ # 业务逻辑层
|
||||
│ ├── psychology/ # 心理测评Service接口(25个)
|
||||
│ └── impl/ # Service实现类
|
||||
│ ├── psychology/ # 心理测评Service实现(24个)
|
||||
│ └── Sys*.java # 系统Service实现
|
||||
└── src/main/resources/mapper/ # MyBatis XML映射文件
|
||||
├── psychology/ # 心理测评Mapper XML(39个)
|
||||
└── system/ # 系统Mapper XML
|
||||
```
|
||||
|
||||
#### 3. ry-xinli-framework(框架模块)
|
||||
**路径**:`ry-xinli-framework/`
|
||||
**职责**:框架配置、安全、通用组件
|
||||
|
||||
**主要功能**:
|
||||
- Spring Security 安全配置
|
||||
- 系统配置管理
|
||||
- 通用 Web 组件
|
||||
- 拦截器、过滤器
|
||||
|
||||
#### 4. ry-xinli-common(公共模块)
|
||||
**路径**:`ry-xinli-common/`
|
||||
**职责**:公共工具类、常量、核心类
|
||||
|
||||
**结构**:
|
||||
```
|
||||
ry-xinli-common/
|
||||
├── src/main/java/com/ddnai/common/
|
||||
│ ├── core/ # 核心类
|
||||
│ ├── utils/ # 工具类
|
||||
│ │ ├── file/ # 文件工具
|
||||
│ │ ├── http/ # HTTP工具
|
||||
│ │ ├── ip/ # IP工具
|
||||
│ │ ├── poi/ # Excel工具
|
||||
│ │ ├── sign/ # 签名工具
|
||||
│ │ └── ...
|
||||
│ ├── constants/ # 常量
|
||||
│ └── config/ # 配置类
|
||||
```
|
||||
|
||||
#### 5. ry-xinli-generator(代码生成器)
|
||||
**路径**:`ry-xinli-generator/`
|
||||
**职责**:代码自动生成工具
|
||||
|
||||
#### 6. ry-xinli-quartz(定时任务)
|
||||
**路径**:`ry-xinli-quartz/`
|
||||
**职责**:定时任务调度
|
||||
|
||||
---
|
||||
|
||||
## 🎨 前端模块结构
|
||||
|
||||
### Vue 前端项目
|
||||
|
||||
**路径**:`xinli-ui/`
|
||||
**技术栈**:
|
||||
- Vue 2.6.12
|
||||
- Element UI 2.15.14
|
||||
- Vuex 3.6.0
|
||||
- Vue Router 3.4.9
|
||||
- Axios 0.28.1
|
||||
- ECharts 5.4.0
|
||||
|
||||
**结构**:
|
||||
```
|
||||
xinli-ui/
|
||||
├── src/
|
||||
│ ├── api/ # API接口定义(40个文件)
|
||||
│ │ ├── psychology/ # 心理测评API(19个)
|
||||
│ │ │ ├── scale.js # 量表API
|
||||
│ │ │ ├── assessment.js # 测评API
|
||||
│ │ │ ├── report.js # 报告API
|
||||
│ │ │ ├── questionnaire.js # 问卷API
|
||||
│ │ │ └── ...
|
||||
│ │ ├── system/ # 系统API(10个)
|
||||
│ │ └── monitor/ # 监控API(7个)
|
||||
│ ├── views/ # 页面视图(76个Vue文件)
|
||||
│ │ ├── psychology/ # 心理测评页面
|
||||
│ │ │ ├── scale/ # 量表管理
|
||||
│ │ │ ├── assessment/ # 测评执行
|
||||
│ │ │ ├── report/ # 测评报告
|
||||
│ │ │ ├── questionnaire/ # 自定义问卷
|
||||
│ │ │ ├── qrcode/ # 二维码管理
|
||||
│ │ │ ├── userProfile/ # 用户档案
|
||||
│ │ │ ├── warning/ # 危机预警
|
||||
│ │ │ └── website/ # 心理网站
|
||||
│ │ └── system/ # 系统管理页面
|
||||
│ ├── components/ # 组件(32个)
|
||||
│ │ ├── Psychology/ # 心理测评组件
|
||||
│ │ └── RuoYi/ # 若依通用组件
|
||||
│ ├── store/ # Vuex状态管理(8个文件)
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── utils/ # 工具函数(24个文件)
|
||||
│ ├── assets/ # 静态资源
|
||||
│ │ ├── icons/ # 图标(89个SVG)
|
||||
│ │ ├── images/ # 图片
|
||||
│ │ └── styles/ # 样式文件
|
||||
│ └── main.js # 入口文件
|
||||
├── public/ # 公共资源
|
||||
└── package.json # 依赖配置
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📱 移动端项目
|
||||
|
||||
### Android 应用
|
||||
|
||||
**路径**:`xinli-App/`
|
||||
**技术栈**:Android + Gradle
|
||||
|
||||
**结构**:
|
||||
```
|
||||
xinli-App/
|
||||
├── app/
|
||||
│ ├── src/main/
|
||||
│ │ ├── java/ # Java代码
|
||||
│ │ ├── res/ # 资源文件
|
||||
│ │ └── AndroidManifest.xml # 清单文件
|
||||
│ └── build.gradle # 构建配置
|
||||
├── build.gradle # 项目构建配置
|
||||
├── gradle/ # Gradle配置
|
||||
└── README.md # 说明文档
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ 数据库设计
|
||||
|
||||
### 核心表结构
|
||||
|
||||
#### 心理测评核心表(20+个)
|
||||
|
||||
1. **量表相关**
|
||||
- `psy_scale` - 量表基本信息
|
||||
- `psy_scale_item` - 量表题目
|
||||
- `psy_scale_option` - 题目选项
|
||||
- `psy_scale_permission` - 量表权限
|
||||
|
||||
2. **测评相关**
|
||||
- `psy_assessment` - 测评记录
|
||||
- `psy_assessment_answer` - 测评答案
|
||||
- `psy_assessment_report` - 测评报告
|
||||
|
||||
3. **因子与计分**
|
||||
- `psy_factor` - 因子定义
|
||||
- `psy_factor_rule` - 因子计分规则
|
||||
- `psy_factor_score` - 因子得分
|
||||
- `psy_result_interpretation` - 结果解释
|
||||
|
||||
4. **扩展功能**
|
||||
- `psy_questionnaire` - 自定义问卷
|
||||
- `psy_questionnaire_item` - 问卷题目
|
||||
- `psy_questionnaire_answer` - 问卷答案
|
||||
- `psy_user_profile` - 用户档案
|
||||
- `psy_qrcode` - 二维码管理
|
||||
- `psy_warning` - 危机预警
|
||||
- `psy_website_content` - 心理网站内容
|
||||
|
||||
#### 系统管理表
|
||||
- `sys_user` - 用户表
|
||||
- `sys_role` - 角色表
|
||||
- `sys_menu` - 菜单表
|
||||
- `sys_dept` - 部门表
|
||||
- `sys_config` - 系统配置
|
||||
- `sys_dict_*` - 字典表
|
||||
|
||||
---
|
||||
|
||||
## 🔧 技术栈详情
|
||||
|
||||
### 后端技术栈
|
||||
|
||||
| 技术 | 版本 | 用途 |
|
||||
|------|------|------|
|
||||
| Spring Boot | 2.5.15 | 应用框架 |
|
||||
| Spring Security | 5.7.14 | 安全框架 |
|
||||
| MyBatis | - | ORM框架 |
|
||||
| MySQL | - | 数据库 |
|
||||
| Redis | - | 缓存/会话 |
|
||||
| Druid | 1.2.23 | 数据库连接池 |
|
||||
| JWT | 0.9.1 | 令牌认证 |
|
||||
| Swagger | 3.0.0 | API文档 |
|
||||
| Apache POI | 4.1.2 | Excel处理 |
|
||||
| PDFBox | 2.0.29 | PDF处理 |
|
||||
| ZXing | 3.5.1 | 二维码生成 |
|
||||
|
||||
### 前端技术栈
|
||||
|
||||
| 技术 | 版本 | 用途 |
|
||||
|------|------|------|
|
||||
| Vue | 2.6.12 | 前端框架 |
|
||||
| Element UI | 2.15.14 | UI组件库 |
|
||||
| Vuex | 3.6.0 | 状态管理 |
|
||||
| Vue Router | 3.4.9 | 路由管理 |
|
||||
| Axios | 0.28.1 | HTTP客户端 |
|
||||
| ECharts | 5.4.0 | 图表库 |
|
||||
| Quill | 2.0.2 | 富文本编辑器 |
|
||||
|
||||
---
|
||||
|
||||
## 📊 核心功能模块
|
||||
|
||||
### 1. 量表管理模块 ✅
|
||||
- 量表CRUD操作
|
||||
- 量表导入导出(Excel)
|
||||
- 量表状态管理
|
||||
- 量表权限控制
|
||||
|
||||
### 2. 题目管理模块 ✅
|
||||
- 题目编辑(富文本)
|
||||
- 选项配置
|
||||
- 批量导入题目
|
||||
- 题目排序拖拽
|
||||
|
||||
### 3. 因子与计分模块 ✅
|
||||
- 因子配置
|
||||
- 因子公式设置
|
||||
- 权重配置
|
||||
- 反向计分设置
|
||||
|
||||
### 4. 测评执行模块 ✅
|
||||
- 开始测评
|
||||
- 暂停/恢复测评
|
||||
- 提交测评
|
||||
- 防重复提交
|
||||
- 断点续测
|
||||
|
||||
### 5. 报告生成模块 ✅
|
||||
- 自动生成报告
|
||||
- 因子分计算
|
||||
- 结果解释
|
||||
- 报告导出(PDF/Excel)
|
||||
|
||||
### 6. 用户档案管理 ✅
|
||||
- 档案字段自定义
|
||||
- 不同年龄段模板
|
||||
- 档案信息CRUD
|
||||
- 档案导入导出
|
||||
|
||||
### 7. 自定义问卷模块 ✅
|
||||
- 问卷创建/编辑
|
||||
- 多种题目类型支持(9种)
|
||||
- 自动计分
|
||||
- 成绩排名统计
|
||||
|
||||
### 8. 二维码功能 ✅
|
||||
- 生成测评二维码
|
||||
- 扫码测试
|
||||
- 扫码查看报告
|
||||
- 扫码注册/登录
|
||||
|
||||
### 9. 危机预警模块
|
||||
- 预警规则设置
|
||||
- 预警记录管理
|
||||
- 预警解除记录
|
||||
|
||||
### 10. 心理网站模块
|
||||
- 文章发布/编辑
|
||||
- 栏目管理
|
||||
- 留言评论
|
||||
- 个性化配置
|
||||
|
||||
---
|
||||
|
||||
## 📁 项目目录结构总览
|
||||
|
||||
```
|
||||
xinli/
|
||||
├── bin/ # 构建脚本
|
||||
│ ├── clean.bat
|
||||
│ ├── package.bat
|
||||
│ └── run.bat
|
||||
├── doc/ # 文档
|
||||
├── pom.xml # Maven根配置
|
||||
├── ry-xinli-admin/ # 启动模块
|
||||
├── ry-xinli-system/ # 系统模块
|
||||
├── ry-xinli-framework/ # 框架模块
|
||||
├── ry-xinli-common/ # 公共模块
|
||||
├── ry-xinli-generator/ # 代码生成器
|
||||
├── ry-xinli-quartz/ # 定时任务
|
||||
├── xinli-ui/ # Vue前端
|
||||
├── xinli-App/ # Android移动端
|
||||
├── sql/ # SQL脚本
|
||||
├── z_Project change/ # 项目变更文档
|
||||
│ ├── 进度汇总/ # 开发进度文档
|
||||
│ └── 量表示例/ # 量表示例数据
|
||||
└── 项目介绍/ # 项目介绍文档
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 安全配置
|
||||
|
||||
- Spring Security 认证授权
|
||||
- JWT Token 机制
|
||||
- 密码加密存储
|
||||
- XSS 攻击防护
|
||||
- 防盗链配置
|
||||
- 操作日志记录
|
||||
|
||||
---
|
||||
|
||||
## 📈 项目规模统计
|
||||
|
||||
### 代码统计(估算)
|
||||
|
||||
| 模块 | Java文件 | Vue文件 | 总行数(估算) |
|
||||
|------|---------|---------|---------------|
|
||||
| 后端 | 400+ | - | 50,000+ |
|
||||
| 前端 | - | 76 | 30,000+ |
|
||||
| 移动端 | 1 | - | 1,000+ |
|
||||
| **总计** | **400+** | **76** | **80,000+** |
|
||||
|
||||
### 功能模块统计
|
||||
|
||||
- **Controller**: 40+ 个
|
||||
- **Service**: 50+ 个
|
||||
- **Mapper**: 60+ 个
|
||||
- **实体类**: 30+ 个
|
||||
- **前端页面**: 76 个
|
||||
- **API接口**: 100+ 个
|
||||
|
||||
---
|
||||
|
||||
## 🎯 项目特点
|
||||
|
||||
1. **模块化设计**:前后端分离,Maven多模块架构
|
||||
2. **功能完整**:覆盖心理测评全流程
|
||||
3. **扩展性强**:支持自定义问卷、用户档案扩展
|
||||
4. **安全可靠**:完善的权限控制和数据安全
|
||||
5. **多端支持**:Web端 + 移动端
|
||||
6. **易于维护**:代码规范,文档完善
|
||||
|
||||
---
|
||||
|
||||
## 📝 开发状态
|
||||
|
||||
### 已完成功能 ✅
|
||||
- 第一阶段:数据库设计与初始化
|
||||
- 第二阶段:核心功能模块(量表、测评、报告)
|
||||
- 第三阶段D3-1:用户档案管理
|
||||
- 第三阶段D3-2:自定义问卷基础功能
|
||||
- 第三阶段D3-3:二维码管理功能
|
||||
|
||||
### 进行中功能 🟡
|
||||
- 第三阶段剩余模块(心理网站、数据统计等)
|
||||
|
||||
---
|
||||
|
||||
## 🔗 相关文档
|
||||
|
||||
- 技术架构设计:`z_Project change/进度汇总/3-技术架构设计.md`
|
||||
- 开发计划:`z_Project change/进度汇总/1-开发计划.md`
|
||||
- 使用指南:`使用指南-*.md`
|
||||
- 项目改造说明:`项目介绍/项目改造说明.md`
|
||||
|
||||
---
|
||||
|
||||
**报告生成时间**:2025-01-27
|
||||
**项目版本**:1.0.0
|
||||
**分析状态**:✅ 完成
|
||||
|
||||
Loading…
Reference in New Issue
Block a user