Files
2025-12-25 18:01:27 +08:00

24 KiB

06-完整SQL脚本

📋 概述

本分文档提供完整的学生退学、休学、毕业处理SQL脚本,包括数据库事务处理、错误处理和操作日志记录。基于对前后端代码的深入分析,脚本涵盖了所有相关业务模块的联动处理。

🔧 完整处理脚本示例

学生状态变更通用处理存储过程

-- ===========================================
-- 学生状态变更通用处理存储过程
-- 支持:休学(XIU_XUE)、退学(TUI_XUE)、复学(FU_XUE)、毕业(BI_YE)
-- 参数说明:
-- @student_id: 学生ID
-- @student_no: 学生学号
-- @change_type: 变更类型
-- @operator_id: 操作员ID
-- @effective_date: 生效日期 (可选,默认当前日期)
-- ===========================================

DELIMITER //

CREATE PROCEDURE process_student_status_change(
    IN p_student_id BIGINT,
    IN p_student_no VARCHAR(32),
    IN p_change_type VARCHAR(20),
    IN p_operator_id BIGINT,
    IN p_effective_date DATE
)
BEGIN
    DECLARE v_current_time DATETIME DEFAULT NOW();
    DECLARE v_effective_date DATE DEFAULT COALESCE(p_effective_date, CURDATE());
    DECLARE v_new_student_status VARCHAR(10);
    DECLARE v_disable_user_account BOOLEAN DEFAULT FALSE;
    DECLARE v_exit_reason VARCHAR(100);
    DECLARE v_cadre_status VARCHAR(10);
    DECLARE v_dismissal_reason VARCHAR(200);
    DECLARE v_error_message VARCHAR(500);
    DECLARE v_affected_rows INT DEFAULT 0;

    -- 声明异常处理
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET v_error_message = CONCAT('学生状态变更处理失败: ', p_change_type, ', 学生ID: ', p_student_id);
        -- 记录错误日志
        INSERT INTO sys_oper_log (
            title, business_type, method, request_method, operator_type,
            oper_name, dept_name, oper_url, oper_ip, oper_location,
            oper_param, json_result, status, error_msg, oper_time, cost_time
        ) VALUES (
            '学生状态变更', 'UPDATE', 'processStudentStatusChange', 'PROCEDURE', 'MANAGE',
            '系统', '教务处', '/student/status/change', '127.0.0.1', '本地',
            CONCAT('学生ID:', p_student_id, ',变更类型:', p_change_type), '{}', 1, v_error_message, v_current_time, 0
        );
        RESIGNAL;
    END;

    -- 开始事务
    START TRANSACTION;

    -- 1. 验证学生存在性
    SELECT COUNT(1) INTO v_affected_rows FROM xg_student_msg
    WHERE id = p_student_id AND deleted = 0;
    IF v_affected_rows = 0 THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '学生不存在或已被删除';
    END IF;

    -- 2. 根据变更类型设置参数
    CASE p_change_type
        WHEN 'XIU_XUE' THEN
            SET v_new_student_status = '2';  -- 休学
            SET v_disable_user_account = FALSE;  -- 休学可保留账号
            SET v_exit_reason = '学生休学';
            SET v_cadre_status = '6';  -- 休学离任
            SET v_dismissal_reason = '学生休学';

        WHEN 'TUI_XUE' THEN
            SET v_new_student_status = '4';  -- 退学
            SET v_disable_user_account = TRUE;  -- 退学必须禁用账号
            SET v_exit_reason = '学生退学';
            SET v_cadre_status = '4';  -- 免职离任
            SET v_dismissal_reason = '学生退学';

        WHEN 'FU_XUE' THEN
            SET v_new_student_status = '3';  -- 复学
            SET v_disable_user_account = FALSE;  -- 复学恢复账号
            SET v_exit_reason = NULL;  -- 复学不涉及退出
            SET v_cadre_status = NULL;  -- 复学不涉及干部
            SET v_dismissal_reason = NULL;

        WHEN 'BI_YE' THEN
            SET v_new_student_status = '5';  -- 毕业
            SET v_disable_user_account = TRUE;  -- 毕业禁用账号
            SET v_exit_reason = '学生毕业';
            SET v_cadre_status = '4';  -- 免职离任
            SET v_dismissal_reason = '学生毕业';

        ELSE
            SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '无效的变更类型';
    END CASE;

    -- 3. 创建状态变更记录
    INSERT INTO xg_student_status_change (
        student_id, student_no, change_type, apply_date,
        effective_date, operator_id, source, process_status
    ) VALUES (
        p_student_id, p_student_no, p_change_type, v_current_time,
        v_effective_date, p_operator_id, 'system', 1
    );

    -- 4. 备份原始数据到his表
    INSERT INTO xg_student_msg_his SELECT * FROM xg_student_msg WHERE id = p_student_id;
    INSERT INTO sys_user_his SELECT * FROM sys_user WHERE user_id = p_student_id;

    -- 5. 更新学生学籍状态
    UPDATE xg_student_msg
    SET student_status = v_new_student_status,
        update_by = p_operator_id,
        update_time = v_current_time
    WHERE id = p_student_id AND deleted = 0;

    -- 6. 处理用户账号状态
    IF v_disable_user_account THEN
        UPDATE sys_user
        SET status = '1',
            update_by = p_operator_id,
            update_time = v_current_time
        WHERE user_id = p_student_id AND account_type = '2' AND del_flag = '0';
    ELSE
        UPDATE sys_user
        SET status = '0',
            update_by = p_operator_id,
            update_time = v_current_time
        WHERE user_id = p_student_id AND account_type = '2' AND del_flag = '0';
    END IF;

    -- 6. 处理班级干部信息(休学、退学、毕业)
    IF p_change_type IN ('XIU_XUE', 'TUI_XUE', 'BI_YE') THEN
        UPDATE xg_class_pos_msg
        SET is_current = 0,
            cadre_status = v_cadre_status,
            dismissal_date = v_effective_date,
            dismissal_reason = v_dismissal_reason,
            term_end_date = v_effective_date,
            term_duration = TIMESTAMPDIFF(MONTH, term_start_date, v_effective_date),
            update_by = p_operator_id,
            update_time = v_current_time
        WHERE student_id = p_student_id AND is_current = 1 AND deleted = 0;

        -- 清理干部用户角色权限
        DELETE FROM sys_user_role
        WHERE user_id = p_student_id;
    END IF;

    -- 7. 处理社团成员关系(休学、退学、毕业)
    IF p_change_type IN ('XIU_XUE', 'TUI_XUE', 'BI_YE') THEN
        UPDATE stgl_member
        SET status = 4,  -- 已退出
            update_by = p_operator_id,
            update_time = v_current_time,
            remark = CONCAT(COALESCE(remark, ''), ' | ', v_exit_reason, ' - ', v_current_time)
        WHERE user_id = p_student_id AND status IN (0, 1, 2) AND deleted = 0;
    END IF;


    -- 11. 处理宿舍住宿关系(退学、毕业)
    IF p_change_type IN ('TUI_XUE', 'BI_YE') THEN
        UPDATE xg_dorm_bed_student
        SET status = '1',
            checkout_time = v_current_time,
            update_by = p_operator_id,
            update_time = v_current_time,
            remark = CONCAT(COALESCE(remark, ''), ' | ', v_exit_reason, '退宿')
        WHERE student_id = p_student_id AND status = '0' AND deleted = '0';
    END IF;


    -- 提交事务
    COMMIT;

    -- 记录操作日志
    INSERT INTO sys_oper_log (
        title, business_type, method, request_method, operator_type,
        oper_name, dept_name, oper_url, oper_ip, oper_location,
        oper_param, json_result, status, error_msg, oper_time, cost_time
    ) VALUES (
        '学生状态变更', 'UPDATE', 'processStudentStatusChange', 'PROCEDURE', 'MANAGE',
        '系统', '教务处', '/student/status/change', '127.0.0.1', '本地',
        CONCAT('学生ID:', p_student_id, ',学号:', p_student_no, ',变更类型:', p_change_type), '{}', 0, NULL, v_current_time, 0
    );

