guoyu/log/学生数据不一致问题-解决方案.md

282 lines
7.1 KiB
Markdown
Raw Normal View History

2025-12-11 23:28:07 +08:00
# 学生数据不一致问题 - 完整解决方案
## 📊 **问题描述**
**现象:**
- 课程分配时选择班级/监区的学生数量 ≠ 用户管理的学生数量
- 例如班级显示30个学生但用户管理只有25个学生
---
## 🔍 **根本原因**
### **数据表关系:**
```
sys_user (用户表)
↓ user_id
study_student (学生表)
↓ student_id
study_student_class (学生-班级关联)
study_course_assignment (课程分配)
study_learning_record (学习记录)
```
### **删除用户时的问题:**
**当前逻辑(`SysUserServiceImpl.deleteUserById`**
```
✅ 删除sys_user用户
✅ 删除sys_user_role用户角色
✅ 删除sys_user_post用户岗位
❌ 未删除study_student学生信息
❌ 未删除study_student_class学生班级关联← 导致数量不一致
❌ 未删除study_course_assignment课程分配
❌ 未删除study_learning_record学习记录
```
**结果:**
- 删除用户后,学生相关数据变成"孤儿数据"
- 课程分配查询`study_student_class`表,包含孤儿数据
- 用户管理查询`sys_user`表,不包含已删除用户
- **数量不一致!**
---
## ✅ **解决方案**
### **步骤1清理现有孤儿数据必须先执行**
**使用SQL脚本**
```
log/数据清理-删除孤儿数据.sql
```
**执行步骤:**
```sql
-- 1. 查询孤儿数据(先确认)
SELECT COUNT(*)
FROM study_student s
LEFT JOIN sys_user u ON s.user_id = u.user_id
WHERE u.user_id IS NULL;
-- 2. 备份数据库(重要!)
-- 3. 执行清理脚本(按顺序删除)
-- 见:数据清理-删除孤儿数据.sql
-- 4. 验证结果
SELECT
'用户表学生' AS type, COUNT(*) AS count
FROM sys_user WHERE user_type IN ('student', '02')
UNION ALL
SELECT
'学生表记录' AS type, COUNT(*) AS count
FROM study_student;
-- 两个数量应该一致
```
---
### **步骤2修复代码已完成**
**文件:** `SysUserServiceImpl.java`
**修改:** 添加级联删除逻辑
```java
@Override
@Transactional
public int deleteUserById(Long userId)
{
// ✅ 新增:级联删除学生相关数据
deleteStudentRelatedData(userId);
// 删除用户与角色关联
userRoleMapper.deleteUserRoleByUserId(userId);
// 删除用户与岗位表
userPostMapper.deleteUserPostByUserId(userId);
return userMapper.deleteUserById(userId);
}
```
**说明:**
- 目前只添加了框架和注释
- 需要后续注入相关Mapper并实现删除逻辑
- 建议使用存储过程或触发器完成级联删除
---
### **步骤3数据库级联删除推荐**
**方案A添加外键约束最佳**
```sql
-- 给study_student表添加外键约束
ALTER TABLE study_student
ADD CONSTRAINT fk_student_user
FOREIGN KEY (user_id)
REFERENCES sys_user(user_id)
ON DELETE CASCADE;
-- 给其他关联表也添加
ALTER TABLE study_student_class
ADD CONSTRAINT fk_student_class_student
FOREIGN KEY (student_id)
REFERENCES study_student(student_id)
ON DELETE CASCADE;
ALTER TABLE study_learning_record
ADD CONSTRAINT fk_learning_student
FOREIGN KEY (student_id)
REFERENCES study_student(student_id)
ON DELETE CASCADE;
-- ... 其他表类似
```
**方案B创建存储过程**
```sql
DELIMITER //
CREATE PROCEDURE delete_user_cascade(IN p_user_id BIGINT)
BEGIN
DECLARE v_student_id BIGINT;
-- 获取学生ID
SELECT student_id INTO v_student_id
FROM study_student
WHERE user_id = p_user_id;
IF v_student_id IS NOT NULL THEN
-- 按顺序删除关联数据
DELETE FROM study_learning_detail WHERE student_id = v_student_id;
DELETE FROM study_learning_record WHERE student_id = v_student_id;
DELETE FROM study_course_assignment WHERE student_id = v_student_id;
DELETE FROM study_student_class WHERE student_id = v_student_id;
DELETE FROM study_exam_record WHERE student_id = v_student_id;
DELETE FROM study_student_answer WHERE student_id = v_student_id;
DELETE FROM study_student_change_log WHERE student_id = v_student_id;
DELETE FROM study_student WHERE student_id = v_student_id;
END IF;
-- 删除用户角色和岗位
DELETE FROM sys_user_role WHERE user_id = p_user_id;
DELETE FROM sys_user_post WHERE user_id = p_user_id;
-- 删除用户
DELETE FROM sys_user WHERE user_id = p_user_id;
END//
DELIMITER ;
-- 使用:
CALL delete_user_cascade(123);
```
---
## 📝 **完整执行计划**
### **立即执行(解决当前问题):**
1.**备份数据库**
```bash
mysqldump -u用户名 -p数据库名 > backup_$(date +%Y%m%d).sql
```
2.**清理孤儿数据**
```bash
mysql -u用户名 -p数据库名 < log/数据清理-删除孤儿数据.sql
```
3.**验证数据一致性**
```sql
SELECT
(SELECT COUNT(*) FROM sys_user WHERE user_type IN ('student', '02')) AS user_count,
(SELECT COUNT(*) FROM study_student) AS student_count;
-- 两个数量应该相等
```
---
### **后续优化(防止再次发生):**
1.**方案1推荐数据库添加外键约束 + ON DELETE CASCADE**
- 优点:数据库级别保证数据一致性
- 缺点:需要重建外键(测试环境先验证)
2.**方案2使用存储过程**
- 优点:集中管理删除逻辑
- 缺点需要修改Java代码调用存储过程
3.**方案3完善Java代码级联删除**
- 优点:灵活可控
- 缺点需要注入多个Mapper代码量大
---
## ⚠️ **注意事项**
1. **必须先备份数据库!**
2. **先在测试环境验证**
3. **执行清理脚本前确认要删除的数据**
4. **清理后验证数据一致性**
5. **重新编译并部署后端(包含修复)**
---
## 🎯 **验证步骤**
### **清理前:**
```sql
-- 查询孤儿学生数
SELECT COUNT(*) FROM study_student s
LEFT JOIN sys_user u ON s.user_id = u.user_id
WHERE u.user_id IS NULL;
-- 可能显示5个孤儿数据
```
### **清理后:**
```sql
-- 再次查询应该为0
SELECT COUNT(*) FROM study_student s
LEFT JOIN sys_user u ON s.user_id = u.user_id
WHERE u.user_id IS NULL;
-- 应该显示0
-- 验证数量一致
SELECT
'用户表学生' AS type, COUNT(*) AS count
FROM sys_user WHERE user_type IN ('student', '02')
UNION ALL
SELECT
'学生表' AS type, COUNT(*) AS count
FROM study_student
UNION ALL
SELECT
'班级学生' AS type, COUNT(DISTINCT student_id) AS count
FROM study_student_class;
-- 三个数量应该基本一致
```
---
## ✅ **总结**
**问题根源:**
- 删除用户时没有级联删除学生数据
- 导致"孤儿"学生记录存在
**解决方案:**
1. 立即执行SQL清理脚本删除孤儿数据
2. 添加数据库外键约束推荐或完善Java代码
3. 测试验证数据一致性
**预防措施:**
- 使用数据库外键约束 + ON DELETE CASCADE
- 或者在删除用户前检查是否有关联数据并提示
**已提供文件:**
- ✅ 数据清理SQL脚本`log/数据清理-删除孤儿数据.sql`
- ✅ 代码修复:`SysUserServiceImpl.java`(已添加级联删除框架)
- ✅ 说明文档:`log/学生数据不一致问题-解决方案.md`