专业添加导入模板下载和数据上传
This commit is contained in:
@@ -4,6 +4,7 @@ import com.roomroot.jwgl.dto.courserunning.ClassesImportDTO;
|
||||
import com.roomroot.jwgl.dto.courserunning.SemesterImportDTO;
|
||||
import com.roomroot.jwgl.dto.courserunning.SingleCourseImportDTO;
|
||||
import com.roomroot.jwgl.dto.departmentpersonnel.DepartmentPersonnelCreateDTO;
|
||||
import com.roomroot.jwgl.dto.discipline.MajorImportRowDTO;
|
||||
import com.roomroot.jwgl.entity.JXSSJH;
|
||||
import com.roomroot.jwgl.entity.JYB;
|
||||
import com.roomroot.jwgl.entity.JYSB;
|
||||
@@ -25,7 +26,9 @@ import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ExcelParseUtil {
|
||||
|
||||
@@ -479,6 +482,174 @@ public class ExcelParseUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 专业管理导入模板的列头定义。
|
||||
* <p>下载模板与解析共用同一份列头,避免模板与解析器不一致。解析按列名匹配,列顺序可调整。</p>
|
||||
*/
|
||||
public static final String[] MAJOR_IMPORT_HEADERS = {
|
||||
"专业代号", "专业名称", "专业方向", "专业代码", "专业版本", "学年制", "学期数",
|
||||
"培训层次", "培训类型", "培训类型2", "学员类别", "主干专业", "自定义分类",
|
||||
"节次类别", "系统模式", "学科专业信息标识号", "教学管理机构编号", "专业标识号",
|
||||
"简称", "规范名称", "培养目标", "专业备注", "专业规范"
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析专业管理导入 Excel 文件(支持 .xls / .xlsx)。
|
||||
* <p>
|
||||
* 表头按 {@link #MAJOR_IMPORT_HEADERS} 的列名匹配(顺序不限,可少列、可多列);
|
||||
* 未识别的列忽略;空白单元格统一归一为 null,由业务层兜底或使用库缺省值。
|
||||
* 专业代号与专业名称均为空的行视为空行跳过。
|
||||
* </p>
|
||||
*
|
||||
* @param inputStream Excel 文件输入流
|
||||
* @param fileName 文件名(用于判断 .xls/.xlsx)
|
||||
* @return 导入行列表(含 Excel 原始行号)
|
||||
* @throws Exception 解析异常
|
||||
*/
|
||||
public static List<MajorImportRowDTO> parseMajorExcel(InputStream inputStream, String fileName) throws Exception {
|
||||
List<MajorImportRowDTO> result = new ArrayList<>();
|
||||
Workbook workbook = createWorkbook(inputStream, fileName);
|
||||
try {
|
||||
Sheet sheet = workbook.getSheetAt(0);
|
||||
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 : MAJOR_IMPORT_HEADERS) {
|
||||
if (headerIndex.containsKey(header)) {
|
||||
matched++;
|
||||
}
|
||||
}
|
||||
if (matched == 0) {
|
||||
throw new IllegalArgumentException("未识别到专业导入模板表头,请先下载「专业导入模板」并按表头填写");
|
||||
}
|
||||
|
||||
for (int rowNum = firstRowNum + 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
|
||||
Row row = sheet.getRow(rowNum);
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ZYB entity = new ZYB();
|
||||
entity.setZydh(cellText(row, headerIndex, "专业代号"));
|
||||
entity.setZymc(cellText(row, headerIndex, "专业名称"));
|
||||
entity.setZyfx(cellText(row, headerIndex, "专业方向"));
|
||||
entity.setZydm(cellText(row, headerIndex, "专业代码"));
|
||||
entity.setZybb(cellText(row, headerIndex, "专业版本"));
|
||||
entity.setXnz(cellText(row, headerIndex, "学年制"));
|
||||
entity.setXqs(cellInt(row, headerIndex, "学期数"));
|
||||
entity.setPxcc(cellText(row, headerIndex, "培训层次"));
|
||||
entity.setPxlx(cellText(row, headerIndex, "培训类型"));
|
||||
entity.setPxlx2(cellText(row, headerIndex, "培训类型2"));
|
||||
entity.setXylb(cellText(row, headerIndex, "学员类别"));
|
||||
entity.setZgzy(cellBool(row, headerIndex, "主干专业"));
|
||||
entity.setZdyfl(cellText(row, headerIndex, "自定义分类"));
|
||||
entity.setJclb(cellText(row, headerIndex, "节次类别"));
|
||||
entity.setXtms(cellText(row, headerIndex, "系统模式"));
|
||||
entity.setXkzyxxbsh(cellText(row, headerIndex, "学科专业信息标识号"));
|
||||
entity.setJxgljgbh(cellText(row, headerIndex, "教学管理机构编号"));
|
||||
entity.setZybsh(cellText(row, headerIndex, "专业标识号"));
|
||||
entity.setJc(cellText(row, headerIndex, "简称"));
|
||||
entity.setGfmc(cellText(row, headerIndex, "规范名称"));
|
||||
entity.setPymb(cellText(row, headerIndex, "培养目标"));
|
||||
entity.setZybz(cellText(row, headerIndex, "专业备注"));
|
||||
entity.setZygf(cellText(row, headerIndex, "专业规范"));
|
||||
|
||||
// 专业代号与专业名称均为空视为空行
|
||||
if (isNotBlank(entity.getZydh()) || isNotBlank(entity.getZymc())) {
|
||||
result.add(new MajorImportRowDTO(rowNum + 1, entity));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
workbook.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表头名归一:去星号标记、括号统一为半角、去空白字符,便于容错匹配。
|
||||
*/
|
||||
private static String normalizeHeader(String title) {
|
||||
if (title == null) {
|
||||
return "";
|
||||
}
|
||||
return title.replace("*", "")
|
||||
.replace("(", "(")
|
||||
.replace(")", ")")
|
||||
.replace(" ", "")
|
||||
.replace("\u00A0", "")
|
||||
.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 按列名取单元格文本,空白统一归一为 null。
|
||||
*/
|
||||
private static String cellText(Row row, Map<String, Integer> headerIndex, String header) {
|
||||
Integer col = headerIndex.get(header);
|
||||
if (col == null) {
|
||||
return null;
|
||||
}
|
||||
String value = getCellStringValue(row.getCell(col));
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按列名取整数值,容错 8 / 8.0 / "8" 等写法。
|
||||
*/
|
||||
private static Integer cellInt(Row row, Map<String, Integer> headerIndex, String header) {
|
||||
String value = cellText(row, headerIndex, header);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.valueOf(value);
|
||||
} catch (NumberFormatException e) {
|
||||
try {
|
||||
return (int) Double.parseDouble(value);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按列名取布尔值,识别 是/否、true/false、1/0、Y/N、√。
|
||||
*/
|
||||
private static Boolean cellBool(Row row, Map<String, Integer> headerIndex, String header) {
|
||||
String value = cellText(row, headerIndex, header);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if ("是".equals(value) || "true".equalsIgnoreCase(value) || "1".equals(value)
|
||||
|| "Y".equalsIgnoreCase(value) || "√".equals(value)) {
|
||||
return true;
|
||||
}
|
||||
if ("否".equals(value) || "false".equalsIgnoreCase(value) || "0".equals(value)
|
||||
|| "N".equalsIgnoreCase(value)) {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isNotBlank(String value) {
|
||||
return value != null && !value.trim().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析专业教学计划表导入 Excel 文件(支持 .xls / .xlsx)。
|
||||
* <p>表头顺序:</p>
|
||||
|
||||
Reference in New Issue
Block a user