END //

DELIMITER ;

学生退学处理专用脚本

-- ===========================================
-- 学生退学处理专用脚本
-- 参数说明:
-- @student_id: 学生ID
-- @student_no: 学生学号
-- @operator_id: 操作员ID
-- ===========================================

DELIMITER //

CREATE PROCEDURE process_student_dropout(
    IN p_student_id BIGINT,
    IN p_student_no VARCHAR(32),
    IN p_operator_id BIGINT
)
BEGIN
    CALL process_student_status_change(p_student_id, p_student_no, 'TUI_XUE', p_operator_id, CURDATE());
END //

DELIMITER ;

-- 调用示例:
-- CALL process_student_dropout(123456789, '20240001', 1);

学生休学处理专用脚本

-- ===========================================
-- 学生休学处理专用脚本
-- 参数说明:
-- @student_id: 学生ID
-- @student_no: 学生学号
-- @operator_id: 操作员ID
-- ===========================================

DELIMITER //

CREATE PROCEDURE process_student_suspend(
    IN p_student_id BIGINT,
    IN p_student_no VARCHAR(32),
    IN p_operator_id BIGINT
)
BEGIN
    CALL process_student_status_change(p_student_id, p_student_no, 'XIU_XUE', p_operator_id, CURDATE());
END //

