总数据导出优化

This commit is contained in:
Qi 2025-06-17 16:14:55 +08:00
parent 944f0e54d6
commit 9e7acd05eb
7 changed files with 202 additions and 37 deletions

View File

@ -75,6 +75,30 @@ public class JeecgController<T, S extends IService<T>> {
mv.addObject(NormalExcelConstants.DATA_LIST, exportList); mv.addObject(NormalExcelConstants.DATA_LIST, exportList);
return mv; return mv;
} }
/**
* 根据传入数据列表导出 Excel
*
* @param dataList 导出的数据列表
* @param clazz 导出类的 Class
* @param title 导出标题
*/
protected ModelAndView exportXlsByList(List<T> dataList, Class<T> clazz, String title) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
mv.addObject(NormalExcelConstants.FILE_NAME, title);
mv.addObject(NormalExcelConstants.CLASS, clazz);
ExportParams exportParams = new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title);
exportParams.setImageBasePath(jeecgBaseConfig.getPath().getUpload());
mv.addObject(NormalExcelConstants.PARAMS, exportParams);
mv.addObject(NormalExcelConstants.DATA_LIST, dataList);
return mv;
}
/** /**
* 根据每页sheet数量导出多sheet * 根据每页sheet数量导出多sheet
* *

View File

@ -17,8 +17,10 @@ import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.base.controller.JeecgController; import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.modules.entity.CetGroup; import org.jeecg.modules.entity.CetGroup;
import org.jeecg.modules.entity.CetInvigilateData; import org.jeecg.modules.entity.CetInvigilateData;
import org.jeecg.modules.entity.ClassRoom;
import org.jeecg.modules.service.ICetGroupService; import org.jeecg.modules.service.ICetGroupService;
import org.jeecg.modules.service.ICetInvigilateDataService; import org.jeecg.modules.service.ICetInvigilateDataService;
import org.jeecg.modules.service.IClassRoomService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
@ -42,6 +44,8 @@ public class CetGroupController extends JeecgController<CetGroup, ICetGroupServi
private ICetGroupService cetGroupService; private ICetGroupService cetGroupService;
@Autowired @Autowired
private ICetInvigilateDataService cetInvigilateDataService; private ICetInvigilateDataService cetInvigilateDataService;
@Autowired
private IClassRoomService classRoomService;
/** /**
* @param webData * @param webData
@ -66,44 +70,63 @@ public class CetGroupController extends JeecgController<CetGroup, ICetGroupServi
@ApiOperation(value="分组数据表-分页列表查询", notes="分组数据表-分页列表查询") @ApiOperation(value="分组数据表-分页列表查询", notes="分组数据表-分页列表查询")
@GetMapping(value = "/list") @GetMapping(value = "/list")
public Result<IPage<CetGroup>> queryPageList(CetGroup cetGroup, public Result<IPage<CetGroup>> queryPageList(CetGroup cetGroup,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize, @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) { HttpServletRequest req) {
QueryWrapper<CetGroup> queryWrapper = QueryGenerator.initQueryWrapper(cetGroup, req.getParameterMap()); QueryWrapper<CetGroup> queryWrapper = QueryGenerator.initQueryWrapper(cetGroup, req.getParameterMap());
Page<CetGroup> page = new Page<>(pageNo, pageSize); Page<CetGroup> page = new Page<>(pageNo, pageSize);
IPage<CetGroup> pageList = cetGroupService.page(page, queryWrapper); IPage<CetGroup> pageList = cetGroupService.page(page, queryWrapper);
// 取分页内所有分组id类型保持一致假设是Long // 获取当前页中所有 groupId roomId
Set<String> groupIds = pageList.getRecords().stream() Set<String> groupIds = new HashSet<>();
.map(CetGroup::getId) Set<String> roomIds = new HashSet<>();
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if(!groupIds.isEmpty()){ for (CetGroup group : pageList.getRecords()) {
// 批量查询所有这些分组下的用户 if (group.getId() != null) {
groupIds.add(group.getId());
}
if (group.getRoomId() != null) {
roomIds.add(group.getRoomId());
}
}
// 查询 invigilator 用户信息
if (!groupIds.isEmpty()) {
List<CetInvigilateData> userList = cetInvigilateDataService.list( List<CetInvigilateData> userList = cetInvigilateDataService.list(
new QueryWrapper<CetInvigilateData>().in("group_id", groupIds) new QueryWrapper<CetInvigilateData>().in("group_id", groupIds)
); );
// 按groupId分组用户名称列表groupId类型保持一致
Map<String, List<String>> groupIdToUserNames = userList.stream() Map<String, List<String>> groupIdToUserNames = userList.stream()
.collect(Collectors.groupingBy(CetInvigilateData::getGroupId, .collect(Collectors.groupingBy(CetInvigilateData::getGroupId,
Collectors.mapping(CetInvigilateData::getName, Collectors.toList()))); Collectors.mapping(CetInvigilateData::getName, Collectors.toList())));
// 设置每个分组的userNames字段 // 查询 roomId -> roomName 映射
pageList.getRecords().forEach(group -> { Map<String, String> roomIdToName = new HashMap<>();
if (!roomIds.isEmpty()) {
List<ClassRoom> rooms = classRoomService.list(
new QueryWrapper<ClassRoom>().in("id", roomIds)
);
roomIdToName = rooms.stream()
.collect(Collectors.toMap(
room -> String.valueOf(room.getId()),
ClassRoom::getFullName // .getName()
));
}
// 设置每条记录的 userNames roomName
for (CetGroup group : pageList.getRecords()) {
String groupIdStr = String.valueOf(group.getId()); String groupIdStr = String.valueOf(group.getId());
List<String> names = groupIdToUserNames.get(groupIdStr); List<String> names = groupIdToUserNames.get(groupIdStr);
// 拼接为字符串例如用逗号分隔 group.setRowUser((names != null && !names.isEmpty()) ? String.join(", ", names) : "");
String userNamesStr = (names != null && !names.isEmpty()) ? String.join(", ", names) : "";
group.setRowUser(userNamesStr);
});
String roomName = roomIdToName.get(String.valueOf(group.getRoomId()));
group.setRoomName(roomName != null ? roomName : "");
}
} }
return Result.OK(pageList); return Result.OK(pageList);
} }
/** /**
* 添加 * 添加
* *
@ -147,12 +170,16 @@ public class CetGroupController extends JeecgController<CetGroup, ICetGroupServi
public Result<String> delete(@RequestParam(name="id", required=true) String id) { public Result<String> delete(@RequestParam(name="id", required=true) String id) {
// 删除分组表中的分组 // 删除分组表中的分组
cetGroupService.removeById(id); cetGroupService.removeById(id);
// 将数据总表对应groupId置空 // 将数据总表对应groupId置空
UpdateWrapper<CetInvigilateData> updateWrapper = new UpdateWrapper<>(); UpdateWrapper<CetInvigilateData> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("group_id", id).set("group_id", null); updateWrapper.eq("group_id", id).set("group_id", null);
cetInvigilateDataService.update(updateWrapper); cetInvigilateDataService.update(updateWrapper);
// 清空 ClassRoom 中的 group_id并将状态设为 0空闲
UpdateWrapper<ClassRoom> roomUpdateWrapper = new UpdateWrapper<>();
roomUpdateWrapper.eq("group_id", id)
.set("group_id", null)
.set("status", 0);
classRoomService.update(roomUpdateWrapper);
return Result.OK("删除成功!"); return Result.OK("删除成功!");
} }
@ -176,7 +203,12 @@ public class CetGroupController extends JeecgController<CetGroup, ICetGroupServi
UpdateWrapper<CetInvigilateData> updateWrapper = new UpdateWrapper<>(); UpdateWrapper<CetInvigilateData> updateWrapper = new UpdateWrapper<>();
updateWrapper.in("group_id", idList).set("group_id", null); updateWrapper.in("group_id", idList).set("group_id", null);
cetInvigilateDataService.update(updateWrapper); cetInvigilateDataService.update(updateWrapper);
// 清空 ClassRoom 中的 group_id并将状态设为 0空闲
UpdateWrapper<ClassRoom> roomUpdateWrapper = new UpdateWrapper<>();
roomUpdateWrapper.in("group_id", idList)
.set("group_id", null)
.set("status", 0);
classRoomService.update(roomUpdateWrapper);
return Result.OK("批量删除成功!"); return Result.OK("批量删除成功!");
} }

View File

@ -44,6 +44,8 @@ public class CetInvigilateDataController extends JeecgController<CetInvigilateDa
private ICetNoTeachersService cetNoTeachersService; private ICetNoTeachersService cetNoTeachersService;
@Autowired @Autowired
private ICetGroupService cetGroupService; private ICetGroupService cetGroupService;
@Autowired
private IClassRoomService classRoomService;
@ -276,7 +278,50 @@ public class CetInvigilateDataController extends JeecgController<CetInvigilateDa
@RequiresPermissions("cet:cet_invigilate_data:exportXls") @RequiresPermissions("cet:cet_invigilate_data:exportXls")
@RequestMapping(value = "/exportXls") @RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, CetInvigilateData cetInvigilateData) { public ModelAndView exportXls(HttpServletRequest request, CetInvigilateData cetInvigilateData) {
return super.exportXls(request, cetInvigilateData, CetInvigilateData.class, "数据总表"); // 1. 获取查询参数后的数据列表
List<CetInvigilateData> dataList = cetInvigilateDataService.list(
QueryGenerator.initQueryWrapper(cetInvigilateData, request.getParameterMap())
);
// 2. 获取所有涉及的 groupId
Set<String> groupIds = dataList.stream()
.map(CetInvigilateData::getGroupId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
// 3. 查询所有 groupId -> roomId 的映射
Map<String, String> groupIdToRoomId = new HashMap<>();
if (!groupIds.isEmpty()) {
List<CetGroup> groups = cetGroupService.list(
new QueryWrapper<CetGroup>().in("id", groupIds)
);
groupIdToRoomId = groups.stream()
.filter(g -> g.getRoomId() != null)
.collect(Collectors.toMap(CetGroup::getId, CetGroup::getRoomId));
}
// 4. 查询所有 roomId -> roomName 的映射
Set<String> roomIds = new HashSet<>(groupIdToRoomId.values());
Map<String, String> roomIdToName = new HashMap<>();
if (!roomIds.isEmpty()) {
List<ClassRoom> rooms = classRoomService.list(
new QueryWrapper<ClassRoom>().in("id", roomIds)
);
roomIdToName = rooms.stream()
.collect(Collectors.toMap(ClassRoom::getId, ClassRoom::getFullName));
}
// 5. 给每条数据填充 roomName 字段
for (CetInvigilateData item : dataList) {
String groupId = item.getGroupId();
String roomId = groupIdToRoomId.get(groupId);
String roomName = roomIdToName.get(roomId);
item.setRoomName(roomName != null ? roomName : "");
}
// 6. 执行导出手动指定导出数据
return super.exportXlsByList(dataList, CetInvigilateData.class, "数据总表");
} }
/** /**

View File

@ -41,6 +41,11 @@ public class CetGroup implements Serializable {
@ApiModelProperty(value = "分组名称") @ApiModelProperty(value = "分组名称")
@TableField(exist = false) @TableField(exist = false)
private String rowUser; private String rowUser;
/**分组名称*/
@Excel(name = "教室名称", width = 15)
@ApiModelProperty(value = "教室名称")
@TableField(exist = false)
private String roomName;
/**教室ID*/ /**教室ID*/
@Excel(name = "教室ID", width = 15) @Excel(name = "教室ID", width = 15)
@ApiModelProperty(value = "教室ID") @ApiModelProperty(value = "教室ID")

