Files
hzdyjs/docs/业务模块/专业信息管理/专业合并功能实施计划.md
T
2026-08-20 18:15:07 +08:00

15 KiB
Raw Blame History

专业合并功能实施计划

状态:P0 已落地(待联调验收)
更新时间:2026-08-18
入口页面:/student/classmsg/majormsg(专业信息)
背景:生产环境存在重复专业定义(如「中药」与「中药(技师)」),需将源专业下班级/学生及业务当前归属合并到目标专业。

已落地代码位置

项 路径
预览/合并 API POST /xg/major/merge/preview、POST /xg/major/merge
Controller ruoyi-xg/.../XgMajorMergeController.java
Service ruoyi-xg/.../XgMajorMergeServiceImpl.java
前端 web/src/views/xg/xsjcxx/classmsg/majormsg/index.vue
DDL docs/设计文档/database/专业信息管理/专业合并快照表结构.sql
权限 docs/设计文档/database/专业信息管理/专业合并菜单权限.sql

1. 目标与原则

1.1 业务目标

在专业信息界面多选专业 → 点击【专业合并】→ 弹窗从已选中指定一个目标专业 → 其余源专业的当前归属数据全部改挂到目标专业 → 源专业停用。

1.2 已确认规则

规则 结论
班级处理 班级整体挂到目标专业;班级 ID 不变;同名班不合并
学生处理 只改专业归属,不改班级
源专业处理后 本版仅 停用(status=1),是否逻辑删除后续再定
学院约束 仅允许同一学院下的专业互相合并
历史快照 转班等「原专业/新专业」历史记录 不回写

1.3 核心原则(防漏改)

凡表示「当前专业归属」的字段,major_id 与 major_name(以及存在的 major_code)必须成对同步更新。

禁止只改 ID、不改名称:列表/导出/筛选会出现「ID 已是中药、名称仍显示中药(技师)」的异常。

更新条件统一为:

WHERE major_id IN (源专业ID列表)
SET major_id = 目标专业ID,
    major_name = 目标专业名称
-- 若表有 major_code / major_dept_id,一并改为目标专业对应值

2. 改造范围总表

2.1 组织与主数据(必改,事务内优先)

序号 表 动作 ID / 名称字段
1 sys_dept(班级节点 bj-*) parent_id 改为目标专业;重算 ancestors 无冗余名称
2 sys_dept(源专业 zy-*) status = '1'(停用) dept_name 保留作追溯
3 xg_class_msg 当前归属改挂 major_id + major_name 同改
4 xg_student_msg 当前归属改挂 major_id + major_name 同改
5 xg_student_manager_source data_id 若等于源专业 ID,改为目标专业 ID 无名称字段

说明:sys_user.dept_id 本系统学生通常挂班级;合并时班级 ID 不变则用户表一般无需改。合并前应用 SQL 探测是否存在 dept_id IN (源专业ID),有则改为目标专业 ID。

2.2 业务冗余表(必改:当前归属 = 源专业时)

原则:有 ID 改 ID,有名称改名称,有编码改编码;能按 ID 定位的优先按 ID。

模块 表 同改字段 备注
请假 xg_student_leave major_id + major_name
销假 xg_student_cancel_leave major_id + major_name
宿舍申请 xg_dorm_application major_id + major_name
床位分配 xg_dorm_bed_student major_id 无名称字段
卫生评分 xg_dorm_hygiene_student_score major_id + major_name
查寝明细 xg_dorm_roll_call_detail major_id + major_name
违纪 xg_dorm_violation major_id + major_name
假期留宿 xg_holiday_stay major_id + major_name
信息收集批次班级 xg_stu_col_batch_class major_id + major_name
学生信息批次 xg_student_msg_batch major_id + major_name
心理测评 xljk_evaluation major_id + major_name
考核申请 xskh_assessment_application major_id + major_name
考核审批 xskh_assessment_application_approval major_id + major_name
处分 xskh_discipline_record major_id + major_name
奖励 xskh_reward_record major_id + major_name
贫困认定 jcbt_poverty_application major_id + major_name(+major_code 若有)
奖学金申请 jcbt_scholarship_application major_id 无名称字段
勤工助学 jcbt_work_study_application 仅有 major_name 见 §2.4 特殊处理
毕业生 zsjy_graduate_info major_id + major_name
实习 zsjy_internship_info major_id 无名称字段
招生专业配置 zsjy_enrollment_major major_dept_id + major_name + major_code 特殊:存在 uk_major_dept 唯一键;目标已有配置则逻辑删除源行,否则改挂

2.3 配置 / 权限类(有则改)

表 动作
jcbt_scholarship_quota target_dept_id 若等于源专业 ID → 改为目标专业 ID(注意唯一约束冲突时需合并名额行)
sys_role_dept dept_id 若等于源专业 ID → 改为目标专业 ID(注意主键冲突去重)

2.4 特殊处理表