DELIMITER ;

-- 调用示例:
-- CALL process_student_suspend(123456789, '20240001', 1);

学生毕业处理专用脚本

-- ===========================================
-- 学生毕业处理专用脚本
-- 参数说明:
-- @student_id: 学生ID
-- @student_no: 学生学号
-- @operator_id: 操作员ID
-- ===========================================

DELIMITER //

CREATE PROCEDURE process_student_graduation(
    IN p_student_id BIGINT,
    IN p_student_no VARCHAR(32),
    IN p_operator_id BIGINT
)
BEGIN
    CALL process_student_status_change(p_student_id, p_student_no, 'BI_YE', p_operator_id, CURDATE());
END //

DELIMITER ;

-- 调用示例:
-- CALL process_student_graduation(123456789, '20240001', 1);

📋 处理流程图

graph TD
    A[开始] --> B[验证学生信息]
    B --> C{学生存在?}
    C -->|否| D[抛出异常]
    C -->|是| E{变更类型}

    E -->|休学| F[设置休学参数]
    E -->|退学| G[设置退学参数]
    E -->|复学| H[设置复学参数]
    E -->|毕业| I[设置毕业参数]

    F --> J[创建变更记录]
    G --> J
    H --> J
    I --> J

    J --> K[更新学生学籍状态]
    K --> L[处理用户账号状态]

    L --> M{需要禁用账号?}
    M -->|是| N[禁用用户账号]
    M -->|否| O[保留用户账号]

    N --> P{涉及组织关系?}
    O --> P
    P -->|是| Q[处理班级干部关系]
    P -->|否| R[跳过组织关系处理]

    Q --> S[处理社团成员关系]
    R --> T[处理经济资助]

    S --> T
    T --> U[奖学金申请处理]
    T --> V[勤工俭学申请处理]
    T --> W[贫困生认定处理]

    U --> X{涉及住宿管理?}
    V --> X
    W --> X

    X -->|是| Y[处理宿舍关系]
    X -->|否| Z[处理心理健康]

    Y --> Z
    Z --> AA[心理咨询预约取消]

    AA --> BB{涉及实践活动?}
    BB -->|是| CC[取消学生活动报名]
    BB -->|否| DD[跳过实践活动处理]

    CC --> EE[终止实习信息]

    EE --> FF[记录操作日志]
    DD --> FF

    FF --> GG[提交事务]
    GG --> HH[结束]

    D --> II[异常处理]
    II --> JJ[回滚事务]
    JJ --> KK[记录错误日志]

🛠️ 技术实现细节

1. 后端服务层实现

📂 核心类文件

  • 实体类: XgStudentMsg.java - 学生信息实体
  • 变更实体: XgStudentStatusChange.java - 状态变更记录实体
  • 控制器: XgStudentStatusChangeController.java - API接口层
  • 服务类: XgStudentStatusChangeServiceImpl.java - 业务逻辑层
  • Mapper: XgStudentStatusChangeMapper.java - 数据访问层

🔧 核心业务方法

@Service
@RequiredArgsConstructor
public class XgStudentStatusChangeServiceImpl implements IXgStudentStatusChangeService {

    private final XgStudentStatusChangeMapper baseMapper;
    private final XgStudentMsgMapper studentMapper;
    private final SysUserMapper userMapper;
    private final XgClassPosMsgMapper classPosMapper;
    private final StglMemberMapper memberMapper;

