宿舍卫生检查系统采用主子表分离设计:
xg_dorm_hygiene_check - 宿舍整体检查信息xg_dorm_hygiene_student_score - 学生个人评分信息检查编号 | 检查日期 | 宿舍楼 | 房间号 | 宿舍总分 | 卫生等级 | 学生1学号 | 学生1姓名 | 学生1得分 | 学生1评级 | 学生2学号 | 学生2姓名 | 学生2得分 | 学生2评级 | ...
/**
* 导出宿舍卫生检查数据(平铺格式)
*/
@SaCheckPermission("xg:hygiene:export")
@PostMapping("/export")
public void export(HttpServletResponse response, XgDormHygieneCheckBo bo) {
List<XgDormHygieneCheckFlatVo> list = hygieneCheckService.queryFlatList(bo);
ExcelUtil.exportExcel(list, "宿舍卫生检查数据", XgDormHygieneCheckFlatVo.class, response);
}
/**
* 导入宿舍卫生检查数据(平铺格式)
*/
@SaCheckPermission("xg:hygiene:import")
@PostMapping("/importData")
public R<Void> importData(MultipartFile file) throws Exception {
ExcelResult<XgDormHygieneCheckFlatVo> result = ExcelUtil.importExcel(file.getInputStream(), XgDormHygieneCheckFlatVo.class);
List<XgDormHygieneCheckFlatVo> flatList = result.getList();
String message = hygieneCheckService.importFlatData(flatList);
return R.ok(message);
}
@Data
@ExcelIgnoreUnannotated
public class XgDormHygieneCheckFlatVo {
// ========== 宿舍检查基本信息 ==========
@ExcelProperty(value = "检查编号")
private String checkNo;
@ExcelProperty(value = "检查日期")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date checkDate;
@ExcelProperty(value = "宿舍楼")
private String buildingName;
@ExcelProperty(value = "房间号")
private String roomCode;
@ExcelProperty(value = "宿舍总分")
private BigDecimal totalScore;
@ExcelProperty(value = "卫生等级")
private String hygieneGrade;
// ========== 学生1信息 ==========
@ExcelProperty(value = "学生1学号")
private String student1No;
@ExcelProperty(value = "学生1姓名")
private String student1Name;
@ExcelProperty(value = "学生1得分")
private BigDecimal student1Score;
@ExcelProperty(value = "学生1评级")
private String student1Grade;
// ========== 学生2信息 ==========
@ExcelProperty(value = "学生2学号")
private String student2No;
@ExcelProperty(value = "学生2姓名")
private String student2Name;
@ExcelProperty(value = "学生2得分")
private BigDecimal student2Score;
@ExcelProperty(value = "学生2评级")
private String student2Grade;
// ========== 学生3信息 ==========
@ExcelProperty(value = "学生3学号")
private String student3No;
@ExcelProperty(value = "学生3姓名")
private String student3Name;
@ExcelProperty(value = "学生3得分")
private BigDecimal student3Score;
@ExcelProperty(value = "学生3评级")
private String student3Grade;
// ========== 学生4信息 ==========
@ExcelProperty(value = "学生4学号")
private String student4No;
@ExcelProperty(value = "学生4姓名")
private String student4Name;
@ExcelProperty(value = "学生4得分")
private BigDecimal student4Score;
@ExcelProperty(value = "学生4评级")
private String student4Grade;
// ========== 备注信息 ==========
@ExcelProperty(value = "检查总结")
private String checkSummary;
@ExcelProperty(value = "整改建议")
private String improvementSuggestions;
// ========== 基础字段 ==========
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
private Long createBy;
private Long updateBy;
}
/**
* 查询平铺格式的卫生检查列表
*/
public List<XgDormHygieneCheckFlatVo> queryFlatList(XgDormHygieneCheckBo bo) {
// 1. 查询主表数据
List<XgDormHygieneCheck> checkList = baseMapper.selectList(buildQueryWrapper(bo));
List<XgDormHygieneCheckFlatVo> flatList = new ArrayList<>();
for (XgDormHygieneCheck check : checkList) {
// 2. 查询对应的学生评分数据
List<XgDormHygieneStudentScore> studentScores = studentScoreMapper.selectList(
new LambdaQueryWrapper<XgDormHygieneStudentScore>()
.eq(XgDormHygieneStudentScore::getHygieneCheckId, check.getId())
.eq(XgDormHygieneStudentScore::getDeleted, 0)
.orderByAsc(XgDormHygieneStudentScore::getStudentNo)
);
// 3. 数据转换为平铺格式
XgDormHygieneCheckFlatVo flatVo = new XgDormHygieneCheckFlatVo();
// 填充主表信息
BeanUtils.copyProperties(check, flatVo);
// 填充学生信息(最多支持4个学生)
for (int i = 0; i < Math.min(studentScores.size(), 4); i++) {
XgDormHygieneStudentScore student = studentScores.get(i);
switch (i) {
case 0:
flatVo.setStudent1No(student.getStudentNo());
flatVo.setStudent1Name(student.getStudentName());
flatVo.setStudent1Score(student.getStudentScore());
flatVo.setStudent1Grade(student.getStudentGrade());
break;
case 1:
flatVo.setStudent2No(student.getStudentNo());
flatVo.setStudent2Name(student.getStudentName());
flatVo.setStudent2Score(student.getStudentScore());
flatVo.setStudent2Grade(student.getStudentGrade());
break;
case 2:
flatVo.setStudent3No(student.getStudentNo());
flatVo.setStudent3Name(student.getStudentName());
flatVo.setStudent3Score(student.getStudentScore());
flatVo.setStudent3Grade(student.getStudentGrade());
break;
case 3:
flatVo.setStudent4No(student.getStudentNo());
flatVo.setStudent4Name(student.getStudentName());
flatVo.setStudent4Score(student.getStudentScore());
flatVo.setStudent4Grade(student.getStudentGrade());
break;
}
}
flatList.add(flatVo);
}
return flatList;
}
/**
* 导入平铺格式数据
*/
@Transactional(rollbackFor = Exception.class)
public String importFlatData(List<XgDormHygieneCheckFlatVo> flatList) {
if (CollUtil.isEmpty(flatList)) {
throw new ServiceException("导入数据不能为空");
}
int successCount = 0;
int failureCount = 0;
StringBuilder failureMsg = new StringBuilder();
for (XgDormHygieneCheckFlatVo flatVo : flatList) {
try {
// 1. 转换并保存主表数据
XgDormHygieneCheck check = convertToCheck(flatVo);
baseMapper.insert(check);
// 2. 转换并保存学生评分数据
List<XgDormHygieneStudentScore> studentScores = convertToStudentScores(flatVo, check.getId());
for (XgDormHygieneStudentScore score : studentScores) {
studentScoreMapper.insert(score);
}
successCount++;
} catch (Exception e) {
failureCount++;
String msg = "<br/>" + failureCount + "、检查编号 " + flatVo.getCheckNo() + " 导入失败:" + e.getMessage();
failureMsg.append(msg);
}
}
if (failureCount > 0) {
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureCount + " 条数据格式不正确,错误如下:");
throw new ServiceException(failureMsg.toString());
} else {
return "恭喜您,数据已全部导入成功!共 " + successCount + " 条";
}
}
/**
* 导出多Sheet格式数据
*/
@SaCheckPermission("xg:hygiene:exportMultiSheet")
@PostMapping("/exportMultiSheet")
public void exportMultiSheet(HttpServletResponse response, XgDormHygieneCheckBo bo) {
// 1. 查询主表数据
List<XgDormHygieneCheckVo> checkList = hygieneCheckService.queryList(bo);
// 2. 查询子表数据
List<XgDormHygieneStudentScoreVo> studentScoreList = hygieneCheckService.queryStudentScoreList(bo);
// 3. 多Sheet导出
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
.autoCloseStream(Boolean.FALSE)
.build();
try {
// Sheet1: 宿舍检查数据
WriteSheet writeSheet1 = EasyExcel.writerSheet(0, "宿舍检查数据")
.head(XgDormHygieneCheckVo.class)
.build();
excelWriter.write(checkList, writeSheet1);
// Sheet2: 学生评分数据
WriteSheet writeSheet2 = EasyExcel.writerSheet(1, "学生评分数据")
.head(XgDormHygieneStudentScoreVo.class)
.build();
excelWriter.write(studentScoreList, writeSheet2);
} finally {
excelWriter.finish();
}
}
检查编号 | 检查日期 | 宿舍楼 | 房间号 | 宿舍总分 | 卫生等级 | 学号 | 姓名 | 个人得分 | 个人评级
WSCC001 | 2025-01-28 | A栋 | 101 | 85.5 | B | 2023001 | 张三 | 88 | B
WSCC001 | 2025-01-28 | A栋 | 101 | 85.5 | B | 2023002 | 李四 | 83 | C
// 导入时校验数据完整性
private void validateImportData(XgDormHygieneCheckFlatVo flatVo) {
// 必填字段校验
if (StringUtils.isBlank(flatVo.getCheckNo())) {
throw new ServiceException("检查编号不能为空");
}
// 宿舍信息校验
if (StringUtils.isBlank(flatVo.getRoomCode())) {
throw new ServiceException("房间号不能为空");
}
// 至少需要一个学生信息
if (StringUtils.isAllBlank(flatVo.getStudent1No(), flatVo.getStudent2No(),
flatVo.getStudent3No(), flatVo.getStudent4No())) {
throw new ServiceException("至少需要填写一个学生信息");
}
}
/**
* 下载导入模板
*/
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) {
// 生成模板数据
List<XgDormHygieneCheckFlatVo> templateList = generateTemplateData();
ExcelUtil.exportExcel(templateList, "宿舍卫生检查导入模板", XgDormHygieneCheckFlatVo.class, response);
}
// 使用批量插入优化性能
@Transactional(rollbackFor = Exception.class)
public void batchInsertStudentScores(List<XgDormHygieneStudentScore> studentScores) {
if (CollUtil.isNotEmpty(studentScores)) {
// 分批插入,每批500条
List<List<XgDormHygieneStudentScore>> partition = CollUtil.split(studentScores, 500);
for (List<XgDormHygieneStudentScore> batch : partition) {
studentScoreMapper.insertBatch(batch);
}
}
}
| 使用场景 | 推荐方案 | 理由 |
|---|---|---|
| 日常数据录入 | 平铺式 | 操作简单,一个Excel搞定 |
| 数据分析报表 | 嵌套式 | 便于透视表分析 |
| 系统间数据交换 | 多Sheet式 | 结构清晰,数据完整 |
| 打印存档 | 平铺式 | 格式紧凑,便于查看 |
# application.yml
ruoyi:
excel:
# 导出最大行数
max-rows: 10000
# 导入最大行数
import-max-rows: 5000
-- 菜单权限配置
INSERT INTO sys_menu VALUES (NULL, '宿舍卫生检查导出', 导出, 'xg:hygiene:export', '', '', 1, 0, 'C', '0', '0', '', 'export', 1, NOW(), NULL, NULL, '');
INSERT INTO sys_menu VALUES (NULL, '宿舍卫生检查导入', 导入, 'xg:hygiene:import', '', '', 1, 0, 'C', '0', '0', '', 'upload', 1, NOW(), NULL, NULL, '');