提交
This commit is contained in:
1 parent
66faebd8ac
commit
aba267170a
13 files changed
+269
-12
No files matched your search
+11
@@ -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<Void> batchRecallBeds(@Validated(AddGroup.class) @RequestBody XgDormBedBatchRecallBo bo) {
|
||||
return toAjax(xgDormBedStudentService.batchRecallBeds(bo));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -516,7 +516,7 @@ public class XgStudentMsg extends TenantEntity {
|
||||
private String dormitoryRoom;
|
||||
|
||||
/**
|
||||
* 扩展字段1
|
||||
* 扩展字段1 - 床位id
|
||||
*/
|
||||
private String extField1;
|
||||
|
||||
|
||||
+1
-1
@@ -516,7 +516,7 @@ public class XgStudentMsgBatch extends TenantEntity {
|
||||
private String dormitoryRoom;
|
||||
|
||||
/**
|
||||
* 扩展字段1
|
||||
* 扩展字段1 - 床位id
|
||||
*/
|
||||
private String extField1;
|
||||
|
||||
|
||||
+23
@@ -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<Long> bedIds;
|
||||
}
|
||||
+1
-1
@@ -517,7 +517,7 @@ public class XgStudentMsgBatchBo extends BaseEntity {
|
||||
private String dormitoryRoom;
|
||||
|
||||
/**
|
||||
* 扩展字段1
|
||||
* 扩展字段1 扩展字段1 - 床位id
|
||||
*/
|
||||
private String extField1;
|
||||
|
||||
|
||||
+1
-1
@@ -517,7 +517,7 @@ public class XgStudentMsgBo extends BaseEntity {
|
||||
private String dormitoryRoom;
|
||||
|
||||
/**
|
||||
* 扩展字段1
|
||||
* 扩展字段1 扩展字段1 - 床位id
|
||||
*/
|
||||
private String extField1;
|
||||
|
||||
|
||||
+1
-1
@@ -623,7 +623,7 @@ public class XgStudentMsgBatchVo implements Serializable {
|
||||
private String dormitoryRoom;
|
||||
|
||||
/**
|
||||
* 扩展字段1
|
||||
* 扩展字段1 扩展字段1 - 床位id
|
||||
*/
|
||||
@ExcelProperty(value = "扩展字段1")
|
||||
private String extField1;
|
||||
|
||||
+1
-1
@@ -623,7 +623,7 @@ public class XgStudentMsgVo implements Serializable {
|
||||
private String dormitoryRoom;
|
||||
|
||||
/**
|
||||
* 扩展字段1
|
||||
* 扩展字段1 扩展字段1 - 床位id
|
||||
*/
|
||||
@ExcelProperty(value = "扩展字段1")
|
||||
private String extField1;
|
||||
|
||||
+10
@@ -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);
|
||||
}
|
||||
+70
@@ -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<XgDormBedStudent> activeAssignQuery = Wrappers.lambdaQuery();
|
||||
activeAssignQuery.in(XgDormBedStudent::getBedId, bo.getBedIds())
|
||||
.eq(XgDormBedStudent::getStatus, "0")
|
||||
.eq(XgDormBedStudent::getDeleted, "0");
|
||||
List<XgDormBedStudent> activeAssignments = baseMapper.selectList(activeAssignQuery);
|
||||
|
||||
if (activeAssignments.isEmpty()) {
|
||||
return true; // 没有需要处理的数据
|
||||
}
|
||||
|
||||
// 2) 更新分配关系:status=1(已退宿), checkout_time=now, update_time=now,
|
||||
// update_by=loginUserId
|
||||
LambdaUpdateWrapper<XgDormBedStudent> 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<XgDormBed> 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<Long> affectedStudentIds = activeAssignments.stream()
|
||||
.map(XgDormBedStudent::getStudentId)
|
||||
.filter(java.util.Objects::nonNull)
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
if (!affectedStudentIds.isEmpty()) {
|
||||
LambdaUpdateWrapper<XgStudentMsg> 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;
|
||||
}
|
||||
}
|
||||
@@ -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. **报表生成**:生成宿舍入住情况报表
|
||||
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": "操作成功"
|
||||
}
|
||||
```
|
||||
@@ -61,3 +61,14 @@ export const delDormBedStudent = (id: string | number | Array<string | number>)
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量收回宿舍床位
|
||||
*/
|
||||
export const batchRecallDormBeds = (data: { bedIds: Array<string | number> }) => {
|
||||
return request({
|
||||
url: '/dormBedStudent/dormBedStudent/batchRecall',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
@@ -133,6 +133,27 @@
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<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">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="expandAllLoading"
|
||||
:disabled="expandAllLoading || selectionLoading || loading"
|
||||
@click="expandAll"
|
||||
>全部展开</el-button
|
||||
>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="collapseAllLoading"
|
||||
:disabled="collapseAllLoading || selectionLoading || loading"
|
||||
@click="collapseAll"
|
||||
>全部收起</el-button
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
@@ -170,7 +191,8 @@
|
||||
<!-- 房间及床位树形表格 -->
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
v-loading="loading || selectionLoading || expandAllLoading || collapseAllLoading"
|
||||
element-loading-text="处理中..."
|
||||
:data="roomList"
|
||||
row-key="id"
|
||||
border
|
||||
@@ -344,6 +366,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 { batchRecallDormBeds } from '@/api/xg/xgss/dormBedStudent/dormBedStudent';
|
||||
|
||||
// const { proxy } = getCurrentInstance();
|
||||
|
||||
@@ -356,6 +379,14 @@ const showSearch = ref(true);
|
||||
const loading = ref(false);
|
||||
const expandedRowKeys = ref<string[]>([]);
|
||||
|
||||
// 加载状态:展开/收起/选择
|
||||
const expandAllLoading = ref(false);
|
||||
const collapseAllLoading = ref(false);
|
||||
const selectionLoading = ref(false);
|
||||
|
||||
// 小延迟,确保加载动画先渲染
|
||||
const delay = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
// 选择状态
|
||||
const selectedRows = ref<any[]>([]);
|
||||
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<any[]>([]);
|
||||
|
||||
/** 处理全选事件 */
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user