提交
This commit is contained in:
1 parent
14d53f60b0
commit
7b53c004ab
10 files changed
+1123
-296
No files matched your search
+15
@@ -20,6 +20,7 @@ import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.xg.domain.vo.XgDormBedStudentVo;
|
||||
import org.dromara.xg.domain.bo.XgDormBedStudentBo;
|
||||
import org.dromara.xg.domain.bo.XgDormBedBatchAllocateBo;
|
||||
import org.dromara.xg.domain.bo.XgDormBedBatchAllocateToClassBo;
|
||||
import org.dromara.xg.service.IXgDormBedStudentService;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo;
|
||||
@@ -117,6 +118,20 @@ public class XgDormBedStudentController extends BaseController {
|
||||
return toAjax(xgDormBedStudentService.batchAllocateBedsToCollege(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量分配床位给班级
|
||||
*
|
||||
* @param bo 批量分配给班级参数
|
||||
*/
|
||||
@SaCheckPermission("dormBedStudent:dormBedStudent:add")
|
||||
@Log(title = "批量分配床位给班级", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/batchAllocateToClass")
|
||||
public R<Void> batchAllocateBedsToClass(
|
||||
@Validated(AddGroup.class) @RequestBody XgDormBedBatchAllocateToClassBo bo) {
|
||||
return toAjax(xgDormBedStudentService.batchAllocateBedsToClass(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量收回宿舍床位
|
||||
*/
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package org.dromara.xg.domain.bo;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 床位批量分配给班级业务对象 xg_dorm_bed_batch_allocate_to_class
|
||||
*
|
||||
* @author zlwu
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class XgDormBedBatchAllocateToClassBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 床位ID列表
|
||||
*/
|
||||
@NotEmpty(message = "床位ID列表不能为空", groups = { AddGroup.class })
|
||||
private List<Long> bedIds;
|
||||
|
||||
/**
|
||||
* 班级ID列表
|
||||
*/
|
||||
@NotEmpty(message = "班级ID列表不能为空", groups = { AddGroup.class })
|
||||
private List<Long> classIds;
|
||||
|
||||
/**
|
||||
* 班级人数列表(与classIds一一对应)
|
||||
*/
|
||||
private List<Integer> classStudentCounts;
|
||||
|
||||
/**
|
||||
* 楼栋ID
|
||||
*/
|
||||
private Long buildingId;
|
||||
|
||||
/**
|
||||
* 楼栋类型(1-男生楼,2-女生楼)
|
||||
*/
|
||||
private String buildingType;
|
||||
|
||||
}
|
||||
+68
-60
@@ -19,71 +19,79 @@ import java.util.Map;
|
||||
*/
|
||||
public interface XgStudentMsgMapper extends BaseMapperPlus<XgStudentMsg, XgStudentMsgVo> {
|
||||
|
||||
/**
|
||||
* 根据组织ID列表分页查询学生信息 用于导出
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param queryWrapper 查询条件
|
||||
* @param deptIds 组织ID列表
|
||||
* @return 学生信息分页结果
|
||||
*/
|
||||
List<XgStudentMsgExportVo> queryPageListExportByDeptId(
|
||||
@Param("ew") LambdaQueryWrapper<XgStudentMsg> queryWrapper,
|
||||
@Param("deptIds") List<Long> deptIds);
|
||||
/**
|
||||
* 根据组织ID列表分页查询学生信息 用于导出
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param queryWrapper 查询条件
|
||||
* @param deptIds 组织ID列表
|
||||
* @return 学生信息分页结果
|
||||
*/
|
||||
List<XgStudentMsgExportVo> queryPageListExportByDeptId(
|
||||
@Param("ew") LambdaQueryWrapper<XgStudentMsg> queryWrapper,
|
||||
@Param("deptIds") List<Long> deptIds);
|
||||
|
||||
/**
|
||||
* 根据组织ID列表分页查询学生信息
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param queryWrapper 查询条件
|
||||
* @param deptIds 组织ID列表
|
||||
* @return 学生信息分页结果
|
||||
*/
|
||||
Page<XgStudentMsgVo> selectPageByDeptIds(@Param("page") Page<XgStudentMsgVo> page,
|
||||
@Param("ew") LambdaQueryWrapper<XgStudentMsg> queryWrapper,
|
||||
@Param("deptIds") List<Long> deptIds);
|
||||
/**
|
||||
* 根据组织ID列表分页查询学生信息
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param queryWrapper 查询条件
|
||||
* @param deptIds 组织ID列表
|
||||
* @return 学生信息分页结果
|
||||
*/
|
||||
Page<XgStudentMsgVo> selectPageByDeptIds(@Param("page") Page<XgStudentMsgVo> page,
|
||||
@Param("ew") LambdaQueryWrapper<XgStudentMsg> queryWrapper,
|
||||
@Param("deptIds") List<Long> deptIds);
|
||||
|
||||
/**
|
||||
* 根据班级ID和姓名查询学生信息列表
|
||||
*
|
||||
* @param classId 班级ID
|
||||
* @param name 学生姓名(模糊查询)
|
||||
* @return 学生信息列表
|
||||
*/
|
||||
List<XgStudentMsgVo> selectStudentsByClassId(@Param("classId") String classId, @Param("name") String name);
|
||||
/**
|
||||
* 根据班级ID和姓名查询学生信息列表
|
||||
*
|
||||
* @param classId 班级ID
|
||||
* @param name 学生姓名(模糊查询)
|
||||
* @return 学生信息列表
|
||||
*/
|
||||
List<XgStudentMsgVo> selectStudentsByClassId(@Param("classId") String classId, @Param("name") String name);
|
||||
|
||||
/**
|
||||
* 根据学生ID查询班主任ID
|
||||
*
|
||||
* @param studentId 学生ID
|
||||
* @return 班主任ID
|
||||
*/
|
||||
Long selectClassTeacherIdByStudentId(@Param("studentId") Long studentId);
|
||||
/**
|
||||
* 根据学生ID查询班主任ID
|
||||
*
|
||||
* @param studentId 学生ID
|
||||
* @return 班主任ID
|
||||
*/
|
||||
Long selectClassTeacherIdByStudentId(@Param("studentId") Long studentId);
|
||||
|
||||
/**
|
||||
* 根据学生ID查询学院ID
|
||||
*
|
||||
* @param studentId 学生ID
|
||||
* @return 学院ID
|
||||
*/
|
||||
Long selectCollegeIdByStudentId(@Param("studentId") Long studentId);
|
||||
/**
|
||||
* 根据学生ID查询学院ID
|
||||
*
|
||||
* @param studentId 学生ID
|
||||
* @return 学院ID
|
||||
*/
|
||||
Long selectCollegeIdByStudentId(@Param("studentId") Long studentId);
|
||||
|
||||
/**
|
||||
* 根据学院ID和角色Key查询具备该角色的用户列表
|
||||
*
|
||||
* @param collegeId 学院ID
|
||||
* @param roleKey 角色Key
|
||||
* @return 用户ID列表
|
||||
*/
|
||||
List<Long> selectUserIdsByCollegeIdAndRoleKey(@Param("collegeId") Long collegeId,
|
||||
@Param("roleKey") String roleKey);
|
||||
/**
|
||||
* 根据学院ID和角色Key查询具备该角色的用户列表
|
||||
*
|
||||
* @param collegeId 学院ID
|
||||
* @param roleKey 角色Key
|
||||
* @return 用户ID列表
|
||||
*/
|
||||
List<Long> selectUserIdsByCollegeIdAndRoleKey(@Param("collegeId") Long collegeId,
|
||||
@Param("roleKey") String roleKey);
|
||||
|
||||
/**
|
||||
* 查询二级学院下的学院、专业、班级学生统计信息
|
||||
*
|
||||
* @param secondaryCollegeId 二级学院ID
|
||||
* @return 学生统计信息列表
|
||||
*/
|
||||
List<Map<String, Object>> getCollegeStudentStats(@Param("secondaryCollegeId") Long secondaryCollegeId);
|
||||
/**
|
||||
* 查询二级学院下的学院、专业、班级学生统计信息
|
||||
*
|
||||
* @param secondaryCollegeId 二级学院ID
|
||||
* @return 学生统计信息列表
|
||||
*/
|
||||
List<Map<String, Object>> getCollegeStudentStats(@Param("secondaryCollegeId") Long secondaryCollegeId);
|
||||
|
||||
/**
|
||||
* 查询二级学院下的学院、专业、班级学生统计信息(按床位分配统计)
|
||||
*
|
||||
* @param secondaryCollegeId 二级学院ID
|
||||
* @return 学生统计信息列表
|
||||
*/
|
||||
List<Map<String, Object>> getCollegeStudentStatsByClass(@Param("secondaryCollegeId") Long secondaryCollegeId);
|
||||
|
||||
}
|
||||
+9
@@ -3,6 +3,7 @@ package org.dromara.xg.service;
|
||||
import org.dromara.xg.domain.vo.XgDormBedStudentVo;
|
||||
import org.dromara.xg.domain.bo.XgDormBedStudentBo;
|
||||
import org.dromara.xg.domain.bo.XgDormBedBatchAllocateBo;
|
||||
import org.dromara.xg.domain.bo.XgDormBedBatchAllocateToClassBo;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
@@ -79,6 +80,14 @@ public interface IXgDormBedStudentService {
|
||||
Boolean batchAllocateBedsToCollege(
|
||||
XgDormBedBatchAllocateBo bo);
|
||||
|
||||
/**
|
||||
* 批量分配床位给班级
|
||||
*
|
||||
* @param bo 批量分配给班级请求参数
|
||||
* @return 是否分配成功
|
||||
*/
|
||||
Boolean batchAllocateBedsToClass(XgDormBedBatchAllocateToClassBo bo);
|
||||
|
||||
/**
|
||||
* 批量收回宿舍床位
|
||||
*
|
||||
|
||||
+305
@@ -45,6 +45,9 @@ import org.dromara.xg.mapper.XgSysDeptMapper;
|
||||
import org.dromara.xg.domain.XgSysDept;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.LinkedHashMap;
|
||||
import org.dromara.xg.domain.bo.XgDormBedBatchAllocateToClassBo;
|
||||
import org.dromara.xg.mapper.XgClassMsgMapper;
|
||||
import org.dromara.xg.domain.XgClassMsg;
|
||||
|
||||
/**
|
||||
* 床位学生分配关系Service业务层处理
|
||||
@@ -65,6 +68,7 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
private final XgDormCampusMapper xgDormCampusMapper;
|
||||
private final XgDormAreaMapper xgDormAreaMapper;
|
||||
private final XgSysDeptMapper xgSysDeptMapper;
|
||||
private final XgClassMsgMapper xgClassMsgMapper;
|
||||
|
||||
/**
|
||||
* 查询床位学生分配关系
|
||||
@@ -504,6 +508,307 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量分配床位给班级
|
||||
*
|
||||
* @param bo 批量分配给班级请求参数
|
||||
* @return 是否分配成功
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean batchAllocateBedsToClass(XgDormBedBatchAllocateToClassBo bo) {
|
||||
try {
|
||||
// 1. 获取床位信息
|
||||
List<XgDormBed> beds = xgDormBedMapper.selectBatchIds(bo.getBedIds());
|
||||
|
||||
// 2. 校验床位是否已分配给班级(检查xg_dorm_bed_student表中的classId)
|
||||
LambdaQueryWrapper<XgDormBedStudent> classCheckWrapper = new LambdaQueryWrapper<>();
|
||||
classCheckWrapper.in(XgDormBedStudent::getBedId, bo.getBedIds())
|
||||
.isNotNull(XgDormBedStudent::getClassId)
|
||||
.eq(XgDormBedStudent::getStatus, "0"); // 正常状态,未退宿
|
||||
|
||||
List<XgDormBedStudent> existingClassAllocations = baseMapper.selectList(classCheckWrapper);
|
||||
|
||||
if (!existingClassAllocations.isEmpty()) {
|
||||
// 找出已分配的床位名称
|
||||
Set<Long> allocatedBedIds = existingClassAllocations.stream()
|
||||
.map(XgDormBedStudent::getBedId)
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
|
||||
List<String> allocatedBedNames = beds.stream()
|
||||
.filter(bed -> allocatedBedIds.contains(bed.getId()))
|
||||
.map(XgDormBed::getBedName)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
throw new RuntimeException("以下床位已分配给班级,无法重复分配:" + String.join("、", allocatedBedNames));
|
||||
}
|
||||
|
||||
// 3. 检查床位是否已分配给学院(必须先分配给学院才能分配给班级)
|
||||
LambdaQueryWrapper<XgDormBedStudent> collegeCheckWrapper = new LambdaQueryWrapper<>();
|
||||
collegeCheckWrapper.in(XgDormBedStudent::getBedId, bo.getBedIds())
|
||||
.isNotNull(XgDormBedStudent::getCollegeId)
|
||||
.isNull(XgDormBedStudent::getClassId) // 只分配了学院,还没分配班级
|
||||
.eq(XgDormBedStudent::getStatus, "0"); // 正常状态,未退宿
|
||||
|
||||
List<XgDormBedStudent> existingCollegeAllocations = baseMapper.selectList(collegeCheckWrapper);
|
||||
|
||||
if (existingCollegeAllocations.isEmpty()) {
|
||||
throw new RuntimeException("选择的床位尚未分配给学院,请先分配给学院后再分配给班级");
|
||||
}
|
||||
|
||||
// 4. 验证所有床位都已分配给学院
|
||||
Set<Long> collegeAllocatedBedIds = existingCollegeAllocations.stream()
|
||||
.map(XgDormBedStudent::getBedId)
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
|
||||
List<Long> unallocatedBedIds = bo.getBedIds().stream()
|
||||
.filter(bedId -> !collegeAllocatedBedIds.contains(bedId))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
if (!unallocatedBedIds.isEmpty()) {
|
||||
List<String> unallocatedBedNames = beds.stream()
|
||||
.filter(bed -> unallocatedBedIds.contains(bed.getId()))
|
||||
.map(XgDormBed::getBedName)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
throw new RuntimeException("以下床位尚未分配给学院,无法分配给班级:" + String.join("、", unallocatedBedNames));
|
||||
}
|
||||
|
||||
// 5. 获取楼栋信息,用于获取校区ID和宿舍区ID
|
||||
XgDormBuilding building = xgDormBuildingMapper.selectById(bo.getBuildingId());
|
||||
if (building == null) {
|
||||
throw new RuntimeException("楼栋信息不存在,buildingId: " + bo.getBuildingId());
|
||||
}
|
||||
|
||||
// 6. 查询班级信息,获取专业ID和学院ID
|
||||
List<XgClassMsg> classes = xgClassMsgMapper.selectBatchIds(bo.getClassIds());
|
||||
if (classes.size() != bo.getClassIds().size()) {
|
||||
throw new RuntimeException("部分班级信息不存在,请检查班级ID");
|
||||
}
|
||||
|
||||
// 建立班级ID到班级信息的映射
|
||||
Map<Long, XgClassMsg> classMap = classes.stream()
|
||||
.collect(java.util.stream.Collectors.toMap(XgClassMsg::getId, cls -> cls));
|
||||
|
||||
// 7. 计算每个班级分配的床位数量
|
||||
Date now = new Date();
|
||||
Long loginUserId = LoginHelper.getUserId();
|
||||
int totalAvailableBeds = collegeAllocatedBedIds.size();
|
||||
|
||||
// 如果没有提供班级人数信息,则平均分配
|
||||
List<Integer> classCounts = bo.getClassStudentCounts();
|
||||
if (classCounts == null || classCounts.isEmpty()) {
|
||||
// 平均分配:每个班级分配的床位数
|
||||
int bedsPerClass = totalAvailableBeds / bo.getClassIds().size();
|
||||
int remainingBeds = totalAvailableBeds % bo.getClassIds().size();
|
||||
|
||||
classCounts = new ArrayList<>();
|
||||
for (int i = 0; i < bo.getClassIds().size(); i++) {
|
||||
int count = bedsPerClass + (i < remainingBeds ? 1 : 0);
|
||||
classCounts.add(count);
|
||||
}
|
||||
}
|
||||
|
||||
// 验证班级人数总和不能超过可用床位总数
|
||||
int totalStudents = classCounts.stream().mapToInt(Integer::intValue).sum();
|
||||
if (totalStudents > totalAvailableBeds) {
|
||||
throw new RuntimeException(
|
||||
"班级人数总和(" + totalStudents + ")超过了可用床位总数(" + totalAvailableBeds + "),请调整分配数量");
|
||||
}
|
||||
|
||||
log.info("床位分配验证通过:可用床位数{},分配学生数{},剩余床位数{}",
|
||||
totalAvailableBeds, totalStudents, totalAvailableBeds - totalStudents);
|
||||
|
||||
// 8. 获取房间信息,建立roomId到roomCode的映射
|
||||
Set<Long> roomIds = beds.stream().map(XgDormBed::getRoomId).collect(java.util.stream.Collectors.toSet());
|
||||
List<XgDormRoom> rooms = xgDormRoomMapper.selectBatchIds(roomIds);
|
||||
Map<Long, String> roomCodeMap = rooms.stream()
|
||||
.collect(java.util.stream.Collectors.toMap(XgDormRoom::getId, XgDormRoom::getRoomCode));
|
||||
|
||||
// 9. 将床位按房间顺序排列,实现房间优先分配
|
||||
// 获取已分配给学院的床位详细信息
|
||||
List<XgDormBed> availableBeds = beds.stream()
|
||||
.filter(bed -> collegeAllocatedBedIds.contains(bed.getId()))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 按房间编码排序床位,确保同一房间的床位连续
|
||||
List<XgDormBed> sortedBeds = availableBeds.stream()
|
||||
.sorted((b1, b2) -> {
|
||||
// 首先按房间编码排序
|
||||
String roomCode1 = roomCodeMap.get(b1.getRoomId());
|
||||
String roomCode2 = roomCodeMap.get(b2.getRoomId());
|
||||
int roomCompare = roomCode1.compareTo(roomCode2);
|
||||
if (roomCompare != 0) {
|
||||
return roomCompare;
|
||||
}
|
||||
// 同一房间内按床位编码排序
|
||||
return b1.getBedCode().compareTo(b2.getBedCode());
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 10. 按房间分组床位,实现房间优先分配策略
|
||||
Map<Long, List<XgDormBed>> bedsByRoom = sortedBeds.stream()
|
||||
.collect(java.util.stream.Collectors.groupingBy(XgDormBed::getRoomId));
|
||||
|
||||
// 获取排序后的房间ID列表
|
||||
List<Long> sortedRoomIds = new ArrayList<>(bedsByRoom.keySet());
|
||||
sortedRoomIds.sort((r1, r2) -> roomCodeMap.get(r1).compareTo(roomCodeMap.get(r2)));
|
||||
|
||||
// 11. 房间优先分配算法(四轮分配策略)
|
||||
// 房间使用状态跟踪
|
||||
int[] roomUsage = new int[sortedRoomIds.size()]; // 记录每个房间已使用床位数
|
||||
Map<Integer, Long> roomClassMap = new java.util.HashMap<>(); // 记录房间主要分配给哪个班级
|
||||
|
||||
// 按班级顺序,采用房间优先分配策略
|
||||
for (int classIndex = 0; classIndex < bo.getClassIds().size(); classIndex++) {
|
||||
Long classId = bo.getClassIds().get(classIndex);
|
||||
XgClassMsg classInfo = classMap.get(classId);
|
||||
int remainingStudents = classCounts.get(classIndex);
|
||||
|
||||
log.info("开始为班级{}分配{}个床位,采用房间优先策略", classInfo.getClassName(), remainingStudents);
|
||||
|
||||
// 第一轮:优先分配完整房间
|
||||
for (int roomIndex = 0; roomIndex < sortedRoomIds.size() && remainingStudents > 0; roomIndex++) {
|
||||
Long roomId = sortedRoomIds.get(roomIndex);
|
||||
List<XgDormBed> roomBeds = bedsByRoom.get(roomId);
|
||||
int roomCapacity = roomBeds.size();
|
||||
|
||||
// 如果房间空闲且学生数足够分配整个房间
|
||||
if (roomUsage[roomIndex] == 0 && remainingStudents >= roomCapacity) {
|
||||
// 分配整个房间给当前班级
|
||||
roomUsage[roomIndex] = roomCapacity;
|
||||
roomClassMap.put(roomIndex, classId);
|
||||
remainingStudents -= roomCapacity;
|
||||
|
||||
log.info("为班级{}分配完整房间{},房间编码{},床位数{}",
|
||||
classInfo.getClassName(), roomId, roomCodeMap.get(roomId), roomCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
// 第二轮:处理剩余学生(优先分配到已有该班级学生的房间)
|
||||
for (int roomIndex = 0; roomIndex < sortedRoomIds.size() && remainingStudents > 0; roomIndex++) {
|
||||
Long roomId = sortedRoomIds.get(roomIndex);
|
||||
List<XgDormBed> roomBeds = bedsByRoom.get(roomId);
|
||||
int availableBedsCount = roomBeds.size() - roomUsage[roomIndex];
|
||||
|
||||
// 优先分配到已有该班级学生的房间
|
||||
if (availableBedsCount > 0 && classId.equals(roomClassMap.get(roomIndex))) {
|
||||
int bedsToAllocate = Math.min(remainingStudents, availableBedsCount);
|
||||
roomUsage[roomIndex] += bedsToAllocate;
|
||||
remainingStudents -= bedsToAllocate;
|
||||
|
||||
log.info("为班级{}在已分配房间{}补充{}个床位",
|
||||
classInfo.getClassName(), roomId, bedsToAllocate);
|
||||
}
|
||||
}
|
||||
|
||||
// 第三轮:如果还有剩余学生,分配到空闲房间(避免混住)
|
||||
for (int roomIndex = 0; roomIndex < sortedRoomIds.size() && remainingStudents > 0; roomIndex++) {
|
||||
Long roomId = sortedRoomIds.get(roomIndex);
|
||||
List<XgDormBed> roomBeds = bedsByRoom.get(roomId);
|
||||
int availableBedsCount = roomBeds.size() - roomUsage[roomIndex];
|
||||
|
||||
// 分配到完全空闲的房间
|
||||
if (availableBedsCount > 0 && roomClassMap.get(roomIndex) == null) {
|
||||
int bedsToAllocate = Math.min(remainingStudents, availableBedsCount);
|
||||
roomUsage[roomIndex] += bedsToAllocate;
|
||||
roomClassMap.put(roomIndex, classId);
|
||||
remainingStudents -= bedsToAllocate;
|
||||
|
||||
log.info("为班级{}分配空闲房间{}的{}个床位",
|
||||
classInfo.getClassName(), roomId, bedsToAllocate);
|
||||
}
|
||||
}
|
||||
|
||||
// 第四轮:确保所有学生都能分配到床位(允许混住)
|
||||
for (int roomIndex = 0; roomIndex < sortedRoomIds.size() && remainingStudents > 0; roomIndex++) {
|
||||
Long roomId = sortedRoomIds.get(roomIndex);
|
||||
List<XgDormBed> roomBeds = bedsByRoom.get(roomId);
|
||||
int availableBedsCount = roomBeds.size() - roomUsage[roomIndex];
|
||||
|
||||
// 分配到任何有空闲床位的房间,即使会产生混住
|
||||
if (availableBedsCount > 0) {
|
||||
int bedsToAllocate = Math.min(remainingStudents, availableBedsCount);
|
||||
roomUsage[roomIndex] += bedsToAllocate;
|
||||
remainingStudents -= bedsToAllocate;
|
||||
|
||||
Long existingClassId = roomClassMap.get(roomIndex);
|
||||
if (existingClassId != null && !existingClassId.equals(classId)) {
|
||||
XgClassMsg existingClass = classMap.get(existingClassId);
|
||||
log.warn("房间{}产生混住:班级{}和班级{}",
|
||||
roomCodeMap.get(roomId), existingClass.getClassName(), classInfo.getClassName());
|
||||
}
|
||||
|
||||
log.info("为班级{}分配房间{}的{}个床位(可能混住)",
|
||||
classInfo.getClassName(), roomId, bedsToAllocate);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否还有未分配的学生
|
||||
if (remainingStudents > 0) {
|
||||
throw new RuntimeException(
|
||||
"班级" + classInfo.getClassName() + "还有" + remainingStudents + "个学生未能分配床位,床位资源不足");
|
||||
}
|
||||
|
||||
log.info("班级{}床位分配完成", classInfo.getClassName());
|
||||
}
|
||||
|
||||
// 12. 批量更新床位学生记录,补充班级和专业信息
|
||||
for (int i = 0; i < bo.getClassIds().size(); i++) {
|
||||
Long classId = bo.getClassIds().get(i);
|
||||
XgClassMsg classInfo = classMap.get(classId);
|
||||
|
||||
// 计算应该分配给该班级的床位数量
|
||||
int classStudentCount = classCounts.get(i);
|
||||
|
||||
// 获取该班级应该分配的床位ID列表(按分配算法确定的顺序)
|
||||
List<Long> classBedIds = new ArrayList<>();
|
||||
int allocatedCount = 0;
|
||||
|
||||
// 按房间分配顺序找出分配给该班级的床位
|
||||
for (int roomIndex = 0; roomIndex < sortedRoomIds.size()
|
||||
&& allocatedCount < classStudentCount; roomIndex++) {
|
||||
if (classId.equals(roomClassMap.get(roomIndex))) {
|
||||
Long roomId = sortedRoomIds.get(roomIndex);
|
||||
List<XgDormBed> roomBeds = bedsByRoom.get(roomId);
|
||||
|
||||
// 计算这个房间分配给该班级的床位数
|
||||
int roomBedsForThisClass = Math.min(classStudentCount - allocatedCount, roomBeds.size());
|
||||
|
||||
for (int j = 0; j < roomBedsForThisClass; j++) {
|
||||
classBedIds.add(roomBeds.get(j).getId());
|
||||
allocatedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!classBedIds.isEmpty()) {
|
||||
// 批量更新该班级分配的床位记录
|
||||
LambdaUpdateWrapper<XgDormBedStudent> updateWrapper = Wrappers.lambdaUpdate();
|
||||
updateWrapper.in(XgDormBedStudent::getBedId, classBedIds)
|
||||
.eq(XgDormBedStudent::getStatus, "0") // 正常状态
|
||||
.isNull(XgDormBedStudent::getClassId) // 只更新还没分配班级的记录
|
||||
.isNotNull(XgDormBedStudent::getCollegeId) // 已分配给学院
|
||||
.set(XgDormBedStudent::getMajorId, classInfo.getMajorId())
|
||||
.set(XgDormBedStudent::getClassId, classInfo.getId())
|
||||
.set(XgDormBedStudent::getUpdateTime, now)
|
||||
.set(XgDormBedStudent::getUpdateBy, loginUserId);
|
||||
|
||||
int updateCount = baseMapper.update(null, updateWrapper);
|
||||
log.info("为班级{}({})更新了{}条床位分配记录", classInfo.getClassName(), classId, updateCount);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("成功更新{}个床位分配给{}个班级,总可用床位:{}",
|
||||
bo.getBedIds().size(), bo.getClassIds().size(), totalAvailableBeds);
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("批量分配床位给班级失败", e);
|
||||
throw new RuntimeException("床位分配失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户二级学院的宿舍楼树形结构
|
||||
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
|
||||
|
||||
+150
-2
@@ -1321,6 +1321,154 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学生信息树(二级学院下的学院、专业、班级统计信息)- 按班级床位分配统计
|
||||
*
|
||||
* @param secondaryCollegeId 二级学院ID,默认101
|
||||
* @return 学生信息树
|
||||
*/
|
||||
public List<XgStudentTreeVo> getStudentTreeByClass(Long secondaryCollegeId) {
|
||||
log.info("开始构建学生信息树(按床位分配统计),二级学院ID: {}", secondaryCollegeId);
|
||||
|
||||
List<XgStudentTreeVo> result = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// 1. 查询二级学院下的所有学院信息,并统计男女生人数(使用新的按班级床位分配统计方法)
|
||||
List<Map<String, Object>> collegeStats = baseMapper.getCollegeStudentStatsByClass(secondaryCollegeId);
|
||||
log.info("查询到 {} 个学院", collegeStats.size());
|
||||
|
||||
// 按学院分组
|
||||
Map<String, List<Map<String, Object>>> collegeMap = collegeStats.stream()
|
||||
.collect(Collectors.groupingBy(item -> {
|
||||
Object collegeIdObj = item.get("college_id");
|
||||
Long collegeId = collegeIdObj instanceof Long ? (Long) collegeIdObj
|
||||
: Long.valueOf(collegeIdObj.toString());
|
||||
String collegeName = (String) item.get("college_name");
|
||||
return collegeId + "_" + collegeName;
|
||||
}));
|
||||
|
||||
// 2. 构建学院节点
|
||||
for (Map.Entry<String, List<Map<String, Object>>> collegeEntry : collegeMap.entrySet()) {
|
||||
String collegeKey = collegeEntry.getKey();
|
||||
List<Map<String, Object>> collegeMajors = collegeEntry.getValue();
|
||||
|
||||
String[] keyParts = collegeKey.split("_");
|
||||
Long collegeId = Long.valueOf(keyParts[0]);
|
||||
String collegeName = keyParts[1];
|
||||
|
||||
// 计算学院级别的统计信息
|
||||
int collegeMaleTotal = 0;
|
||||
int collegeFemaleTotal = 0;
|
||||
int collegeMaleAssigned = 0;
|
||||
int collegeFemaleAssigned = 0;
|
||||
|
||||
for (Map<String, Object> item : collegeMajors) {
|
||||
collegeMaleTotal += ((Number) item.get("male_total")).intValue();
|
||||
collegeFemaleTotal += ((Number) item.get("female_total")).intValue();
|
||||
collegeMaleAssigned += ((Number) item.get("male_assigned")).intValue();
|
||||
collegeFemaleAssigned += ((Number) item.get("female_assigned")).intValue();
|
||||
}
|
||||
|
||||
XgStudentTreeVo collegeNode = new XgStudentTreeVo();
|
||||
collegeNode.setId(collegeId.toString());
|
||||
collegeNode.setLabel(collegeName);
|
||||
collegeNode.setType("college");
|
||||
collegeNode.setCollegeId(collegeId);
|
||||
collegeNode.setCollegeName(collegeName);
|
||||
collegeNode.setMaleTotal(collegeMaleTotal);
|
||||
collegeNode.setFemaleTotal(collegeFemaleTotal);
|
||||
collegeNode.setMaleAssigned(collegeMaleAssigned);
|
||||
collegeNode.setFemaleAssigned(collegeFemaleAssigned);
|
||||
collegeNode.setChildren(new ArrayList<>());
|
||||
|
||||
// 按专业分组
|
||||
Map<String, List<Map<String, Object>>> majorMap = collegeMajors.stream()
|
||||
.collect(Collectors.groupingBy(item -> {
|
||||
Object majorIdObj = item.get("major_id");
|
||||
Long majorId = majorIdObj instanceof Long ? (Long) majorIdObj
|
||||
: Long.valueOf(majorIdObj.toString());
|
||||
String majorName = (String) item.get("major_name");
|
||||
return majorId + "_" + majorName;
|
||||
}));
|
||||
|
||||
// 3. 构建专业节点
|
||||
for (Map.Entry<String, List<Map<String, Object>>> majorEntry : majorMap.entrySet()) {
|
||||
String majorKey = majorEntry.getKey();
|
||||
List<Map<String, Object>> majorClasses = majorEntry.getValue();
|
||||
|
||||
String[] majorKeyParts = majorKey.split("_");
|
||||
Long majorId = Long.valueOf(majorKeyParts[0]);
|
||||
String majorName = majorKeyParts[1];
|
||||
|
||||
// 计算专业级别的统计信息
|
||||
int majorMaleTotal = 0;
|
||||
int majorFemaleTotal = 0;
|
||||
int majorMaleAssigned = 0;
|
||||
int majorFemaleAssigned = 0;
|
||||
|
||||
for (Map<String, Object> item : majorClasses) {
|
||||
majorMaleTotal += ((Number) item.get("male_total")).intValue();
|
||||
majorFemaleTotal += ((Number) item.get("female_total")).intValue();
|
||||
majorMaleAssigned += ((Number) item.get("male_assigned")).intValue();
|
||||
majorFemaleAssigned += ((Number) item.get("female_assigned")).intValue();
|
||||
}
|
||||
|
||||
XgStudentTreeVo majorNode = new XgStudentTreeVo();
|
||||
majorNode.setId(majorId.toString());
|
||||
majorNode.setLabel(majorName);
|
||||
majorNode.setType("major");
|
||||
majorNode.setMajorId(majorId);
|
||||
majorNode.setMajorName(majorName);
|
||||
majorNode.setMaleTotal(majorMaleTotal);
|
||||
majorNode.setFemaleTotal(majorFemaleTotal);
|
||||
majorNode.setMaleAssigned(majorMaleAssigned);
|
||||
majorNode.setFemaleAssigned(majorFemaleAssigned);
|
||||
majorNode.setChildren(new ArrayList<>());
|
||||
|
||||
// 4. 构建班级节点
|
||||
for (Map<String, Object> classData : majorClasses) {
|
||||
Object classIdObj = classData.get("class_id");
|
||||
Long classId = classIdObj instanceof Long ? (Long) classIdObj
|
||||
: Long.valueOf(classIdObj.toString());
|
||||
String className = (String) classData.get("class_name");
|
||||
String grade = (String) classData.get("grade");
|
||||
|
||||
int classMaleTotal = ((Number) classData.get("male_total")).intValue();
|
||||
int classFemaleTotal = ((Number) classData.get("female_total")).intValue();
|
||||
int classMaleAssigned = ((Number) classData.get("male_assigned")).intValue();
|
||||
int classFemaleAssigned = ((Number) classData.get("female_assigned")).intValue();
|
||||
|
||||
XgStudentTreeVo classNode = new XgStudentTreeVo();
|
||||
classNode.setId(classId.toString());
|
||||
classNode.setLabel(className);
|
||||
classNode.setType("class");
|
||||
classNode.setGrade(grade);
|
||||
classNode.setClassId(classId);
|
||||
classNode.setClassName(className);
|
||||
classNode.setMaleTotal(classMaleTotal);
|
||||
classNode.setFemaleTotal(classFemaleTotal);
|
||||
classNode.setMaleAssigned(classMaleAssigned);
|
||||
classNode.setFemaleAssigned(classFemaleAssigned);
|
||||
classNode.setChildren(new ArrayList<>());
|
||||
|
||||
majorNode.getChildren().add(classNode);
|
||||
}
|
||||
|
||||
collegeNode.getChildren().add(majorNode);
|
||||
}
|
||||
|
||||
result.add(collegeNode);
|
||||
}
|
||||
|
||||
log.info("成功构建学生信息树,共 {} 个学院", result.size());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("构建学生信息树失败", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户所在学院的学生信息树
|
||||
*
|
||||
@@ -1338,8 +1486,8 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService {
|
||||
|
||||
log.info("获取到当前用户的学院ID: {}", collegeId);
|
||||
|
||||
// 2. 调用现有的getStudentTree方法
|
||||
return getStudentTree(collegeId);
|
||||
// 2. 调用按床位分配统计的方法
|
||||
return getStudentTreeByClass(collegeId);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取当前用户学院的学生信息树失败", e);
|
||||
|
||||
+50
@@ -200,4 +200,54 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
ORDER BY c.grade DESC, sm.college_name, sm.major_name, sm.class_name
|
||||
</select>
|
||||
|
||||
<!-- 查询二级学院下的学院、专业、班级学生统计信息(按床位分配统计) -->
|
||||
<select id="getCollegeStudentStatsByClass" resultType="java.util.Map">
|
||||
SELECT
|
||||
c.grade,
|
||||
sm.college_id,
|
||||
sm.college_name,
|
||||
sm.major_id,
|
||||
sm.major_name,
|
||||
sm.class_id,
|
||||
sm.class_name,
|
||||
COALESCE(SUM(CASE WHEN sm.gender = '0' AND sm.is_boarder = 1 THEN 1 ELSE 0 END), 0) AS male_total,
|
||||
COALESCE(SUM(CASE WHEN sm.gender = '1' AND sm.is_boarder = 1 THEN 1 ELSE 0 END), 0) AS female_total,
|
||||
COALESCE((
|
||||
SELECT COUNT(DISTINCT dbs.bed_id)
|
||||
FROM xg_dorm_bed_student dbs
|
||||
LEFT JOIN xg_dorm_bed db ON dbs.bed_id = db.id
|
||||
LEFT JOIN xg_dorm_room dr ON db.room_id = dr.id
|
||||
LEFT JOIN xg_dorm_building dbuild ON dr.building_id = dbuild.id
|
||||
WHERE dbs.class_id = sm.class_id
|
||||
AND dbs.deleted = '0'
|
||||
AND dbs.status = '0'
|
||||
AND db.deleted = '0'
|
||||
AND dbuild.building_type = '1'
|
||||
), 0) AS male_assigned,
|
||||
COALESCE((
|
||||
SELECT COUNT(DISTINCT dbs.bed_id)
|
||||
FROM xg_dorm_bed_student dbs
|
||||
LEFT JOIN xg_dorm_bed db ON dbs.bed_id = db.id
|
||||
LEFT JOIN xg_dorm_room dr ON db.room_id = dr.id
|
||||
LEFT JOIN xg_dorm_building dbuild ON dr.building_id = dbuild.id
|
||||
WHERE dbs.class_id = sm.class_id
|
||||
AND dbs.deleted = '0'
|
||||
AND dbs.status = '0'
|
||||
AND db.deleted = '0'
|
||||
AND dbuild.building_type = '2'
|
||||
), 0) AS female_assigned
|
||||
FROM xg_student_msg sm
|
||||
LEFT JOIN xg_class_msg c ON sm.class_id = c.id
|
||||
LEFT JOIN sys_dept d ON sm.college_id = d.dept_id
|
||||
WHERE sm.deleted = 0
|
||||
AND sm.student_status = '1'
|
||||
AND sm.is_boarder = 1
|
||||
AND (d.parent_id = #{secondaryCollegeId} OR d.dept_id = #{secondaryCollegeId} OR FIND_IN_SET(#{secondaryCollegeId}, d.ancestors))
|
||||
AND sm.college_id IS NOT NULL
|
||||
AND sm.major_id IS NOT NULL
|
||||
AND sm.class_id IS NOT NULL
|
||||
GROUP BY c.grade, sm.college_id, sm.college_name, sm.major_id, sm.major_name, sm.class_id, sm.class_name
|
||||
ORDER BY c.grade DESC, sm.college_name, sm.major_name, sm.class_name
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Reference in new issue
Block a user