forked from liweijie/education
人才培养方案
This commit is contained in:
+10
-2
@@ -121,10 +121,18 @@ public class TrainingProgramController {
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 培养方案目录导入模板(仅表头,含学年制、学期数等必填列)。
|
||||
*/
|
||||
@GetMapping("/template")
|
||||
public void downloadTemplate(HttpServletResponse response) throws Exception {
|
||||
trainingProgramService.exportZYBTemplate(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 培养方案(专业)数据文件导入(.xls/.xlsx)。
|
||||
* <p>表头顺序:专业代号, 专业名称, 专业代码, 专业版本, 培训类型, 培训层次, 学年制,
|
||||
* 学期数, 专业方向, 专业备注, 学员类别, 简称, 培训类型2。专业代号不填自动生成。</p>
|
||||
* <p>按列名匹配:专业代号, 专业名称, 专业代码, 专业版本, 培训类型, 培训层次, 学年制,
|
||||
* 学期数, 专业方向, 专业备注, 学员类别, 简称, 培训类型2。专业代号不填自动生成;学年制、学期数必填。</p>
|
||||
*/
|
||||
@PostMapping("/import")
|
||||
public Result<Integer> importData(@RequestParam("file") MultipartFile file) throws Exception {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.roomroot.jwgl.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
@@ -33,12 +34,12 @@ public class ZYB {
|
||||
@TableField("专业方向")
|
||||
private String zyfx;
|
||||
|
||||
/** 学年制 */
|
||||
@TableField("学年制")
|
||||
/** 学年制(达梦非空;空串会被当成 NULL,插入时必须有值) */
|
||||
@TableField(value = "学年制", fill = FieldFill.INSERT, insertStrategy = FieldStrategy.ALWAYS)
|
||||
private String xnz;
|
||||
|
||||
/** 学期数 */
|
||||
@TableField("学期数")
|
||||
@TableField(value = "学期数", fill = FieldFill.INSERT, insertStrategy = FieldStrategy.ALWAYS)
|
||||
private Integer xqs;
|
||||
|
||||
/** 专业备注 */
|
||||
@@ -109,26 +110,26 @@ public class ZYB {
|
||||
@TableField("简称")
|
||||
private String jc;
|
||||
|
||||
/** 节次类别 */
|
||||
@TableField("节次类别")
|
||||
private String jclb;
|
||||
|
||||
/** 系统模式 */
|
||||
@TableField("系统模式")
|
||||
private String xtms;
|
||||
|
||||
/** JSONZD NVARCHAR(4000) */
|
||||
@TableField("JSONZD")
|
||||
private String jsonzd;
|
||||
|
||||
/** 主干专业 */
|
||||
@TableField("主干专业")
|
||||
@TableField(value = "主干专业", fill = FieldFill.INSERT, insertStrategy = FieldStrategy.ALWAYS)
|
||||
private Boolean zgzy;
|
||||
|
||||
/** 规范名称 */
|
||||
@TableField("规范名称")
|
||||
/** 规范名称(达梦非空,缺省用专业名称) */
|
||||
@TableField(value = "规范名称", fill = FieldFill.INSERT, insertStrategy = FieldStrategy.ALWAYS)
|
||||
private String gfmc;
|
||||
|
||||
/** 节次类别 */
|
||||
@TableField(value = "节次类别", fill = FieldFill.INSERT, insertStrategy = FieldStrategy.ALWAYS)
|
||||
private String jclb;
|
||||
|
||||
/** 系统模式 */
|
||||
@TableField(value = "系统模式", fill = FieldFill.INSERT, insertStrategy = FieldStrategy.ALWAYS)
|
||||
private String xtms;
|
||||
|
||||
/** 教学大纲编号 */
|
||||
@TableField("教学大纲编号")
|
||||
private String jxdgbh;
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package com.roomroot.jwgl.handler;
|
||||
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.roomroot.jwgl.entity.ZYB;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 专业表插入时补齐达梦非空列。达梦将空串视为 NULL,缺省值必须是非空文本或数字。
|
||||
*/
|
||||
@Component
|
||||
public class ZybInsertFillHandler implements MetaObjectHandler {
|
||||
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
Object origin = metaObject.getOriginalObject();
|
||||
if (!(origin instanceof ZYB)) {
|
||||
return;
|
||||
}
|
||||
ZYB entity = (ZYB) origin;
|
||||
fillNotNullColumns(entity);
|
||||
strictInsertFill(metaObject, "xnz", String.class, entity.getXnz());
|
||||
strictInsertFill(metaObject, "xqs", Integer.class, entity.getXqs());
|
||||
strictInsertFill(metaObject, "gfmc", String.class, entity.getGfmc());
|
||||
strictInsertFill(metaObject, "jclb", String.class, entity.getJclb());
|
||||
strictInsertFill(metaObject, "xtms", String.class, entity.getXtms());
|
||||
strictInsertFill(metaObject, "zgzy", Boolean.class, entity.getZgzy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
// 仅插入补齐
|
||||
}
|
||||
|
||||
/** @deprecated 使用 {@link #fillNotNullColumns(ZYB)} */
|
||||
public static void fillAcademicYears(ZYB entity) {
|
||||
fillNotNullColumns(entity);
|
||||
}
|
||||
|
||||
public static void fillNotNullColumns(ZYB entity) {
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
String name = blankToNull(entity.getZymc());
|
||||
String xnz = blankToNull(entity.getXnz());
|
||||
if (xnz == null) {
|
||||
if (entity.getXqs() != null && entity.getXqs() > 0) {
|
||||
xnz = String.valueOf(Math.max(1, (entity.getXqs() + 1) / 2));
|
||||
} else {
|
||||
xnz = "4";
|
||||
}
|
||||
}
|
||||
entity.setXnz(xnz);
|
||||
if (entity.getXqs() == null) {
|
||||
int years = 4;
|
||||
try {
|
||||
years = (int) Math.round(Double.parseDouble(xnz));
|
||||
} catch (NumberFormatException ignored) {
|
||||
years = 4;
|
||||
}
|
||||
entity.setXqs(Math.max(1, years) * 2);
|
||||
}
|
||||
if (blankToNull(entity.getGfmc()) == null) {
|
||||
entity.setGfmc(name != null ? name : "-");
|
||||
}
|
||||
if (blankToNull(entity.getZybsh()) == null && blankToNull(entity.getZydh()) != null) {
|
||||
entity.setZybsh(entity.getZydh());
|
||||
}
|
||||
if (blankToNull(entity.getJc()) == null) {
|
||||
entity.setJc(name != null ? name : "-");
|
||||
}
|
||||
if (blankToNull(entity.getZyfx()) == null) {
|
||||
entity.setZyfx("-");
|
||||
}
|
||||
if (blankToNull(entity.getZybb()) == null) {
|
||||
entity.setZybb("-");
|
||||
}
|
||||
if (blankToNull(entity.getPxlx2()) == null) {
|
||||
entity.setPxlx2("其他");
|
||||
}
|
||||
if (blankToNull(entity.getXylb()) == null) {
|
||||
entity.setXylb("其他");
|
||||
}
|
||||
if (blankToNull(entity.getJclb()) == null) {
|
||||
entity.setJclb("双节次");
|
||||
}
|
||||
if (blankToNull(entity.getXtms()) == null) {
|
||||
entity.setXtms("军校模式");
|
||||
}
|
||||
if (blankToNull(entity.getZdyfl()) == null) {
|
||||
entity.setZdyfl("-");
|
||||
}
|
||||
if (blankToNull(entity.getZygf()) == null) {
|
||||
entity.setZygf("-");
|
||||
}
|
||||
if (entity.getZgzy() == null) {
|
||||
entity.setZgzy(false);
|
||||
}
|
||||
if (entity.getTy() == null) {
|
||||
entity.setTy(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static String blankToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
}
|
||||
+3
@@ -63,6 +63,9 @@ public interface TrainingProgramService {
|
||||
/** 导出培养方案(专业)到 Excel(.xlsx) */
|
||||
void exportZYBExcel(jakarta.servlet.http.HttpServletResponse response) throws Exception;
|
||||
|
||||
/** 下载培养方案目录导入模板(仅表头) */
|
||||
void exportZYBTemplate(jakarta.servlet.http.HttpServletResponse response) throws Exception;
|
||||
|
||||
/**
|
||||
* 查询未停用的教学管理机构下拉选项。
|
||||
*/
|
||||
|
||||
+2
@@ -104,6 +104,7 @@ public class DisciplineServiceImpl implements DisciplineService {
|
||||
}
|
||||
assertMajorCodeUnique(entity.getZydh());
|
||||
fillMajorDefaults(entity);
|
||||
com.roomroot.jwgl.handler.ZybInsertFillHandler.fillNotNullColumns(entity);
|
||||
zybMapper.insert(entity);
|
||||
}
|
||||
|
||||
@@ -284,6 +285,7 @@ public class DisciplineServiceImpl implements DisciplineService {
|
||||
if (StringUtils.isEmpty(entity.getGfmc())) {
|
||||
entity.setGfmc(entity.getZymc());
|
||||
}
|
||||
com.roomroot.jwgl.handler.ZybInsertFillHandler.fillNotNullColumns(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+84
-7
@@ -44,6 +44,10 @@ public class TrainingProgramServiceImpl implements TrainingProgramService {
|
||||
}
|
||||
entity.setTy(false);
|
||||
entity.setQysj(LocalDateTime.now());
|
||||
com.roomroot.jwgl.handler.ZybInsertFillHandler.fillNotNullColumns(entity);
|
||||
if (findExistingMajor(entity) != null) {
|
||||
throw new RuntimeException("专业名称已存在,请勿重复新增");
|
||||
}
|
||||
zybMapper.insert(entity);
|
||||
}
|
||||
|
||||
@@ -90,24 +94,84 @@ public class TrainingProgramServiceImpl implements TrainingProgramService {
|
||||
if (list.isEmpty()) {
|
||||
throw new RuntimeException("Excel中无有效数据");
|
||||
}
|
||||
int inserted = 0;
|
||||
int updated = 0;
|
||||
int rowNo = 1;
|
||||
for (ZYB entity : list) {
|
||||
if (entity.getZydh() == null || entity.getZydh().isEmpty()) {
|
||||
entity.setZydh(com.roomroot.jwgl.utils.UuidUtil.getUUID());
|
||||
rowNo++;
|
||||
if (entity.getZymc() == null || entity.getZymc().trim().isEmpty()) {
|
||||
throw new RuntimeException("第" + rowNo + "行:专业名称不能为空");
|
||||
}
|
||||
if (entity.getTy() == null) {
|
||||
entity.setZymc(entity.getZymc().trim());
|
||||
if (entity.getZydh() != null) {
|
||||
entity.setZydh(entity.getZydh().trim());
|
||||
if (entity.getZydh().isEmpty()) {
|
||||
entity.setZydh(null);
|
||||
}
|
||||
}
|
||||
com.roomroot.jwgl.handler.ZybInsertFillHandler.fillNotNullColumns(entity);
|
||||
|
||||
ZYB existing = findExistingMajor(entity);
|
||||
if (existing != null) {
|
||||
entity.setZydh(existing.getZydh());
|
||||
if (entity.getZybsh() == null || entity.getZybsh().isEmpty()) {
|
||||
entity.setZybsh(existing.getZybsh() != null ? existing.getZybsh() : existing.getZydh());
|
||||
}
|
||||
entity.setTy(false);
|
||||
zybMapper.updateById(entity);
|
||||
updated++;
|
||||
} else {
|
||||
if (entity.getZydh() == null || entity.getZydh().isEmpty()) {
|
||||
entity.setZydh(com.roomroot.jwgl.utils.UuidUtil.getUUID());
|
||||
}
|
||||
if (entity.getZybsh() == null || entity.getZybsh().isEmpty()) {
|
||||
entity.setZybsh(entity.getZydh());
|
||||
}
|
||||
entity.setTy(false);
|
||||
entity.setQysj(LocalDateTime.now());
|
||||
zybMapper.insert(entity);
|
||||
inserted++;
|
||||
}
|
||||
zybMapper.insert(entity);
|
||||
}
|
||||
return list.size();
|
||||
return inserted + updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按主键或专业名称命中已有记录,避免触发 PK_专业名称表。
|
||||
*/
|
||||
private ZYB findExistingMajor(ZYB entity) {
|
||||
if (entity.getZydh() != null && !entity.getZydh().isEmpty()) {
|
||||
ZYB byId = zybMapper.selectById(entity.getZydh());
|
||||
if (byId != null) {
|
||||
return byId;
|
||||
}
|
||||
}
|
||||
java.util.List<ZYB> sameName = zybMapper.selectList(new LambdaQueryWrapper<ZYB>()
|
||||
.eq(ZYB::getZymc, entity.getZymc()));
|
||||
if (sameName == null || sameName.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (ZYB row : sameName) {
|
||||
boolean fxSame = java.util.Objects.equals(
|
||||
nullToEmpty(row.getZyfx()), nullToEmpty(entity.getZyfx()));
|
||||
boolean lxSame = java.util.Objects.equals(
|
||||
nullToEmpty(row.getPxlx()), nullToEmpty(entity.getPxlx()));
|
||||
if (fxSame && lxSame) {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
return sameName.get(0);
|
||||
}
|
||||
|
||||
private static String nullToEmpty(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportZYBExcel(jakarta.servlet.http.HttpServletResponse response) throws Exception {
|
||||
java.util.List<ZYB> list = zybMapper.selectList(
|
||||
new LambdaQueryWrapper<ZYB>().orderByAsc(ZYB::getZydh));
|
||||
String[] headers = {"专业代号", "专业名称", "专业代码", "专业版本", "培训类型", "培训层次",
|
||||
"学年制", "学期数", "专业方向", "专业备注", "学员类别", "简称", "培训类型2"};
|
||||
String[] headers = com.roomroot.jwgl.utils.ExcelParseUtil.TRAINING_PROGRAM_IMPORT_HEADERS;
|
||||
byte[] bytes = com.roomroot.jwgl.utils.ExcelExportUtil.export("培养方案", headers, list,
|
||||
ZYB::getZydh, ZYB::getZymc, ZYB::getZydm, ZYB::getZybb, ZYB::getPxlx,
|
||||
ZYB::getPxcc, ZYB::getXnz, ZYB::getXqs, ZYB::getZyfx, ZYB::getZybz,
|
||||
@@ -120,6 +184,19 @@ public class TrainingProgramServiceImpl implements TrainingProgramService {
|
||||
response.getOutputStream().flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportZYBTemplate(jakarta.servlet.http.HttpServletResponse response) throws Exception {
|
||||
String[] headers = com.roomroot.jwgl.utils.ExcelParseUtil.TRAINING_PROGRAM_IMPORT_HEADERS;
|
||||
byte[] bytes = com.roomroot.jwgl.utils.ExcelExportUtil.export("培养方案", headers,
|
||||
java.util.Collections.emptyList());
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setHeader("Content-Disposition",
|
||||
"attachment; filename=" + java.net.URLEncoder.encode(
|
||||
"人才培养方案目录模板.xlsx", java.nio.charset.StandardCharsets.UTF_8));
|
||||
response.getOutputStream().write(bytes);
|
||||
response.getOutputStream().flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.List<JXGLJG> listTeachingOrganizations() {
|
||||
java.util.List<JXGLJG> list = jxgljgMapper.selectEnabledOptions();
|
||||
|
||||
@@ -450,11 +450,17 @@ public class ExcelParseUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 人才培养方案(专业表)导入模板列头。下载模板与解析共用,按列名匹配,顺序可调整。
|
||||
*/
|
||||
public static final String[] TRAINING_PROGRAM_IMPORT_HEADERS = {
|
||||
"专业代号", "专业名称", "专业代码", "专业版本", "培训类型", "培训层次",
|
||||
"学年制", "学期数", "专业方向", "专业备注", "学员类别", "简称", "培训类型2"
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析培养方案(专业表)导入 Excel 文件(支持 .xls / .xlsx)。
|
||||
* <p>表头顺序:</p>
|
||||
* 专业代号, 专业名称, 专业代码, 专业版本, 培训类型, 培训层次, 学年制, 学期数,
|
||||
* 专业方向, 专业备注, 学员类别, 简称, 培训类型2
|
||||
* <p>按 {@link #TRAINING_PROGRAM_IMPORT_HEADERS} 列名匹配;未识别表头则拒绝导入。</p>
|
||||
*
|
||||
* @return 专业实体列表
|
||||
* @throws Exception 解析异常
|
||||
@@ -464,29 +470,53 @@ public class ExcelParseUtil {
|
||||
Workbook workbook = createWorkbook(inputStream, fileName);
|
||||
try {
|
||||
Sheet sheet = workbook.getSheetAt(0);
|
||||
int lastRowNum = sheet.getLastRowNum();
|
||||
for (int rowNum = 1; rowNum <= lastRowNum; rowNum++) {
|
||||
int firstRowNum = sheet.getFirstRowNum();
|
||||
Row headerRow = sheet.getRow(firstRowNum);
|
||||
if (headerRow == null) {
|
||||
throw new IllegalArgumentException("导入文件缺少表头行,请先下载「人才培养方案目录模板」填写");
|
||||
}
|
||||
|
||||
Map<String, Integer> headerIndex = new LinkedHashMap<>();
|
||||
for (int col = headerRow.getFirstCellNum(); col < headerRow.getLastCellNum(); col++) {
|
||||
String title = normalizeHeader(getCellStringValue(headerRow.getCell(col)));
|
||||
if (!title.isEmpty() && !headerIndex.containsKey(title)) {
|
||||
headerIndex.put(title, col);
|
||||
}
|
||||
}
|
||||
int matched = 0;
|
||||
for (String header : TRAINING_PROGRAM_IMPORT_HEADERS) {
|
||||
if (headerIndex.containsKey(header)) {
|
||||
matched++;
|
||||
}
|
||||
}
|
||||
if (matched == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"未识别到人才培养方案目录模板表头,请下载「人才培养方案目录模板」并按表头填写(须含学年制、学期数等列)");
|
||||
}
|
||||
if (!headerIndex.containsKey("学年制") && headerIndex.containsKey("学制")) {
|
||||
headerIndex.put("学年制", headerIndex.get("学制"));
|
||||
}
|
||||
|
||||
for (int rowNum = firstRowNum + 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
|
||||
Row row = sheet.getRow(rowNum);
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
ZYB entity = new ZYB();
|
||||
entity.setZydh(getCellStringValue(row.getCell(0)));
|
||||
entity.setZymc(getCellStringValue(row.getCell(1)));
|
||||
entity.setZydm(getCellStringValue(row.getCell(2)));
|
||||
entity.setZybb(getCellStringValue(row.getCell(3)));
|
||||
entity.setPxlx(getCellStringValue(row.getCell(4)));
|
||||
entity.setPxcc(getCellStringValue(row.getCell(5)));
|
||||
entity.setXnz(getCellStringValue(row.getCell(6)));
|
||||
entity.setXqs(parseIntCell(row.getCell(7)));
|
||||
entity.setZyfx(getCellStringValue(row.getCell(8)));
|
||||
entity.setZybz(getCellStringValue(row.getCell(9)));
|
||||
entity.setXylb(getCellStringValue(row.getCell(10)));
|
||||
entity.setJc(getCellStringValue(row.getCell(11)));
|
||||
entity.setPxlx2(getCellStringValue(row.getCell(12)));
|
||||
// 至少要有专业代号或名称才视为有效行
|
||||
if (entity.getZydh() != null && !entity.getZydh().isEmpty()
|
||||
|| entity.getZymc() != null && !entity.getZymc().isEmpty()) {
|
||||
entity.setZydh(cellText(row, headerIndex, "专业代号"));
|
||||
entity.setZymc(cellText(row, headerIndex, "专业名称"));
|
||||
entity.setZydm(cellText(row, headerIndex, "专业代码"));
|
||||
entity.setZybb(cellText(row, headerIndex, "专业版本"));
|
||||
entity.setPxlx(cellText(row, headerIndex, "培训类型"));
|
||||
entity.setPxcc(cellText(row, headerIndex, "培训层次"));
|
||||
entity.setXnz(cellText(row, headerIndex, "学年制"));
|
||||
entity.setXqs(cellInt(row, headerIndex, "学期数"));
|
||||
entity.setZyfx(cellText(row, headerIndex, "专业方向"));
|
||||
entity.setZybz(cellText(row, headerIndex, "专业备注"));
|
||||
entity.setXylb(cellText(row, headerIndex, "学员类别"));
|
||||
entity.setJc(cellText(row, headerIndex, "简称"));
|
||||
entity.setPxlx2(cellText(row, headerIndex, "培训类型2"));
|
||||
if (isNotBlank(entity.getZydh()) || isNotBlank(entity.getZymc())) {
|
||||
result.add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user