This commit is contained in:
zlwu committed 2025-08-14 00:32:15 +08:00
1 parent 14d53f60b0
commit 7b53c004ab
10 files changed
+1123 -296

No files matched your search

@@ -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));
}
/**
* 批量收回宿舍床位
*/
@@ -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;
}
@@ -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);
}
@@ -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);
/**
* 批量收回宿舍床位
*
@@ -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());
}
}
/**
* 获取当前用户二级学院的宿舍楼树形结构
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
@@ -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);
@@ -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>
+18
View File
@@ -111,3 +111,21 @@ export const batchAllocateBedsToCollege = (data: {
data: data
});
};
/**
* 批量分配床位给班级
* @param data 批量分配给班级参数
*/
export const batchAllocateBedsToClass = (data: {
bedIds: (string | number)[];
classIds: (string | number)[];
classStudentCounts?: number[];
buildingId?: string | number;
buildingType?: string;
}) => {
return request({
url: '/dormBedStudent/dormBedStudent/batchAllocateToClass',
method: 'post',
data: data
});
};
+12 -12
View File
@@ -132,7 +132,7 @@
<template #header>
<el-row>
<el-col :span="12">
<el-button type="primary" icon="check" size="small" @click="handleAddRoom">分配给二级学院</el-button>
<el-button type="primary" icon="check" size="small" @click="handleAddRoom">分配给班级</el-button>
<el-button type="warning" icon="RefreshLeft" size="small" @click="handleBatchRecall">批量收回宿舍</el-button>
</el-col>
<el-col :span="12" style="text-align: right">
@@ -340,11 +340,11 @@
/>
<!-- 学院分配对话框 -->
<college-allocation-dialog
v-model="collegeAllocationDialog.visible"
:title="collegeAllocationDialog.title"
:selected-beds="collegeAllocationDialog.selectedBeds"
:building-info="collegeAllocationDialog.buildingInfo"
<class-allocation-dialog
v-model="classAllocationDialog.visible"
:title="classAllocationDialog.title"
:selected-beds="classAllocationDialog.selectedBeds"
:building-info="classAllocationDialog.buildingInfo"
@success="handleCollegeAllocationSuccess"
/>
</div>
@@ -368,7 +368,7 @@ import ElMessageBox from 'element-plus/es/components/message-box/index';
// 引入子组件
import RoomDialog from './components/RoomDialog.vue';
import BedDialog from './components/BedDialog.vue';
import CollegeAllocationDialog from './components/CollegeAllocationDialog.vue';
import ClassAllocationDialog from './components/ClassAllocationDialog.vue';
import { batchRecallDormBeds } from '@/api/xg/xgss/dormBedStudent/dormBedStudent';
// const { proxy } = getCurrentInstance();
@@ -449,9 +449,9 @@ const bedDialog = reactive({
});
// 学院分配对话框
const collegeAllocationDialog = reactive({
const classAllocationDialog = reactive({
visible: false,
title: '分配床位给二级学院',
title: '分配床位给班级',
selectedBeds: [] as any[],
buildingInfo: undefined as DormTreeNode | undefined
});
@@ -668,9 +668,9 @@ const handleAddRoom = () => {
}
// 准备分配对话框数据
collegeAllocationDialog.selectedBeds = selectedBeds;
collegeAllocationDialog.buildingInfo = selectedBuilding.value;
collegeAllocationDialog.visible = true;
classAllocationDialog.selectedBeds = selectedBeds;
classAllocationDialog.buildingInfo = selectedBuilding.value;
classAllocationDialog.visible = true;
};
/** 批量收回宿舍 */