diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/FacultyServiceImpl.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/FacultyServiceImpl.java index 4195177fc..a95f2957c 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/FacultyServiceImpl.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/FacultyServiceImpl.java @@ -1,5 +1,6 @@ package com.roomroot.jwgl.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.roomroot.jwgl.dto.faculty.AddTeacherDTO; import com.roomroot.jwgl.entity.*; @@ -127,20 +128,133 @@ public class FacultyServiceImpl implements FacultyService { // ==================== 教员管理 ==================== @Override - @Transactional + @Transactional(rollbackFor = Exception.class) public void addJYB(AddTeacherDTO dto) { + if (dto == null || dto.getTeacher() == null) { + throw new BusinessException("教员信息不能为空"); + } JYB entity = dto.getTeacher(); - //离职状态默认为0 - entity.setLzzt(0); + + // 教员姓名必填 + if (isBlank(entity.getJyxm())) { + throw new BusinessException("教员姓名不能为空"); + } + entity.setJyxm(entity.getJyxm().trim()); + + // 教研室必填:教员表 [教研室代号] 为非空外键,缺失时会直接抛数据库非空约束异常 + if (isBlank(entity.getJysdh())) { + throw new BusinessException("请选择所属教研室"); + } + entity.setJysdh(entity.getJysdh().trim()); + if (jysbMapper.selectById(entity.getJysdh()) == null) { + throw new BusinessException("教研室不存在:" + entity.getJysdh()); + } + + // 教员编号:前端留空时自动生成(沿用库内既有形态 JY+两位序号),手填时校验唯一 + if (isBlank(entity.getJybh())) { + String generated = nextJybh(); + entity.setJybh(generated); + // 序号未填时与编号流水号保持一致(库内 JY02 对应 序号=2 的既有约定) + if (isBlank(entity.getXh())) { + String digits = generated.replaceAll("\\D", ""); + if (!digits.isEmpty()) { + entity.setXh(String.valueOf(Integer.parseInt(digits))); + } + } + } else { + entity.setJybh(entity.getJybh().trim()); + if (jybMapper.selectById(entity.getJybh()) != null) { + throw new BusinessException("教员编号已存在:" + entity.getJybh()); + } + } + + // 非空列兜底:MyBatis-Plus 只写入非 null 字段,字段为 null 时数据库缺省值不会生效 + applyTeacherDefaults(entity); jybMapper.insert(entity); - // 使用前端传入的教员属性数据 + + // 使用前端传入的教员属性数据([主任][教员编号] 为非空列,需兜底) JYSX jysx = dto.getAttributes(); if (jysx != null) { jysx.setJybh(entity.getJybh()); + if (jysx.getZr() == null) { + jysx.setZr(0); + } jysxMapper.insert(jysx); } } + /** 生成下一个教员编号:取库内 JY 前缀编号的最大流水号 +1(JY41、JY42…),异常或全不匹配时退化为 UUID */ + private String nextJybh() { + try { + List rows = jybMapper.selectList( + new LambdaQueryWrapper().select(JYB::getJybh).likeRight(JYB::getJybh, "JY")); + int max = 0; + for (JYB r : rows) { + String code = r.getJybh(); + if (code == null || code.length() <= 2) { + continue; + } + String digits = code.substring(2); + if (digits.matches("\\d+")) { + max = Math.max(max, Integer.parseInt(digits)); + } + } + for (int seq = max + 1; seq <= max + 500; seq++) { + String candidate = String.format("JY%02d", seq); + if (jybMapper.selectById(candidate) == null) { + return candidate; + } + } + } catch (Exception ignored) { + // 查询异常时退化为 UUID,避免新增教员被阻塞 + } + return UuidUtil.getUUID(); + } + + /** 教员表非空列的默认值兜底(与数据库缺省值保持一致) */ + private void applyTeacherDefaults(JYB e) { + if (e.getLzzt() == null) { + e.setLzzt(0); + } + if (e.getXslx() == null) { + e.setXslx(0); + } + if (e.getEwyxkbs() == null) { + e.setEwyxkbs(0); + } + if (e.getXb() == null) { + e.setXb(1); + } + if (e.getWxjy() == null) { + e.setWxjy(0); + } + if (e.getWxyy() == null) { + e.setWxyy(0); + } + if (e.getZzljy() == null) { + e.setZzljy(0); + } + if (isBlank(e.getZyjszwdj())) { + e.setZyjszwdj("初级"); + } + if (isBlank(e.getJylb())) { + e.setJylb("专业技术军官"); + } + if (isBlank(e.getZzlb())) { + e.setZzlb("在职"); + } + if (e.getDslb() == null) { + e.setDslb(""); + } + if (e.getXy() == null) { + e.setXy(""); + } + } + + private boolean isBlank(String s) { + return s == null || s.trim().isEmpty(); + } + @Override @Transactional public void disableJYB(String jybh) { @@ -172,15 +286,25 @@ public class FacultyServiceImpl implements FacultyService { if (list.isEmpty()) { throw new BusinessException("Excel中无有效数据"); } - for (JYB entity : list) { + for (int i = 0; i < list.size(); i++) { + JYB entity = list.get(i); + // Excel 第 1 行为抬头,故数据行号 = 下标 + 2 + int rowNo = i + 2; + if (isBlank(entity.getJyxm())) { + throw new BusinessException("第 " + rowNo + " 行:教员姓名未填写"); + } + // 教研室代号为非空外键,缺失会直接抛数据库非空约束异常,这里给出可定位的提示 + if (isBlank(entity.getJysdh())) { + throw new BusinessException("第 " + rowNo + " 行:教研室代号未填写"); + } // 编号为空自动生成 - if (entity.getJybh() == null || entity.getJybh().isEmpty()) { - entity.setJybh(UuidUtil.getUUID()); - } - // 离职状态默认 0 - if (entity.getLzzt() == null) { - entity.setLzzt(0); + if (isBlank(entity.getJybh())) { + entity.setJybh(nextJybh()); + } else if (jybMapper.selectById(entity.getJybh().trim()) != null) { + throw new BusinessException("第 " + rowNo + " 行:教员编号已存在 " + entity.getJybh()); } + // 离职状态等非空列兜底(数据库缺省值在 MyBatis-Plus 下不会生效) + applyTeacherDefaults(entity); jybMapper.insert(entity); } return list.size(); diff --git a/frontend/src/views/teachOffice/teacher/index.vue b/frontend/src/views/teachOffice/teacher/index.vue index 1ac4efea8..58cb352fa 100644 --- a/frontend/src/views/teachOffice/teacher/index.vue +++ b/frontend/src/views/teachOffice/teacher/index.vue @@ -98,6 +98,32 @@ > 教员基本信息 + + + + + + + + + + + + + + @@ -608,6 +634,7 @@ import { getTeacherAttribute, updateTeacherAttribute } from '@/api/teachOffice/teacher' +import { listOffice } from '@/api/teachOffice/office' import { getDicts } from '@/api/system/dict/data' export default { @@ -629,6 +656,8 @@ export default { addForm: this.createEmptyAddForm(), addRules: { 'teacher.jyxm': [{ required: true, message: '请输入教员姓名', trigger: 'blur' }], + 'teacher.jysdh': [{ required: true, message: '请选择所属教研室', trigger: 'change' }], + 'teacher.jybh': [{ max: 50, message: '教员工号不能超过 50 个字符', trigger: 'blur' }], 'teacher.xh': [{ required: true, message: '请输入序号', trigger: 'blur' }], 'teacher.zyjszwdj': [{ required: true, message: '请输入专业技术职务等级', trigger: 'blur' }], @@ -679,12 +708,16 @@ export default { zyjszwdjOptions: [], jylbOptions: [], zzlbOptions: [], - dslbOptions: [] + dslbOptions: [], + // 教研室下拉(新增教员时选择所属教研室) + officeOptions: [], + officeLoading: false } }, mounted() { this.fetchList() this.loadDicts() + this.loadOfficeOptions() }, methods: { /** 统一格式化后端 LocalDateTime(去除 T、截断到秒) */ @@ -726,6 +759,29 @@ export default { getDicts('teacher_dslb').then(res => { this.dslbOptions = res.data || [] }).catch(() => {}) }, + /** 加载教研室下拉:教员表 [教研室代号] 为非空外键,新增时必须选择 */ + loadOfficeOptions() { + this.officeLoading = true + listOffice({ pageNum: 1, pageSize: 500 }).then(response => { + const data = response.data || {} + const rows = data.records || [] + const seen = {} + const options = [] + rows.forEach(row => { + const jysdh = row.jysdh + // 已停用(ty=1)的教研室不再参与新建教员 + if (!jysdh || seen[jysdh] || Number(row.ty) === 1) return + seen[jysdh] = true + options.push({ jysdh, jysmc: row.jysmc || jysdh }) + }) + this.officeOptions = options + }).catch(() => { + this.officeOptions = [] + }).finally(() => { + this.officeLoading = false + }) + }, + cleanPayload(obj) { const payload = {} Object.keys(obj).forEach(key => {