    @Transactional(rollbackFor = Exception.class)
    public Boolean processStudentStatusChange(XgStudentStatusChangeBo bo) {
        // 1. 验证学生信息
        XgStudentMsg student = validateStudent(bo.getStudentId());

        // 2. 验证业务规则
        validateChangeRules(student, bo.getChangeType());

        // 3. 创建变更记录
        XgStudentStatusChange change = createChangeRecord(bo);

        // 4. 更新学生状态
        updateStudentStatus(student, bo.getChangeType(), bo.getOperatorId());

        // 5. 处理用户账号
        handleUserAccount(student, bo.getChangeType(), bo.getOperatorId());

        // 6. 处理组织关系
        handleOrganizationRelations(student, bo.getChangeType(), bo.getOperatorId());

        // 7. 处理经济资助
        handleEconomicSupport(student, bo.getChangeType(), bo.getOperatorId());

        // 8. 处理其他业务
        handleOtherBusiness(student, bo.getChangeType(), bo.getOperatorId());

        // 9. 记录操作日志
        logOperation(change);

        return true;
    }

    private XgStudentMsg validateStudent(Long studentId) {
        XgStudentMsg student = studentMapper.selectById(studentId);
        if (student == null || student.getDeleted() == 1L) {
            throw new ServiceException("学生不存在或已被删除");
        }
        return student;
    }

    private void validateChangeRules(XgStudentMsg student, String changeType) {
        String currentStatus = student.getStudentStatus();
        // 状态变更规则验证
        switch (changeType) {
            case "XIU_XUE": // 休学
                if (!"1".equals(currentStatus)) { // 必须是在读状态
                    throw new ServiceException("只有在读学生才能申请休学");
                }
                break;
            case "TUI_XUE": // 退学
                if ("5".equals(currentStatus)) { // 毕业状态不能退学
                    throw new ServiceException("已毕业学生不能退学");
                }
                break;
            case "FU_XUE": // 复学
                if (!"2".equals(currentStatus)) { // 必须是休学状态
                    throw new ServiceException("只有休学学生才能复学");
                }
                break;
            case "BI_YE": // 毕业
                if (!"1".equals(currentStatus) && !"3".equals(currentStatus)) {
                    throw new ServiceException("只有在读或复学学生才能毕业");
                }
                break;
        }
    }
}

2. 前端界面实现

📱 状态变更界面

文件位置: web/src/views/xg/xsjcxx/xgStudentStatusChange/index.vue

