guoyu/log/学生数据不一致问题-解决方案.md
2025-12-11 23:28:07 +08:00

7.1 KiB
Raw Blame History

学生数据不一致问题 - 完整解决方案

📊 问题描述

现象:

  • 课程分配时选择班级/监区的学生数量 ≠ 用户管理的学生数量
  • 例如班级显示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

执行步骤:

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

修改: 添加级联删除逻辑

@Override
@Transactional
public int deleteUserById(Long userId)
{
    // ✅ 新增:级联删除学生相关数据
    deleteStudentRelatedData(userId);
    
    // 删除用户与角色关联
    userRoleMapper.deleteUserRoleByUserId(userId);
    // 删除用户与岗位表
    userPostMapper.deleteUserPostByUserId(userId);
    return userMapper.deleteUserById(userId);
}

说明:

  • 目前只添加了框架和注释
  • 需要后续注入相关Mapper并实现删除逻辑
  • 建议使用存储过程或触发器完成级联删除

步骤3数据库级联删除推荐

方案A添加外键约束最佳

-- 给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创建存储过程

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. 备份数据库

    mysqldump -u用户名 -p数据库名 > backup_$(date +%Y%m%d).sql
    
  2. 清理孤儿数据

    mysql -u用户名 -p数据库名 < log/数据清理-删除孤儿数据.sql
    
  3. 验证数据一致性

    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. 重新编译并部署后端(包含修复)

🎯 验证步骤

清理前:

-- 查询孤儿学生数
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个孤儿数据

清理后:

-- 再次查询应该为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