表 策略
jcbt_work_study_application 仅有 major_name:按源专业名称精确匹配更新为目标专业名称;合并前日志记录影响行数
xg_student_class_change 不更新(original_* / new_* 为历史真相)
*_his、备份表、sys_oper_log 不更新
cx_zysj / cx_bjsj / cx_xssj 本版不更新;文档标注超星同步回写风险,P2 做映射
zsjy_major_info / his_* 非学工主链路组织树;本版不改,避免误伤招生目录

2.5 班级维度表(一般不必改专业)

以下按 class_id 关联,班级 ID 不变则无需因合并改行(除非另有 major_id):

  • xg_conduct_score
  • xg_class_pos_msg
  • xg_class_teacher_counselor_rel

合并后统计「按专业」时走班级 → xg_class_msg.major_id,因此班级主数据改对即可。


3. 防漏改机制(解决「范围不够」)

3.1 后端统一更新器(推荐)

不要散落多处手写 UPDATE。在服务内维护白名单清单:

MajorMergeTableSpec {
  tableName
  idColumn          // major_id / major_dept_id / data_id / target_dept_id / dept_id
  nameColumn?       // major_name,可空
  codeColumn?       // major_code,可空
  extraWhere?       // 如 deleted=0
  strategy          // BY_ID | BY_NAME_ONLY | DEPT_TREE | SKIP_HISTORY
}

合并服务循环执行:

  1. 预检 COUNT(按源 ID / 源名称)→ 写入合并结果 VO / 操作日志
  2. 事务内按清单 UPDATE
  3. 组织树改挂 + 源专业停用
  4. 合并后复查:各表白名单 COUNT(源专业ID) == 0,否则整单回滚并报错

3.2 合并前预检 SQL(上线前必跑)

对每个候选源专业 ID,输出各表命中行数;发现「新表有 major_id 但不在白名单」时,禁止上线,先补清单。

可维护仓库脚本:test/专业合并影响行数预检.sql(开发阶段补充)。

3.3 名称同步硬规则

IF 表同时存在 major_id 与 major_name:
    禁止只更新其中一个
IF 仅存在 major_name:
    使用源专业名称精确匹配更新(并记录日志)
IF 仅存在 major_id:
    只更新 ID

目标专业名称取自合并时刻 sys_dept.dept_name(目标节点),保证与组织树一致。


3.4 合并前行级快照备份(P0 必做,便于错误恢复)

为什么要做 / 为什么不做整表备份

方式 结论
整表 CREATE TABLE AS SELECT 不采用:学生/业务表体量大,每次合并磁盘与耗时不可控
仅事务回滚 不够:合并成功提交后若发现选错目标专业,事务已结束无法回退
只备份将被修改的行 采用:体积小、可按 merge_batch_id 追溯与回滚

表设计

1)合并批次主表 xg_major_merge_batch

字段 说明
id 雪花 ID,即 merge_batch_id
target_major_id / target_major_name 目标专业
source_major_ids / source_major_names JSON,源专业列表
college_id 所属学院
status SUCCESS / FAILED / ROLLED_BACK
summary_json 各表更新行数摘要
operator_id / create_time 等 标准审计字段

2)行快照表 xg_major_merge_row_snapshot

字段 说明
id 雪花 ID
merge_batch_id 关联批次
table_name 原表名,如 xg_student_msg
row_pk 原表主键值(字符串,兼容联合主键时用约定拼接)
row_json 更新前整行 JSON(或至少还原所需列:专业相关字段 + 主键 + 组织树字段)
create_time 快照时间

说明:sys_dept 班级改挂、源专业停用前,同样写入快照(记录改前的 parent_id/ancestors/status)。

执行顺序(必须先快照再改数)

1. 校验参数与同学院
2. 生成 merge_batch_id,写入 batch 主表(status 可先 SUCCESS 前用 RUNNING)
3. 按白名单:把「即将被 UPDATE 的行」INSERT 进 row_snapshot
4. 再执行各表 UPDATE / 组织树改挂 / 停用源专业
5. 复查无源专业 ID 残留
6. 更新 batch.summary_json、status=SUCCESS
失败则事务回滚(含已写入的本批快照);若需「成功后回滚」见下

事务策略建议:

  • 同事务:快照 + 改数同一事务 → 失败自动干净回滚。
  • 成功后误操作恢复:另提供运维接口/SQL:按 merge_batch_id 读取 row_json 回写原表(P0 至少落库快照并文档化手工回滚步骤;P1 再做一键 rollback API)。

回滚注意事项

  • 回滚只能恢复「本批改过的行」;合并后若业务又改过这些行,回滚会覆盖后续人工修改 → 回滚前需人工确认。
  • sys_role_dept / 名额表若发生「删源行 + 合并到目标行」,快照需记录删除前整行,回滚逻辑单独处理。
  • 快照表本身不做业务查询用途,可按保留策略定期归档(如保留 180 天)。

4. 接口设计

4.1 影响预览(建议 P0 带上,降低误操作)

POST /system/dept/major/merge/preview
权限: majormsg:major:merge

Body:
{
  "targetMajorId": 20251223133304109,
  "sourceMajorIds": [20251223133304116]
}