<template>
  <div class="app-container">
    <!-- 查询表单 -->
    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="学号" prop="studentNo">
        <el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable style="width: 200px" @keyup.enter="handleQuery" />
      </el-form-item>
      <el-form-item label="变更类型" prop="changeType">
        <el-select v-model="queryParams.changeType" placeholder="请选择变更类型" clearable style="width: 200px">
          <el-option label="休学" value="XIU_XUE" />
          <el-option label="退学" value="TUI_XUE" />
          <el-option label="复学" value="FU_XUE" />
          <el-option label="毕业" value="BI_YE" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <!-- 操作按钮 -->
    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['studentMsg:studentStatusChange:add']">
          新增
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['studentMsg:studentStatusChange:edit']">
          修改
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['studentMsg:studentStatusChange:remove']">
          删除
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button type="warning" plain icon="Operation" @click="handleBatchProcess" v-hasPermi="['studentMsg:studentStatusChange:process']">
          批量处理
        </el-button>
      </el-col>
    </el-row>

    <!-- 数据表格 -->
    <el-table v-loading="loading" :data="statusChangeList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" />
      <el-table-column label="学号" align="center" prop="studentNo" />
      <el-table-column label="学生姓名" align="center">
        <template #default="scope">
          <span>{{ scope.row.studentName }}</span>
        </template>
      </el-table-column>
      <el-table-column label="变更类型" align="center" prop="changeType">
        <template #default="scope">
          <el-tag :type="getChangeTypeTagType(scope.row.changeType)">
            {{ getChangeTypeLabel(scope.row.changeType) }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column label="生效日期" align="center" prop="effectiveDate" width="120">
        <template #default="scope">
          <span>{{ parseTime(scope.row.effectiveDate, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="处理状态" align="center" prop="processStatus" width="100">
        <template #default="scope">
          <el-tag :type="scope.row.processStatus === 1 ? 'success' : 'warning'">
            {{ scope.row.processStatus === 1 ? '已处理' : '未处理' }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column label="操作员" align="center" prop="operatorName" />
      <el-table-column label="操作时间" align="center" prop="createTime" width="180" />
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template #default="scope">
          <el-button size="small" type="text" icon="View" @click="handleView(scope.row)" v-hasPermi="['studentMsg:studentStatusChange:query']">详情</el-button>
          <el-button size="small" type="text" icon="Edit" @click="handleUpdate(scope.row)" v-if="scope.row.processStatus === 0" v-hasPermi="['studentMsg:studentStatusChange:edit']">修改</el-button>
          <el-button size="small" type="text" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['studentMsg:studentStatusChange:remove']">删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 分页组件 -->
    <pagination
      v-show="total>0"
      :total="total"
      v-model:page="queryParams.pageNum"
      v-model:limit="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 状态变更对话框 -->
    <el-dialog :title="title" v-model="open" width="600px" append-to-body>
      <el-form ref="statusChangeForm" :model="form" :rules="rules" label-width="100px">
        <!-- 学生信息选择 -->
        <el-row>
          <el-col :span="12">
            <el-form-item label="学号" prop="studentNo">
              <el-input v-model="form.studentNo" placeholder="请输入学号" @blur="handleStudentNoBlur" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="学生姓名">
              <el-input v-model="form.studentName" disabled />
            </el-form-item>
          </el-col>
        </el-row>

        <!-- 变更信息 -->
        <el-row>
          <el-col :span="12">
            <el-form-item label="变更类型" prop="changeType">
              <el-select v-model="form.changeType" placeholder="请选择变更类型" style="width: 100%">
                <el-option label="休学" value="XIU_XUE" />
                <el-option label="退学" value="TUI_XUE" />
                <el-option label="复学" value="FU_XUE" />
                <el-option label="毕业" value="BI_YE" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="生效日期" prop="effectiveDate">
              <el-date-picker
                v-model="form.effectiveDate"
                type="date"
                placeholder="选择生效日期"
                value-format="YYYY-MM-DD"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>

        <el-form-item label="变更原因" prop="reason">
          <el-input
            v-model="form.reason"
            type="textarea"
            placeholder="请输入变更原因"
            maxlength="500"
            show-word-limit
            :rows="3"
          />
        </el-form-item>
      </el-form>

      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确 定</el-button>
          <el-button @click="cancel">取 消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

🔄 业务规则总结

1. 状态变更矩阵

当前状态 可变更状态 说明 账号处理 组织关系 经济资助 住宿管理
在读(1) 休学(2)、退学(4)、毕业(5) 正常学籍状态 休学保留,退学/毕业禁用 处理干部和社团 终止申请 退学/毕业退宿
休学(2) 复学(3)、退学(4) 休学后状态 复学恢复,退学禁用 退学处理干部和社团 终止申请 退学退宿
复学(3) 休学(2)、退学(4)、毕业(5) 复学后状态 毕业禁用 退学处理干部和社团 终止申请 退学/毕业退宿
其他状态 需特殊审批 异常状态 根据情况 根据情况 根据情况 根据情况

2. 处理优先级

  1. 数据验证: 学生存在性、状态合法性
  2. 核心状态: 学籍状态、用户账号
  3. 组织关系: 班级干部、社团成员
  4. 经济资助: 奖学金、勤工俭学、贫困生认定
  5. 其他业务: 宿舍、心理咨询、活动报名、实习
  6. 日志记录: 操作审计和异常处理

3. 事务管理

  • 大事务模式: 所有操作在一个事务中完成
  • 回滚机制: 任何步骤失败都回滚整个操作
  • 异常处理: 详细记录错误信息和上下文
  • 操作日志: 记录完整的操作轨迹

⚠️ 注意事项

1. 性能优化

  • 批量处理: 支持多个学生的批量状态变更
  • 索引优化: 确保学生ID、学号相关的查询有适当索引
  • 异步处理: 大批量操作考虑异步处理机制
  • 缓存更新: 变更后及时更新相关缓存

2. 安全考虑

  • 权限验证: 严格验证操作员权限
  • 数据校验: 多重验证防止数据污染
  • 审计记录: 完整记录所有操作日志
  • 敏感操作: 重要操作需要二次确认

3. 扩展性设计

  • 模块化处理: 每个业务模块独立处理方法
  • 配置化规则: 状态变更规则可配置化
  • 插件化扩展: 支持业务模块的插件化扩展
  • 流程编排: 支持复杂业务流程的编排

📅 更新记录

  • 2025-12-XX: 分文档创建,包含完整SQL处理脚本
  • 2025-12-XX: 基于前后端代码分析完善完整SQL脚本和处理流程
  • 2025-12-XX: 增加通用处理存储过程和专用处理脚本
  • 2025-12-XX: 添加详细的技术实现细节和前端界面代码
  • 后续更新: 根据实际业务需求持续完善

本分文档基于对前后端代码的深入分析,提供完整的SQL处理脚本和详细的技术实现方案,确保业务逻辑的准确性和系统的高可用性。