diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedController.java index 9bb630a7..f3063c1f 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedController.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedController.java @@ -139,4 +139,19 @@ public class XgDormBedController extends BaseController { List list = xgDormBedService.queryBedDetailsByBuildingId(buildingId); return R.ok(list); } + + /** + * 查询指定房间中属于指定班级的床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + * @param classId 班级ID + */ + @SaCheckPermission("dormbed:dormBed:query") + @GetMapping("/details/room/{roomId}/class/{classId}") + public R> getBedDetailsByRoomIdAndClassId( + @NotNull(message = "房间ID不能为空") @PathVariable Long roomId, + @NotNull(message = "班级ID不能为空") @PathVariable Long classId) { + List list = xgDormBedService.queryBedDetailsByRoomIdAndClassId(roomId, classId); + return R.ok(list); + } } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedStudentController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedStudentController.java index a0bb851e..400a77f8 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedStudentController.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedStudentController.java @@ -25,6 +25,7 @@ import org.dromara.xg.service.IXgDormBedStudentService; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo; import org.dromara.xg.domain.vo.DormBuildingTreeVo; +import org.dromara.xg.domain.bo.XgDormSelectiveAllocationBo; /** * 床位学生分配关系 @@ -228,7 +229,7 @@ public class XgDormBedStudentController extends BaseController { @Log(title = "班主任选择性分配床位", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping("/classTeacherSelectiveAllocation") - public R classTeacherSelectiveAllocation(@RequestBody Object allocationParams, + public R classTeacherSelectiveAllocation(@Validated @RequestBody XgDormSelectiveAllocationBo allocationParams, @RequestParam(required = false) Long classId) { return toAjax(xgDormBedStudentService.classTeacherSelectiveAllocation(allocationParams, classId)); } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormRoomController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormRoomController.java index 57cf6edb..a6a8337d 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormRoomController.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormRoomController.java @@ -54,6 +54,18 @@ public class XgDormRoomController extends BaseController { return xgDormRoomService.queryPageListByCurrentUserCollege(bo, pageQuery); } + /** + * 查询指定班级的宿舍房间信息列表 + */ + @SaCheckPermission("xgss:dormRoom:list") + @GetMapping("/class/{classId}/list") + public TableDataInfo classRoomList( + @NotNull(message = "班级ID不能为空") @PathVariable Long classId, + XgDormRoomBo bo, + PageQuery pageQuery) { + return xgDormRoomService.queryPageListByClassId(bo, pageQuery, classId); + } + /** * 导出宿舍房间信息列表 */ diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormSelectiveAllocationBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormSelectiveAllocationBo.java new file mode 100644 index 00000000..50950274 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormSelectiveAllocationBo.java @@ -0,0 +1,35 @@ +package org.dromara.xg.domain.bo; + +import lombok.Data; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotBlank; + +import java.util.List; + +/** + * 班主任选择性分配床位请求参数 + * + * @author zlwu + * @date 2025-01-27 + */ +@Data +public class XgDormSelectiveAllocationBo { + + /** + * 选择的床位ID数组 + */ + @NotEmpty(message = "床位ID列表不能为空") + private List bedIds; + + /** + * 选择的学生ID数组 + */ + @NotEmpty(message = "学生ID列表不能为空") + private List studentIds; + + /** + * 分配类型:random-随机分配,studentNo-按学号分配 + */ + @NotBlank(message = "分配类型不能为空") + private String allocationType; +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java index f1a4e2ed..382c3b6b 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java @@ -188,4 +188,63 @@ public interface XgDormBedMapper extends BaseMapperPlus List selectBedDetailsByRoomIdAndCollegeId(@Param("roomId") Long roomId, @Param("collegeId") Long collegeId); + /** + * 查询指定房间中属于指定班级的床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + * @param classId 班级ID + * @return 床位详细信息列表 + */ + @Select(""" + SELECT + b.id, + b.bed_code, + b.bed_name, + b.room_id, + r.room_name, + b.bed_type, + b.bed_status, + b.remark as bed_remark, + b.create_by, + b.create_time, + b.update_by, + b.update_time, + + bs.id as assignment_id, + bs.assign_time, + bs.status as assignment_status, + + bs.college_id, + college_dept.dept_name as college_name, + + bs.major_id, + major_dept.dept_name as major_name, + + bs.class_id, + class_dept.dept_name as class_name, + c.class_code, + c.grade, + + bs.student_id, + s.student_no, + s.name as student_name, + s.gender as student_gender, + s.phone as student_phone, + s.id_card as student_id_card + FROM xg_dorm_bed b + LEFT JOIN xg_dorm_room r ON b.room_id = r.id + INNER JOIN xg_dorm_bed_student bs ON b.id = bs.bed_id AND bs.status = '0' AND bs.deleted = '0' + LEFT JOIN sys_dept college_dept ON bs.college_id = college_dept.dept_id AND college_dept.del_flag = '0' + LEFT JOIN sys_dept major_dept ON bs.major_id = major_dept.dept_id AND major_dept.del_flag = '0' + LEFT JOIN sys_dept class_dept ON bs.class_id = class_dept.dept_id AND class_dept.del_flag = '0' + LEFT JOIN xg_class_msg c ON bs.class_id = c.id AND c.deleted = 0 + LEFT JOIN xg_student_msg s ON bs.student_id = s.id AND s.deleted = 0 + WHERE b.room_id = #{roomId} + AND bs.class_id = #{classId} + AND b.deleted = '0' + ORDER BY b.bed_code + """) + List selectBedDetailsByRoomIdAndClassId(@Param("roomId") Long roomId, + @Param("classId") Long classId); + } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedService.java index 3d04ae40..b3e41219 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedService.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedService.java @@ -90,4 +90,13 @@ public interface IXgDormBedService { * @return 床位详细信息列表 */ List queryBedDetailsByBuildingId(Long buildingId); + + /** + * 查询指定房间中属于指定班级的床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + * @param classId 班级ID + * @return 床位详细信息列表 + */ + List queryBedDetailsByRoomIdAndClassId(Long roomId, Long classId); } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedStudentService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedStudentService.java index 45195394..1ba9971d 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedStudentService.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedStudentService.java @@ -12,6 +12,7 @@ import java.util.List; import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo; import org.dromara.xg.domain.vo.DormBuildingTreeVo; +import org.dromara.xg.domain.bo.XgDormSelectiveAllocationBo; /** * 床位学生分配关系Service接口 @@ -163,7 +164,7 @@ public interface IXgDormBedStudentService { * @param classId 班级ID,可为空 * @return 是否分配成功 */ - Boolean classTeacherSelectiveAllocation(Object allocationParams, Long classId); + Boolean classTeacherSelectiveAllocation(XgDormSelectiveAllocationBo allocationParams, Long classId); /** * 班主任一键收回床位 diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormRoomService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormRoomService.java index c6a4ad3d..f5b8cd41 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormRoomService.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormRoomService.java @@ -42,6 +42,16 @@ public interface IXgDormRoomService { */ TableDataInfo queryPageListByCurrentUserCollege(XgDormRoomBo bo, PageQuery pageQuery); + /** + * 分页查询指定班级的宿舍房间信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @param classId 班级ID + * @return 宿舍房间信息分页列表 + */ + TableDataInfo queryPageListByClassId(XgDormRoomBo bo, PageQuery pageQuery, Long classId); + /** * 查询符合条件的宿舍房间信息列表 * diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedServiceImpl.java index aea0cec6..76221a79 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedServiceImpl.java @@ -191,6 +191,18 @@ public class XgDormBedServiceImpl implements IXgDormBedService { return baseMapper.selectBedDetailsByBuildingId(buildingId); } + /** + * 查询指定房间中属于指定班级的床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + * @param classId 班级ID + * @return 床位详细信息列表 + */ + @Override + public List queryBedDetailsByRoomIdAndClassId(Long roomId, Long classId) { + return baseMapper.selectBedDetailsByRoomIdAndClassId(roomId, classId); + } + /** * 获取当前用户的二级学院ID * 通过当前用户的部门id,向上寻找包括他自己,找到dept_category前几位是xy-的就是二级学院 diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java index 2a584487..5a5409f3 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java @@ -30,8 +30,12 @@ import java.util.Set; import java.util.Collection; import java.util.Date; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Objects; +import java.util.Collections; import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo; +import org.dromara.xg.domain.bo.XgDormSelectiveAllocationBo; import org.dromara.xg.mapper.XgStudentMsgMapper; import org.dromara.xg.domain.XgStudentMsg; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; @@ -48,6 +52,8 @@ import java.util.LinkedHashMap; import org.dromara.xg.domain.bo.XgDormBedBatchAllocateToClassBo; import org.dromara.xg.mapper.XgClassMsgMapper; import org.dromara.xg.domain.XgClassMsg; +import org.dromara.xg.domain.vo.XgDormBedDetailVo; +import org.dromara.xg.domain.bo.XgDormSelectiveAllocationBo; /** * 床位学生分配关系Service业务层处理 @@ -1188,8 +1194,70 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { public List getClassTeacherBedDetails(Long buildingId, Long classId) { try { Long targetClassId = classId != null ? classId : getCurrentUserClassId(); + if (targetClassId == null) { + log.warn("无法获取班级信息,请确认用户是否为班主任或传入正确的班级ID"); + return new ArrayList<>(); + } + log.info("获取班主任管理班级的房间床位详情,宿舍楼ID: {},classId:{}", buildingId, targetClassId); - return new ArrayList<>(); + + // 1. 构建房间查询条件 + LambdaQueryWrapper roomQuery = Wrappers.lambdaQuery(); + roomQuery.eq(XgDormRoom::getDeleted, "0") + .eq(XgDormRoom::getRoomStatus, "0"); + + if (buildingId != null) { + roomQuery.eq(XgDormRoom::getBuildingId, buildingId); + } + + // 2. 获取该班级分配到的房间ID列表 + LambdaQueryWrapper bedStudentQuery = Wrappers.lambdaQuery(); + bedStudentQuery.select(XgDormBedStudent::getRoomId) + .eq(XgDormBedStudent::getClassId, targetClassId) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNotNull(XgDormBedStudent::getRoomId) + .groupBy(XgDormBedStudent::getRoomId); + + List roomRecords = baseMapper.selectList(bedStudentQuery); + Set allocatedRoomIds = roomRecords.stream() + .map(XgDormBedStudent::getRoomId) + .collect(Collectors.toSet()); + + if (allocatedRoomIds.isEmpty()) { + log.info("班级 {} 没有分配的房间", targetClassId); + return new ArrayList<>(); + } + + // 3. 查询这些房间的信息 + roomQuery.in(XgDormRoom::getId, allocatedRoomIds); + List rooms = xgDormRoomMapper.selectList(roomQuery); + + // 4. 为每个房间构建详细信息(房间+床位) + List result = new ArrayList<>(); + for (XgDormRoom room : rooms) { + // 查询该房间中属于指定班级的床位详细信息 + List bedDetails = xgDormBedMapper.selectBedDetailsByRoomIdAndClassId(room.getId(), + targetClassId); + + // 构建房间记录 + Map roomRecord = new HashMap<>(); + roomRecord.put("id", room.getId()); + roomRecord.put("roomName", room.getRoomName()); + roomRecord.put("buildingId", room.getBuildingId()); + roomRecord.put("floorNum", room.getFloorNum()); + roomRecord.put("roomType", room.getRoomType()); + roomRecord.put("roomStatus", room.getRoomStatus()); + roomRecord.put("remark", room.getRemark()); + roomRecord.put("isBed", false); + roomRecord.put("children", bedDetails); + + result.add(roomRecord); + } + + log.info("成功获取班级 {} 的房间床位详情,共 {} 个房间", targetClassId, result.size()); + return result; + } catch (Exception e) { log.error("获取班主任管理班级的房间床位详情失败", e); return new ArrayList<>(); @@ -1199,16 +1267,80 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { /** * 获取班主任管理班级的未分配学生列表 * + * @param classId 班级ID,为空时使用当前用户管理的班级 * @return 未分配学生列表 */ @Override public List getClassTeacherUnallocatedStudents(Long classId) { try { Long targetClassId = classId != null ? classId : getCurrentUserClassId(); - log.info("获取班主任管理班级的未分配学生列表,classId:{}", targetClassId); - return new ArrayList<>(); + if (targetClassId == null) { + log.warn("无法获取班级信息,请确认用户是否为班主任或传入正确的班级ID"); + return new ArrayList<>(); + } + + log.info("获取班主任管理班级的未分配学生列表,classId: {}", targetClassId); + + // 1. 查询该班级所有需要住校的学生 + LambdaQueryWrapper studentQuery = Wrappers.lambdaQuery(); + studentQuery.eq(XgStudentMsg::getStudentStatus, "1") // 学生状态为正常 + .eq(XgStudentMsg::getDeleted, 0L) // 未删除 + .eq(XgStudentMsg::getIsBoarder, "1") // 需要住校 + .eq(XgStudentMsg::getClassId, targetClassId) // 指定班级 + .orderByAsc(XgStudentMsg::getStudentNo); // 按学号排序 + + List boardingStudents = xgStudentMsgMapper.selectList(studentQuery); + + if (boardingStudents.isEmpty()) { + log.info("班级 {} 没有需要住校的学生", targetClassId); + return new ArrayList<>(); + } + + log.info("班级 {} 共有 {} 名学生需要住校", targetClassId, boardingStudents.size()); + + // 2. 查询已经分配床位的学生ID列表 + LambdaQueryWrapper bedQuery = Wrappers.lambdaQuery(); + bedQuery.select(XgDormBedStudent::getStudentId) + .eq(XgDormBedStudent::getClassId, targetClassId) // 指定班级 + .eq(XgDormBedStudent::getStatus, "0") // 正常状态(未退宿) + .eq(XgDormBedStudent::getDeleted, "0") // 未删除 + .isNotNull(XgDormBedStudent::getStudentId); // 学生ID不为空 + + List allocatedBedRecords = baseMapper.selectList(bedQuery); + Set allocatedStudentIds = allocatedBedRecords.stream() + .map(XgDormBedStudent::getStudentId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + log.info("班级 {} 已分配床位的学生数量: {}", targetClassId, allocatedStudentIds.size()); + + // 3. 计算未分配床位的学生(需要住校但未分配床位的学生) + List unallocatedStudents = boardingStudents.stream() + .filter(student -> !allocatedStudentIds.contains(student.getId())) + .collect(Collectors.toList()); + + log.info("班级 {} 统计结果 - 总住校学生: {}人,已分配床位: {}人,未分配床位: {}人", + targetClassId, boardingStudents.size(), allocatedStudentIds.size(), unallocatedStudents.size()); + + // 4. 转换为合适的返回格式,包含学生基本信息 + return unallocatedStudents.stream() + .map(student -> { + Map studentInfo = new HashMap<>(); + studentInfo.put("id", student.getId()); + studentInfo.put("studentNo", student.getStudentNo()); + studentInfo.put("studentName", student.getName()); + studentInfo.put("gender", student.getGender()); + studentInfo.put("classId", student.getClassId()); + studentInfo.put("majorId", student.getMajorId()); + studentInfo.put("collegeId", student.getCollegeId()); + studentInfo.put("phone", student.getPhone()); + studentInfo.put("idCard", student.getIdCard()); + return studentInfo; + }) + .collect(Collectors.toList()); + } catch (Exception e) { - log.error("获取班主任管理班级的未分配学生列表失败", e); + log.error("获取班主任管理班级的未分配学生列表失败,classId: {}", classId, e); return new ArrayList<>(); } } @@ -1261,18 +1393,341 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { */ @Override @Transactional(rollbackFor = Exception.class) - public Boolean classTeacherSelectiveAllocation(Object allocationParams, Long classId) { + public Boolean classTeacherSelectiveAllocation(XgDormSelectiveAllocationBo allocationParams, Long classId) { try { Long targetClassId = classId != null ? classId : getCurrentUserClassId(); - // TODO: 实现班主任选择性分配床位 log.info("班主任选择性分配床位,classId:{},分配参数: {}", targetClassId, allocationParams); + + List bedIds = allocationParams.getBedIds(); + List studentIds = allocationParams.getStudentIds(); + String allocationType = allocationParams.getAllocationType(); + + // 参数校验 + if (bedIds == null || bedIds.isEmpty()) { + throw new RuntimeException("床位ID列表不能为空"); + } + if (studentIds == null || studentIds.isEmpty()) { + throw new RuntimeException("学生ID列表不能为空"); + } + if (bedIds.size() < studentIds.size()) { + throw new RuntimeException("床位数量不能少于学生数量"); + } + + Date now = new Date(); + Long loginUserId = LoginHelper.getUserId(); + + // 1. 验证床位状态 - 确保所有床位都是空闲且有效的 + LambdaQueryWrapper bedQuery = Wrappers.lambdaQuery(); + bedQuery.in(XgDormBed::getId, bedIds) + .eq(XgDormBed::getDeleted, "0"); + List availableBeds = xgDormBedMapper.selectList(bedQuery); + + if (availableBeds.size() != bedIds.size()) { + throw new RuntimeException("部分床位不存在或已被删除"); + } + + // 检查床位是否已被学生占用(床位已分配给学院/班级,但student_id有值表示被学生占用) + LambdaQueryWrapper existingQuery = Wrappers.lambdaQuery(); + existingQuery.in(XgDormBedStudent::getBedId, bedIds) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNotNull(XgDormBedStudent::getStudentId); // 检查是否已有学生占用 + List existingAllocations = baseMapper.selectList(existingQuery); + + if (!existingAllocations.isEmpty()) { + // 找出已被占用的床位名称 + Set occupiedBedIds = existingAllocations.stream() + .map(XgDormBedStudent::getBedId) + .collect(Collectors.toSet()); + List occupiedBedNames = availableBeds.stream() + .filter(bed -> occupiedBedIds.contains(bed.getId())) + .map(XgDormBed::getBedName) + .collect(Collectors.toList()); + throw new RuntimeException("部分床位已被学生占用:" + String.join("、", occupiedBedNames) + ",请刷新后重试"); + } + + // 2. 验证学生状态 - 确保所有学生都未分配床位 + LambdaQueryWrapper studentQuery = Wrappers.lambdaQuery(); + studentQuery.in(XgStudentMsg::getId, studentIds) + .eq(XgStudentMsg::getDeleted, 0L); + List students = xgStudentMsgMapper.selectList(studentQuery); + + if (students.size() != studentIds.size()) { + throw new RuntimeException("部分学生不存在或已被删除"); + } + + // 检查学生是否已分配床位(通过dormitory_building、dormitory_room、ext_field1三个字段判断) + List alreadyAllocatedStudents = students.stream() + .filter(student -> (student.getDormitoryBuilding() != null + && !student.getDormitoryBuilding().trim().isEmpty()) || + (student.getDormitoryRoom() != null && !student.getDormitoryRoom().trim().isEmpty()) || + (student.getExtField1() != null && !student.getExtField1().trim().isEmpty())) + .collect(Collectors.toList()); + + if (!alreadyAllocatedStudents.isEmpty()) { + String allocatedStudentNames = alreadyAllocatedStudents.stream() + .map(student -> student.getName() + "(" + student.getStudentNo() + ")") + .collect(Collectors.joining("、")); + throw new RuntimeException("学生 " + allocatedStudentNames + " 已分配宿舍,请刷新后重试"); + } + + // 3. 根据分配类型进行床位分配 + List finalBedIds; + List finalStudentIds; + + if ("random".equals(allocationType)) { + // 随机分配:打乱床位顺序 + finalBedIds = new ArrayList<>(bedIds); + Collections.shuffle(finalBedIds); + finalStudentIds = new ArrayList<>(studentIds); + } else if ("studentNo".equals(allocationType)) { + // 按学号分配:学生按学号排序,床位按名称排序 + Map studentNoMap = students.stream() + .collect(Collectors.toMap(XgStudentMsg::getId, XgStudentMsg::getStudentNo)); + + finalStudentIds = studentIds.stream() + .sorted((id1, id2) -> { + String no1 = studentNoMap.get(id1); + String no2 = studentNoMap.get(id2); + return no1.compareToIgnoreCase(no2); + }) + .collect(Collectors.toList()); + + // 创建床位ID到床位信息的映射 + Map bedInfoMap = availableBeds.stream() + .collect(Collectors.toMap(XgDormBed::getId, bed -> bed)); + + // 创建房间ID到房间名称的映射 + Set roomIds = availableBeds.stream() + .map(XgDormBed::getRoomId) + .collect(Collectors.toSet()); + LambdaQueryWrapper roomQuery = Wrappers.lambdaQuery(); + roomQuery.in(XgDormRoom::getId, roomIds) + .eq(XgDormRoom::getDeleted, "0"); + List rooms = xgDormRoomMapper.selectList(roomQuery); + Map roomNameMap = rooms.stream() + .collect(Collectors.toMap(XgDormRoom::getId, XgDormRoom::getRoomName)); + + finalBedIds = bedIds.stream() + .sorted((id1, id2) -> { + XgDormBed bed1 = bedInfoMap.get(id1); + XgDormBed bed2 = bedInfoMap.get(id2); + String roomName1 = roomNameMap.get(bed1.getRoomId()); + String roomName2 = roomNameMap.get(bed2.getRoomId()); + + // 先按房间名称排序 + int roomCompare = roomName1.compareToIgnoreCase(roomName2); + if (roomCompare != 0) { + return roomCompare; + } + // 房间相同时,再按床位名称排序 + return bed1.getBedName().compareToIgnoreCase(bed2.getBedName()); + }) + .collect(Collectors.toList()); + } else { + throw new RuntimeException("不支持的分配类型:" + allocationType); + } + + // 创建床位-学生映射关系 + Map bedStudentMap = new HashMap<>(); + for (int i = 0; i < finalStudentIds.size(); i++) { + bedStudentMap.put(finalBedIds.get(i), finalStudentIds.get(i)); + } + + // 4. 更新或插入xg_dorm_bed_student表 + Map bedMap = availableBeds.stream() + .collect(Collectors.toMap(XgDormBed::getId, bed -> bed)); + Map studentMap = students.stream() + .collect(Collectors.toMap(XgStudentMsg::getId, student -> student)); + + // 查询现有的床位分配记录(已分配给班级但没有学生的记录) + LambdaQueryWrapper existingBedQuery = Wrappers.lambdaQuery(); + existingBedQuery.in(XgDormBedStudent::getBedId, bedIds) + .eq(XgDormBedStudent::getClassId, targetClassId) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNull(XgDormBedStudent::getStudentId); // 没有学生的记录 + List existingBedRecords = baseMapper.selectList(existingBedQuery); + + // 创建床位ID到现有记录的映射 + Map existingBedMap = existingBedRecords.stream() + .collect(Collectors.toMap(XgDormBedStudent::getBedId, record -> record)); + + List bedStudentAllocations = new ArrayList<>(); + + for (Map.Entry entry : bedStudentMap.entrySet()) { + Long bedId = entry.getKey(); + Long studentId = entry.getValue(); + XgDormBed bed = bedMap.get(bedId); + XgStudentMsg student = studentMap.get(studentId); + + XgDormBedStudent existingRecord = existingBedMap.get(bedId); + if (existingRecord != null) { + // 更新现有记录:添加学生信息 + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); + updateWrapper.eq(XgDormBedStudent::getId, existingRecord.getId()) + .set(XgDormBedStudent::getStudentId, studentId) + .set(XgDormBedStudent::getAssignTime, now) + .set(XgDormBedStudent::getUpdateBy, loginUserId) + .set(XgDormBedStudent::getUpdateTime, now); + baseMapper.update(null, updateWrapper); + } else { + // 创建新的分配记录 + XgDormBedStudent allocation = new XgDormBedStudent(); + allocation.setBedId(bedId); + allocation.setStudentId(studentId); + allocation.setRoomId(bed.getRoomId()); + allocation.setBuildingId(getBuildingIdByBedId(bedId)); + allocation.setAreaId(getAreaIdByBedId(bedId)); + allocation.setCampusId(getCampusIdByBedId(bedId)); + + // 从学生信息获取学院、专业、班级信息 + allocation.setCollegeId(getCollegeIdByClassId(targetClassId)); + allocation.setMajorId(getMajorIdByClassId(targetClassId)); + allocation.setClassId(targetClassId); + + allocation.setStatus("0"); // 0正常 + allocation.setAssignTime(now); + allocation.setCreateBy(loginUserId); + allocation.setCreateTime(now); + allocation.setUpdateBy(loginUserId); + allocation.setUpdateTime(now); + allocation.setDeleted("0"); + + bedStudentAllocations.add(allocation); + } + } + + // 批量插入新的分配关系 + if (!bedStudentAllocations.isEmpty()) { + baseMapper.insertBatch(bedStudentAllocations); + } + + // 5. 批量更新xg_student_msg表 + for (Map.Entry entry : bedStudentMap.entrySet()) { + Long bedId = entry.getKey(); + Long studentId = entry.getValue(); + XgDormBed bed = bedMap.get(bedId); + + LambdaUpdateWrapper updateStudent = Wrappers.lambdaUpdate(); + updateStudent.eq(XgStudentMsg::getId, studentId) + .eq(XgStudentMsg::getDeleted, 0L) + .set(XgStudentMsg::getDormitoryBuilding, getBuildingIdByBedId(bedId).toString()) + .set(XgStudentMsg::getDormitoryRoom, bed.getRoomId().toString()) + .set(XgStudentMsg::getExtField1, bedId.toString()) + .set(XgStudentMsg::getUpdateBy, loginUserId) + .set(XgStudentMsg::getUpdateTime, now); + + xgStudentMsgMapper.update(null, updateStudent); + } + + // 6. 更新床位状态为已分配 + LambdaUpdateWrapper updateBed = Wrappers.lambdaUpdate(); + updateBed.in(XgDormBed::getId, bedStudentMap.keySet()) + .eq(XgDormBed::getDeleted, "0") + .set(XgDormBed::getBedStatus, "1") // 1已分配 + .set(XgDormBed::getUpdateBy, loginUserId) + .set(XgDormBed::getUpdateTime, now); + + xgDormBedMapper.update(null, updateBed); + + log.info("班主任选择性分配床位成功,分配了{}个床位给{}名学生", bedStudentMap.size(), finalStudentIds.size()); return true; + } catch (Exception e) { log.error("班主任选择性分配床位失败", e); throw new RuntimeException("选择性分配床位失败:" + e.getMessage()); } } + /** + * 根据床位ID获取楼栋ID + */ + private Long getBuildingIdByBedId(Long bedId) { + // 通过床位ID查询房间ID,再通过房间ID查询楼栋ID + LambdaQueryWrapper bedQuery = Wrappers.lambdaQuery(); + bedQuery.eq(XgDormBed::getId, bedId).eq(XgDormBed::getDeleted, "0"); + XgDormBed bed = xgDormBedMapper.selectOne(bedQuery); + + if (bed != null && bed.getRoomId() != null) { + LambdaQueryWrapper roomQuery = Wrappers.lambdaQuery(); + roomQuery.eq(XgDormRoom::getId, bed.getRoomId()).eq(XgDormRoom::getDeleted, "0"); + XgDormRoom room = xgDormRoomMapper.selectOne(roomQuery); + + if (room != null) { + return room.getBuildingId(); + } + } + + return null; + } + + /** + * 根据床位ID获取宿舍区ID + */ + private Long getAreaIdByBedId(Long bedId) { + Long buildingId = getBuildingIdByBedId(bedId); + if (buildingId != null) { + LambdaQueryWrapper buildingQuery = Wrappers.lambdaQuery(); + buildingQuery.eq(XgDormBuilding::getId, buildingId).eq(XgDormBuilding::getDeleted, "0"); + XgDormBuilding building = xgDormBuildingMapper.selectOne(buildingQuery); + + if (building != null) { + return building.getAreaId(); + } + } + + return null; + } + + /** + * 根据床位ID获取校区ID + */ + private Long getCampusIdByBedId(Long bedId) { + Long buildingId = getBuildingIdByBedId(bedId); + if (buildingId != null) { + LambdaQueryWrapper buildingQuery = Wrappers.lambdaQuery(); + buildingQuery.eq(XgDormBuilding::getId, buildingId).eq(XgDormBuilding::getDeleted, "0"); + XgDormBuilding building = xgDormBuildingMapper.selectOne(buildingQuery); + + if (building != null) { + return building.getCampusId(); + } + } + + return null; + } + + /** + * 根据班级ID获取学院ID + */ + private Long getCollegeIdByClassId(Long classId) { + LambdaQueryWrapper classQuery = Wrappers.lambdaQuery(); + classQuery.eq(XgClassMsg::getId, classId).eq(XgClassMsg::getDeleted, 0L); + XgClassMsg classMsg = xgClassMsgMapper.selectOne(classQuery); + + if (classMsg != null) { + return classMsg.getCollegeId(); + } + + return null; + } + + /** + * 根据班级ID获取专业ID + */ + private Long getMajorIdByClassId(Long classId) { + LambdaQueryWrapper classQuery = Wrappers.lambdaQuery(); + classQuery.eq(XgClassMsg::getId, classId).eq(XgClassMsg::getDeleted, 0L); + XgClassMsg classMsg = xgClassMsgMapper.selectOne(classQuery); + + if (classMsg != null) { + return classMsg.getMajorId(); + } + + return null; + } + /** * 班主任一键收回床位 * diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java index e6f10971..dddcfed0 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java @@ -98,6 +98,37 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService { return TableDataInfo.build(result); } + /** + * 分页查询指定班级的宿舍房间信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @param classId 班级ID + * @return 宿舍房间信息分页列表 + */ + @Override + public TableDataInfo queryPageListByClassId(XgDormRoomBo bo, PageQuery pageQuery, Long classId) { + // 1. 参数校验 + if (classId == null) { + log.warn("班级ID不能为空,返回空结果"); + return TableDataInfo.build(new Page<>()); + } + + // 2. 获取分配给指定班级的房间ID列表 + Set classAllocatedRoomIds = getClassAllocatedRoomIds(classId); + if (classAllocatedRoomIds.isEmpty()) { + log.info("班级 {} 没有分配的房间,返回空结果", classId); + return TableDataInfo.build(new Page<>()); + } + + // 3. 构建查询条件,只查询分配给指定班级的具体房间,并支持额外的过滤条件 + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + lqw.in(XgDormRoom::getId, classAllocatedRoomIds); + + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + /** * 查询符合条件的宿舍房间信息列表 * @@ -255,4 +286,28 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService { return Set.of(); } } + + /** + * 获取分配给指定班级的房间ID列表 + */ + private Set getClassAllocatedRoomIds(Long classId) { + try { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.select(XgDormBedStudent::getRoomId) + .eq(XgDormBedStudent::getClassId, classId) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNotNull(XgDormBedStudent::getRoomId) + .groupBy(XgDormBedStudent::getRoomId); + + List roomRecords = xgDormBedStudentMapper.selectList(queryWrapper); + return roomRecords.stream() + .map(XgDormBedStudent::getRoomId) + .collect(Collectors.toSet()); + + } catch (Exception e) { + log.error("获取班级 {} 分配的房间ID列表失败", classId, e); + return Set.of(); + } + } } diff --git a/docs/业务模块/学工管理-宿舍管理/README.md b/docs/业务模块/学工管理-宿舍管理/README.md index c0f95ddb..d0ce037c 100644 --- a/docs/业务模块/学工管理-宿舍管理/README.md +++ b/docs/业务模块/学工管理-宿舍管理/README.md @@ -147,4 +147,32 @@ **📞 联系方式**:如有问题或建议,请联系开发团队 **📅 最后更新**:2025-01-14 -**🔄 模块状态**:设计阶段 \ No newline at end of file +**🔄 模块状态**:设计阶段 + +## 🆕 前端功能更新(班主任分配床位给学生) + +- 新增页面:`web/src/views/xg/xgss/bzrfp/bzrfp.vue` + - 班主任可在“我的班级”Tab 中,针对每个班级进行床位分配给学生 + - 左侧:宿舍楼树(通过接口获取,仅宿舍楼节点可选) + - 右侧:房间/床位树表,支持选择空闲床位与状态查看 + - 操作:选择性分配(弹窗)、一键随机分配、按学号分配、一键收回 +- 弹窗组件:`web/src/views/xg/xgss/bzrfp/components/SelectiveAllocationDialog.vue` + - 左侧为可用床位列表,右侧为该班级未分配且有住宿需求的学生列表 + - 支持随机分配/按学号分配,两侧数量需一致 + +- 相关前端API(已存在于相应模块,无需新增后端接口): + - 获取班主任宿舍楼树:`getClassTeacherDormTree(classId)` → `@/api/xg/xgss/dormBedStudent/dormBedStudent` 目录 + - 获取房间床位详情(含学生):`getClassTeacherBedDetails(buildingId, classId)` + - 获取未分配学生列表:`getClassTeacherUnallocatedStudents(classId)` + - 选择性分配:`classTeacherSelectiveAllocation({ bedIds, studentIds, allocationType }, classId)` + - 一键随机分配:`classTeacherRandomAllocation(buildingId, classId)` + - 按学号分配:`classTeacherStudentNoAllocation(buildingId, classId)` + - 一键收回:`classTeacherRecallBeds(buildingId, classId)` + - 获取我管理的班级列表:`getMyClassListAsTeacher()` → `@/api/xg/xsjcxx/classMsg/classMsg` + +- 数据来源说明: + - 分配数据基于 `xg_dorm_bed_student` 关联 `xg_dorm_room`、`xg_student_msg` 实现: + - 通过 `building_id + class_id` 获取该班在楼栋下的房间与床位分配 + - 通过 `student_id` 判定床位是否已入住,并展示学生信息 + +> 本次为前端界面与交互完善,无数据库表结构与后端接口变更。文档同步记录以便后续联调与测试追溯。 \ No newline at end of file diff --git a/web/src/api/xg/xgss/dormBed/index.ts b/web/src/api/xg/xgss/dormBed/index.ts index 2e4cf3f8..b93d289a 100644 --- a/web/src/api/xg/xgss/dormBed/index.ts +++ b/web/src/api/xg/xgss/dormBed/index.ts @@ -94,6 +94,18 @@ export const getBedDetailsByBuildingId = (buildingId: string | number): AxiosPro }); }; +/** + * 查询指定房间中属于指定班级的床位详细信息列表(包含学生信息) + * @param roomId 房间ID + * @param classId 班级ID + */ +export const getBedDetailsByRoomIdAndClassId = (roomId: string | number, classId: string | number): AxiosPromise => { + return request({ + url: `/dormbed/dormBed/details/room/${roomId}/class/${classId}`, + method: 'get' + }); +}; + /** * 批量分配床位给二级学院 * @param data 批量分配参数 diff --git a/web/src/api/xg/xgss/dormRoom/index.ts b/web/src/api/xg/xgss/dormRoom/index.ts index 73e4d137..05581f9c 100644 --- a/web/src/api/xg/xgss/dormRoom/index.ts +++ b/web/src/api/xg/xgss/dormRoom/index.ts @@ -64,6 +64,32 @@ export const updateDormRoom = (data: DormRoomForm) => { }); }; +/** + * 查询指定班级的宿舍房间信息列表 + * @param classId 班级ID + * @param query 查询条件 + * @returns {*} + */ +export const listDormRoomByClassId = (classId: string | number, query?: DormRoomQuery): AxiosPromise => { + return request({ + url: `/xgss/dormRoom/class/${classId}/list`, + method: 'get', + params: query + }); +}; + +/** + * 查询指定房间的床位详细信息列表(包含学生信息) + * @param roomId 房间ID + * @returns {*} + */ +export const getDormBedDetailsByRoomId = (roomId: string | number): AxiosPromise => { + return request({ + url: `/dormbed/dormBed/details/room/${roomId}`, + method: 'get' + }); +}; + /** * 删除宿舍房间信息 * @param id diff --git a/web/src/views/xg/xgss/bzrfp/bzrfp-bak1.vue b/web/src/views/xg/xgss/bzrfp/bzrfp-bak1.vue new file mode 100644 index 00000000..9160364f --- /dev/null +++ b/web/src/views/xg/xgss/bzrfp/bzrfp-bak1.vue @@ -0,0 +1,685 @@ + + + + + diff --git a/web/src/views/xg/xgss/bzrfp/bzrfp.vue b/web/src/views/xg/xgss/bzrfp/bzrfp.vue index 5b22e520..065d967a 100644 --- a/web/src/views/xg/xgss/bzrfp/bzrfp.vue +++ b/web/src/views/xg/xgss/bzrfp/bzrfp.vue @@ -95,9 +95,9 @@