Response data:
{
  "targetMajorId": ...,
  "targetMajorName": "中药",
  "sources": [
    {
      "majorId": ...,
      "majorName": "中药(技师)",
      "classCount": 1,
      "studentCount": 50,
      "tableHits": [
        { "table": "xg_student_leave", "rows": 0 },
        ...
      ]
    }
  ],
  "warnings": ["同学院校验通过", "..."]
}

4.2 执行合并

POST /system/dept/major/merge
权限: majormsg:major:merge

Body: 同 preview

Response data:
{
  "success": true,
  "mergeBatchId": 2089...,
  "movedClassCount": 1,
  "updatedStudentCount": 50,
  "disabledMajorCount": 1,
  "snapshotRowCount": 120,
  "tableUpdateSummary": [ { "table": "xg_class_msg", "updated": 1 }, ... ]
}

校验失败示例:

  • 源列表为空 / 未含目标 / 目标不在已选集合
  • 所选不足 2 个专业
  • 跨学院
  • 目标已停用或不存在
  • 复查仍有源专业 ID 残留

4.3 权限与菜单

  • 新增按钮权限:majormsg:major:merge
  • 菜单/角色授权与现有 majormsg:major:* 一并配置 SQL

5. 前端设计(majormsg/index.vue)

  1. 工具栏增加【专业合并】(v-hasPermi="['majormsg:major:merge']")
  2. selection.length < 2 时禁用
  3. 弹窗:
    • 展示已选专业列表(名称 + 类别编码)
    • 单选「目标专业」(仅能选已选中的项)
    • 可选:调用 preview 展示班级数/学生数/业务命中
    • 二次确认文案:明确「源专业将停用;班级不合并;名称与 ID 同步改挂」
  4. 成功后刷新列表

6. 后端实现要点

项 说明
模块位置 建议放在系统部门能力旁:SysDept 相关 Service,或学工模块包装后调部门服务;事务边界在同一服务方法
事务 @Transactional(rollbackFor = Exception.class)
祖先路径 班级 ancestors = 目标专业.ancestors + ',' + 目标专业.deptId
操作日志 @Log 记录目标/源/摘要
幂等 源专业已停用且无子班级时可提示「无需合并」

伪代码顺序:

1. 校验参数与同学院
2. 创建 merge_batch,按白名单把「将改行」写入 xg_major_merge_row_snapshot
3. 更新各业务表白名单(ID+名称同改)
4. 更新 xg_student_msg / xg_class_msg
5. 更新 sys_dept 班级 parent/ancestors
6. 处理 manager_source / role_dept / scholarship_quota
7. 停用源专业
8. 复查 COUNT(源ID)==0,否则抛错回滚
9. 回写 batch 摘要与 SUCCESS;接口返回 mergeBatchId

7. 分期交付

阶段 内容 验收
P0 前端按钮+弹窗;preview+merge API;行级快照表+批次表;sys_dept+xg_class_msg+xg_student_msg+manager_source;白名单业务表 ID+名称同改;源专业停用;权限+日志;文档化按 batch 手工回滚步骤 用「中药(技师)→中药」演练:班级挂对、学生名称变「中药」、源专业停用、复查无残留;快照行数与更新行数一致
P1 预检脚本入库;名额/角色冲突处理增强;一键按 mergeBatchId 回滚 API;合并结果导出 成功合并后可一键恢复到合并前
P2 超星同步映射;可选逻辑删除源专业;快照归档策略 同步不回写重复专业

8. 测试用例(最小集)

  1. 同学院 2 个专业合并成功;目标名称写入所有命中业务行
  2. 只改 ID 漏改名称的回归:抽查 xg_student_msg、xg_student_leave 等 major_id 与 major_name 一致
  3. 跨学院拒绝
  4. 选 1 个专业无法合并
  5. 同名班级并存(不合并班级)
  6. xg_student_class_change 历史行不变
  7. 事务中途失败整单回滚(含本批快照)
  8. 无权限看不到按钮 / 接口 403
  9. 合并成功后快照可查;按文档步骤可手工恢复抽样表

9. 文档与配置同步(开发时)

  • API:docs/开发文档/API/ 下增补「专业合并」章节(或写入专业信息管理 API)
  • 权限初始化 SQL
  • 快照/批次表 DDL 放入 docs/设计文档/database/
  • 本实施计划随代码落地更新「状态:已完成」

10. 开放问题(实现前可默认)

问题 默认建议
勤工助学仅名称字段误伤同名? 精确匹配源 dept_name;合并前 preview 展示行数人工确认
奖学金名额行冲突 目标已有名额则累加 total_quota 后删源行,或报错人工处理(P0 可报错)
超星 P0 合并后口头告知运营:勿从超星再灌该源专业码
快照保留多久 默认 180 天,可配置

11. 评审确认清单

请确认后进入开发:

  • ID 与名称(及 code)同改原则同意
  • §2 表白名单范围同意(可增删)
  • 历史表 / 超星本版不改同意
  • P0 含 preview 同意
  • 权限码 majormsg:major:merge 同意
  • 合并前行级快照备份同意(非整表;P0 落库 + 手工回滚说明;P1 一键回滚)

确认后按本计划实现前后端与预检 SQL。