From aba267170a0c8f103da05d88f4d5dc08eb66d7ea Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Wed, 13 Aug 2025 17:21:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../XgDormBedStudentController.java | 11 ++ .../org/dromara/xg/domain/XgStudentMsg.java | 2 +- .../dromara/xg/domain/XgStudentMsgBatch.java | 2 +- .../xg/domain/bo/XgDormBedBatchRecallBo.java | 23 ++++ .../xg/domain/bo/XgStudentMsgBatchBo.java | 2 +- .../dromara/xg/domain/bo/XgStudentMsgBo.java | 2 +- .../xg/domain/vo/XgStudentMsgBatchVo.java | 2 +- .../dromara/xg/domain/vo/XgStudentMsgVo.java | 2 +- .../xg/service/IXgDormBedStudentService.java | 10 ++ .../impl/XgDormBedStudentServiceImpl.java | 70 ++++++++++++ docs/开发文档/API/宿舍房间管理API.md | 41 ++++++- .../dormBedStudent/dormBedStudent/index.ts | 11 ++ web/src/views/xg/xgss/xsssfpejxy/index.vue | 103 +++++++++++++++++- 13 files changed, 269 insertions(+), 12 deletions(-) create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormBedBatchRecallBo.java 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 78eeedf8..f7deb169 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 @@ -22,6 +22,7 @@ import org.dromara.xg.domain.bo.XgDormBedStudentBo; import org.dromara.xg.domain.bo.XgDormBedBatchAllocateBo; import org.dromara.xg.service.IXgDormBedStudentService; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo; /** * 床位学生分配关系 @@ -115,4 +116,14 @@ public class XgDormBedStudentController extends BaseController { return toAjax(xgDormBedStudentService.batchAllocateBedsToCollege(bo)); } + /** + * 批量收回宿舍床位 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:edit") + @Log(title = "批量收回宿舍床位", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/batchRecall") + public R batchRecallBeds(@Validated(AddGroup.class) @RequestBody XgDormBedBatchRecallBo bo) { + return toAjax(xgDormBedStudentService.batchRecallBeds(bo)); + } } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsg.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsg.java index f0eb3310..6ad69c43 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsg.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsg.java @@ -516,7 +516,7 @@ public class XgStudentMsg extends TenantEntity { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 - 床位id */ private String extField1; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsgBatch.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsgBatch.java index 8c975ffa..06a3697c 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsgBatch.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsgBatch.java @@ -516,7 +516,7 @@ public class XgStudentMsgBatch extends TenantEntity { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 - 床位id */ private String extField1; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormBedBatchRecallBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormBedBatchRecallBo.java new file mode 100644 index 00000000..dde9b7d4 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormBedBatchRecallBo.java @@ -0,0 +1,23 @@ +package org.dromara.xg.domain.bo; + +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; + +/** + * 床位批量收回业务对象 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class XgDormBedBatchRecallBo extends BaseEntity { + + /** + * 床位ID列表 + */ + @NotEmpty(message = "床位ID列表不能为空", groups = { AddGroup.class }) + private List bedIds; +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBatchBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBatchBo.java index 5a8a042c..e110bd17 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBatchBo.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBatchBo.java @@ -517,7 +517,7 @@ public class XgStudentMsgBatchBo extends BaseEntity { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 扩展字段1 - 床位id */ private String extField1; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBo.java index 3a260d47..20eb3ff5 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBo.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBo.java @@ -517,7 +517,7 @@ public class XgStudentMsgBo extends BaseEntity { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 扩展字段1 - 床位id */ private String extField1; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgBatchVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgBatchVo.java index 528d3273..83c5c17c 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgBatchVo.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgBatchVo.java @@ -623,7 +623,7 @@ public class XgStudentMsgBatchVo implements Serializable { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 扩展字段1 - 床位id */ @ExcelProperty(value = "扩展字段1") private String extField1; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgVo.java index 7ec84a4c..57f92e09 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgVo.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgVo.java @@ -623,7 +623,7 @@ public class XgStudentMsgVo implements Serializable { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 扩展字段1 - 床位id */ @ExcelProperty(value = "扩展字段1") private String extField1; 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 83aa1784..ba031671 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 @@ -9,6 +9,8 @@ import org.dromara.common.mybatis.core.page.PageQuery; import java.util.Collection; import java.util.List; +import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo; + /** * 床位学生分配关系Service接口 * @@ -75,4 +77,12 @@ public interface IXgDormBedStudentService { */ Boolean batchAllocateBedsToCollege( XgDormBedBatchAllocateBo bo); + + /** + * 批量收回宿舍床位 + * + * @param bo 批量收回请求参数 + * @return 是否成功 + */ + Boolean batchRecallBeds(XgDormBedBatchRecallBo bo); } 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 c3b59f55..11cb945c 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 @@ -31,6 +31,12 @@ import java.util.Collection; import java.util.Date; import java.util.ArrayList; +import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo; +import org.dromara.xg.mapper.XgStudentMsgMapper; +import org.dromara.xg.domain.XgStudentMsg; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.dromara.common.satoken.utils.LoginHelper; + /** * 床位学生分配关系Service业务层处理 * @@ -46,6 +52,7 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { private final XgDormBedMapper xgDormBedMapper; private final XgDormBuildingMapper xgDormBuildingMapper; private final XgDormRoomMapper xgDormRoomMapper; + private final XgStudentMsgMapper xgStudentMsgMapper; /** * 查询床位学生分配关系 @@ -421,4 +428,67 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { allocationRecords.add(allocation); } } + + /** + * 批量收回宿舍床位 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchRecallBeds(XgDormBedBatchRecallBo bo) { + Date now = new Date(); + Long loginUserId = LoginHelper.getUserId(); + + // 1) 查出这些床位当前有效的分配关系(status=0, deleted=0) + LambdaQueryWrapper activeAssignQuery = Wrappers.lambdaQuery(); + activeAssignQuery.in(XgDormBedStudent::getBedId, bo.getBedIds()) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0"); + List activeAssignments = baseMapper.selectList(activeAssignQuery); + + if (activeAssignments.isEmpty()) { + return true; // 没有需要处理的数据 + } + + // 2) 更新分配关系:status=1(已退宿), checkout_time=now, update_time=now, + // update_by=loginUserId + LambdaUpdateWrapper updateAssign = Wrappers.lambdaUpdate(); + updateAssign.in(XgDormBedStudent::getBedId, bo.getBedIds()) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .set(XgDormBedStudent::getStatus, "1") + .set(XgDormBedStudent::getCheckoutTime, now) + .set(XgDormBedStudent::getUpdateTime, now) + .set(XgDormBedStudent::getUpdateBy, loginUserId); + baseMapper.update(null, updateAssign); + + // 3) 更新床位:bed_status=0(空闲), update_time=now, update_by=loginUserId + LambdaUpdateWrapper updateBed = Wrappers.lambdaUpdate(); + updateBed.in(XgDormBed::getId, bo.getBedIds()) + .eq(XgDormBed::getDeleted, "0") + .set(XgDormBed::getBedStatus, "0") + .set(XgDormBed::getUpdateTime, now) + .set(XgDormBed::getUpdateBy, loginUserId); + xgDormBedMapper.update(null, updateBed); + + // 4) 更新学生信息:找到受影响的studentId,设置 student_status=1,清空 + // ext_field1/dormitory_room/dormitory_building + Set affectedStudentIds = activeAssignments.stream() + .map(XgDormBedStudent::getStudentId) + .filter(java.util.Objects::nonNull) + .collect(java.util.stream.Collectors.toSet()); + if (!affectedStudentIds.isEmpty()) { + LambdaUpdateWrapper updateStudent = Wrappers.lambdaUpdate(); + updateStudent.in(XgStudentMsg::getId, affectedStudentIds) + .eq(XgStudentMsg::getDeleted, 0L) + .set(XgStudentMsg::getStudentStatus, "1") + .set(XgStudentMsg::getExtField1, null) + .set(XgStudentMsg::getDormitoryRoom, null) + .set(XgStudentMsg::getDormitoryBuilding, null) + .set(XgStudentMsg::getUpdateTime, now) + .set(XgStudentMsg::getUpdateBy, loginUserId); + xgStudentMsgMapper.update(null, updateStudent); + } + + return true; + } } diff --git a/docs/开发文档/API/宿舍房间管理API.md b/docs/开发文档/API/宿舍房间管理API.md index 185255f5..b60ce95e 100644 --- a/docs/开发文档/API/宿舍房间管理API.md +++ b/docs/开发文档/API/宿舍房间管理API.md @@ -11,6 +11,7 @@ - [4. 宿舍床位管理](#4-宿舍床位管理) - [4.1 获取房间床位详细信息列表](#41-获取房间床位详细信息列表) - [4.2 获取宿舍楼床位详细信息列表](#42-获取宿舍楼床位详细信息列表) + - [4.3 批量收回宿舍床位](#43-批量收回宿舍床位) ## 1. 宿舍楼树形结构 @@ -678,4 +679,42 @@ async function loadBedDetailsByBuilding(buildingId) { 1. **宿舍管理**:管理员查看房间或宿舍楼的床位分配情况 2. **学生信息核查**:快速查看入住学生的基本信息 3. **统计分析**:进行床位利用率、学院分布等数据分析 -4. **报表生成**:生成宿舍入住情况报表 \ No newline at end of file +4. **报表生成**:生成宿舍入住情况报表 + +### 4.3 批量收回宿舍床位 + +#### 接口说明 +将选中的床位从当前分配状态恢复为空闲状态,并记录退宿时间等信息。 + +#### 请求方式 +``` +POST /dormBedStudent/dormBedStudent/batchRecall +``` + +#### 权限要求 +``` +dormBedStudent:dormBedStudent:edit +``` + +#### 请求参数 +| 参数名 | 类型 | 必须 | 说明 | +|--------|------|------|------| +| bedIds | Long[] | 是 | 要收回的床位ID列表 | + +示例: +```json +{ "bedIds": [1001, 1002, 1003] } +``` + +#### 处理逻辑 +- 更新 `xg_dorm_bed_student`:按 `bed_id`、`status=0`、`deleted=0` 命中记录,设置 `status=1(已退宿)`、`checkout_time=当前时间`、`update_time=当前时间`、`update_by=当前用户`。 +- 更新 `xg_dorm_bed`:按 `id in bedIds` 且 `deleted=0`,设置 `bed_status=0(空闲)`、`update_time=当前时间`、`update_by=当前用户`。 +- 更新 `xg_student_msg`:根据上述分配记录中的 `student_id` 汇总(过滤空),且 `deleted=0`,设置 `student_status=1`,并将 `ext_field1`、`dormitory_room`、`dormitory_building` 置空,同时更新 `update_time`、`update_by`。 + +#### 返回结果 +```json +{ + "code": 200, + "msg": "操作成功" +} +``` \ No newline at end of file diff --git a/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/index.ts b/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/index.ts index e3e12c50..59b286bb 100644 --- a/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/index.ts +++ b/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/index.ts @@ -61,3 +61,14 @@ export const delDormBedStudent = (id: string | number | Array) method: 'delete' }); }; + +/** + * 批量收回宿舍床位 + */ +export const batchRecallDormBeds = (data: { bedIds: Array }) => { + return request({ + url: '/dormBedStudent/dormBedStudent/batchRecall', + method: 'post', + data + }); +}; diff --git a/web/src/views/xg/xgss/xsssfpejxy/index.vue b/web/src/views/xg/xgss/xsssfpejxy/index.vue index dae7d637..51cab0c8 100644 --- a/web/src/views/xg/xgss/xsssfpejxy/index.vue +++ b/web/src/views/xg/xgss/xsssfpejxy/index.vue @@ -133,6 +133,27 @@ 分配给二级学院 + 批量收回宿舍 + + + 全部展开 + 全部收起 @@ -170,7 +191,8 @@ ([]); +// 加载状态:展开/收起/选择 +const expandAllLoading = ref(false); +const collapseAllLoading = ref(false); +const selectionLoading = ref(false); + +// 小延迟,确保加载动画先渲染 +const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + // 选择状态 const selectedRows = ref([]); const selectedRoomCount = ref(0); @@ -643,6 +674,61 @@ const handleAddRoom = () => { collegeAllocationDialog.visible = true; }; +/** 批量收回宿舍 */ +const handleBatchRecall = async () => { + if (!selectedBuilding.value) { + ElMessage.warning('请先选择宿舍楼'); + return; + } + const selectedBeds = selectedRows.value.filter((row) => row.isBed).map((row) => row.id); + if (selectedBeds.length === 0) { + ElMessage.warning('请先选择要收回的床位'); + return; + } + try { + const content = `确认收回选中的 ${selectedBeds.length} 个床位吗?\n\n重要提示:回收后,学生已分配的宿舍都会回收,无法恢复`; + await ElMessageBox.confirm(content, '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }); + } catch { + return; + } + try { + await batchRecallDormBeds({ bedIds: selectedBeds }); + ElMessage.success('批量收回成功'); + // 刷新数据 + selectableRowsCache.value = []; + loadRoomData(); + } catch (e) { + ElMessage.error('批量收回失败'); + console.error(e); + } +}; + +/** 全部展开 */ +const expandAll = async () => { + if (expandAllLoading.value || selectionLoading.value || !roomList.value?.length) return; + expandAllLoading.value = true; + await nextTick(); + await delay(16); + expandedRowKeys.value = roomList.value.map((room) => String(room.id)); + await nextTick(); + expandAllLoading.value = false; +}; + +/** 全部收起 */ +const collapseAll = async () => { + if (collapseAllLoading.value || selectionLoading.value) return; + collapseAllLoading.value = true; + await nextTick(); + await delay(16); + expandedRowKeys.value = []; + await nextTick(); + collapseAllLoading.value = false; +}; + /** 编辑房间 */ const handleEditRoom = (row: any) => { roomDialog.title = '编辑房间'; @@ -741,13 +827,13 @@ const handleCollegeAllocationSuccess = () => { /** 处理行的展开/收起 */ const handleExpandChange = (row: any, expanded: boolean) => { - const rowId = String(row.roomId); + const expandKey = String(row.id); if (expanded) { // 添加到已展开行数组 - expandedRowKeys.value.push(rowId); + expandedRowKeys.value.push(expandKey); } else { // 从已展开行数组中移除 - const index = expandedRowKeys.value.findIndex((key) => key === rowId); + const index = expandedRowKeys.value.findIndex((key) => key === expandKey); if (index !== -1) { expandedRowKeys.value.splice(index, 1); } @@ -854,7 +940,10 @@ const getAllSelectableRows = () => { const selectableRowsCache = ref([]); /** 处理全选事件 */ -const handleSelectAll = (selection: any[]) => { +const handleSelectAll = async (selection: any[]) => { + if (selectionLoading.value) return; + + selectionLoading.value = true; // 避免在全选操作时触发联动逻辑 isUpdatingSelection.value = true; @@ -862,6 +951,9 @@ const handleSelectAll = (selection: any[]) => { // 判断是全选还是取消全选 const isSelectAll = selection.length > 0; + await nextTick(); + await delay(16); + if (isSelectAll) { console.log('================>全选0'); // 全选操作:选择所有可用的行(包括房间和床位) @@ -892,6 +984,7 @@ const handleSelectAll = (selection: any[]) => { } } finally { isUpdatingSelection.value = false; + selectionLoading.value = false; } };