View File

@ -59,7 +59,7 @@ public class CetInvigilateData implements Serializable {
@ApiModelProperty(value = "学院") @ApiModelProperty(value = "学院")
private String college; private String college;
/**照片地址*/ /**照片地址*/
@Excel(name = "照片地址", width = 15) // @Excel(name = "照片地址", width = 15)
@ApiModelProperty(value = "照片地址") @ApiModelProperty(value = "照片地址")
private String photoAddress; private String photoAddress;
/**创建人*/ /**创建人*/
@ -89,9 +89,14 @@ public class CetInvigilateData implements Serializable {
// @Excel(name = "分组", width = 15) // @Excel(name = "分组", width = 15)
@ApiModelProperty(value = "分组") @ApiModelProperty(value = "分组")
private String groupId; private String groupId;
/**类型*/ /**分组*/
@Excel(name = "分组", width = 15) // @Excel(name = "分组", width = 15)
@ApiModelProperty(value = "分组") @ApiModelProperty(value = "分组")
@TableField(exist = false) @TableField(exist = false)
private String groupName; private String groupName;
/**教室*/
@Excel(name = "教室", width = 15)
@ApiModelProperty(value = "教室")
@TableField(exist = false)
private String roomName;
} }

View File

@ -16,8 +16,10 @@ import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
/** /**
* @Description: 分组数据表 * @Description: 分组数据表
@ -39,6 +41,7 @@ public class CetGroupServiceImpl extends ServiceImpl<CetGroupMapper, CetGroup> i
if (!(idsObj instanceof List)) { if (!(idsObj instanceof List)) {
return Result.error("参数错误:未找到班级 ID 列表"); return Result.error("参数错误:未找到班级 ID 列表");
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<String> groupIds = (List<String>) idsObj; List<String> groupIds = (List<String>) idsObj;
@ -51,29 +54,33 @@ public class CetGroupServiceImpl extends ServiceImpl<CetGroupMapper, CetGroup> i
new LambdaQueryWrapper<ClassRoom>().eq(ClassRoom::getStatus, 0) new LambdaQueryWrapper<ClassRoom>().eq(ClassRoom::getStatus, 0)
); );
if (freeRooms.size() < groupIds.size()) { // 查询所有分组名称构造 Map<groupId, groupName>
return Result.error("空闲教室数量不足,请减少分配数量或释放教室"); List<CetGroup> groups = cetGroupMapper.selectBatchIds(groupIds);
} Map<String, String> groupIdNameMap = groups.stream()
.collect(Collectors.toMap(CetGroup::getId, CetGroup::getGroupName));
for (int i = 0; i < groupIds.size(); i++) { List<String> failedGroupIds = new ArrayList<>();
int minSize = Math.min(groupIds.size(), freeRooms.size());
for (int i = 0; i < minSize; i++) {
String groupId = groupIds.get(i); String groupId = groupIds.get(i);
ClassRoom room = freeRooms.get(i); ClassRoom room = freeRooms.get(i);
// 使用 LambdaUpdateWrapper 更新教室状态和绑定班级
boolean updated = classRoomMapper.update( boolean updated = classRoomMapper.update(
null, null,
new LambdaUpdateWrapper<ClassRoom>() new LambdaUpdateWrapper<ClassRoom>()
.eq(ClassRoom::getId, room.getId()) .eq(ClassRoom::getId, room.getId())
.eq(ClassRoom::getStatus, 0) // 乐观锁保证未被其他线程占用 .eq(ClassRoom::getStatus, 0)
.set(ClassRoom::getGroupId, groupId) .set(ClassRoom::getGroupId, groupId)
.set(ClassRoom::getStatus, 1) .set(ClassRoom::getStatus, 1)
) > 0; ) > 0;
if (!updated) { if (!updated) {
throw new RuntimeException("教室 " + room.getFullName() + " 被占用,请稍后重试"); failedGroupIds.add(groupId);
continue;
} }
// 更新班级的教室字段假设 classMapper 是使用 MyBatis Plus
cetGroupMapper.update( cetGroupMapper.update(
null, null,
new LambdaUpdateWrapper<CetGroup>() new LambdaUpdateWrapper<CetGroup>()
@ -82,7 +89,25 @@ public class CetGroupServiceImpl extends ServiceImpl<CetGroupMapper, CetGroup> i
); );
} }
return Result.ok("成功为 " + groupIds.size() + " 个班级分配教室"); // 如果教室不够则剩下的也视为失败
if (groupIds.size() > freeRooms.size()) {
for (int i = freeRooms.size(); i < groupIds.size(); i++) {
failedGroupIds.add(groupIds.get(i));
}
}
// 构造返回消息
String message = "成功为 " + (groupIds.size() - failedGroupIds.size()) + " 个分组分配教室。";
if (!failedGroupIds.isEmpty()) {
List<String> failedGroupNames = failedGroupIds.stream()
.map(id -> groupIdNameMap.getOrDefault(id, "未知分组(ID=" + id + ")"))
.collect(Collectors.toList());
message += " 以下分组未分配到教室:" + String.join(", ", failedGroupNames);
}
return Result.ok(message);
} }
} }

View File

@ -153,12 +153,13 @@ public class CetInvigilateDataServiceImpl extends ServiceImpl<CetInvigilateDataM
int index = 1; int index = 1;
for (List<CetInvigilateData> group : groups) { for (List<CetInvigilateData> group : groups) {
// 创建组 // 中文数字组名
String groupName = "" + index + ""; String groupName = "" + toChineseNumber(index) + "";
CetGroup cetGroup = new CetGroup() CetGroup cetGroup = new CetGroup()
.setGroupName(groupName) .setGroupName(groupName)
.setRoomId("") // 可选 .setRoomId("") // 可选
.setCreateBy("system") // 建议为当前用户 .setCreateBy("system") // 建议替换为当前用户
.setCreateTime(new Date()) .setCreateTime(new Date())
.setSysOrgCode("default"); .setSysOrgCode("default");
@ -167,7 +168,7 @@ public class CetInvigilateDataServiceImpl extends ServiceImpl<CetInvigilateDataM
// 设置组ID并更新每位成员 // 设置组ID并更新每位成员
for (CetInvigilateData member : group) { for (CetInvigilateData member : group) {
member.setGroupId(groupId); // 确保 entity 中有 groupId 字段 member.setGroupId(groupId);
cetInvigilateDataMapper.updateById(member); cetInvigilateDataMapper.updateById(member);
} }
@ -179,6 +180,34 @@ public class CetInvigilateDataServiceImpl extends ServiceImpl<CetInvigilateDataM
return Result.ok("共成功组队:" + successCount + " 组,失败:" + (total - successCount) + ""); return Result.ok("共成功组队:" + successCount + " 组,失败:" + (total - successCount) + "");
} }
private String toChineseNumber(int num) {
String[] numChinese = {"", "", "", "", "", "", "", "", "", ""};
String[] units = {"", "", "", ""};
if (num <= 0) return "";
StringBuilder result = new StringBuilder();
String numStr = String.valueOf(num);
int length = numStr.length();
for (int i = 0; i < length; i++) {
int digit = numStr.charAt(i) - '0';
int position = length - i - 1;
if (digit != 0) {
if (!(digit == 1 && position == 1 && result.length() == 0)) {
result.append(numChinese[digit]);
}
result.append(units[position]);
} else {
// Append "" only if next digit is non-zero and not at the end
if (i < length - 1 && numStr.charAt(i + 1) != '0') {
result.append("");
}
}
}
return result.toString();
}
} }