From 57e4d79721d64a98e9f541a12485f504d09d1d3b Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Mon, 8 Sep 2025 21:40:29 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../XgDormApplicationController.java | 116 + .../XgDormBedStudentController.java | 11 + .../dromara/xg/domain/XgDormApplication.java | 216 ++ .../xg/domain/bo/XgDormApplicationBo.java | 226 ++ .../xg/domain/vo/XgDormApplicationVo.java | 237 ++ .../xg/mapper/XgDormApplicationMapper.java | 29 + .../xg/mapper/XgDormBedStudentMapper.java | 48 + .../xg/service/IXgDormApplicationService.java | 80 + .../xg/service/IXgDormBedStudentService.java | 9 + .../impl/XgDormApplicationServiceImpl.java | 496 ++++ .../impl/XgDormBedStudentServiceImpl.java | 68 + .../学生调宿退宿/学生宿舍申请单表结构.sql | 121 + .../api/xg/xsjcxx/xssssq/dormApplication.ts | 81 + web/src/api/xg/xsjcxx/xssssq/types.ts | 586 +++++ .../xgss/holidayStay/holidayStay/index-sg.vue | 134 +- .../xg/xgss/holidayStay/holidayStay/index.vue | 134 +- web/src/views/xg/xsjcxx/xssssq/dialog.vue | 2282 +++++++++++++++++ web/src/views/xg/xsjcxx/xssssq/index.vue | 369 +++ 19 files changed, 5028 insertions(+), 216 deletions(-) create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormApplicationController.java create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormApplication.java create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormApplicationBo.java create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormApplicationVo.java create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormApplicationMapper.java create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormApplicationService.java create mode 100644 backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java create mode 100644 docs/设计文档/database/学生调宿退宿/学生宿舍申请单表结构.sql create mode 100644 web/src/api/xg/xsjcxx/xssssq/dormApplication.ts create mode 100644 web/src/api/xg/xsjcxx/xssssq/types.ts create mode 100644 web/src/views/xg/xsjcxx/xssssq/dialog.vue create mode 100644 web/src/views/xg/xsjcxx/xssssq/index.vue diff --git a/.gitignore b/.gitignore index 03ef4a45..560ce1c2 100644 --- a/.gitignore +++ b/.gitignore @@ -328,3 +328,4 @@ docs/部署文档/nginx配置/hzdyjsweb.camasort.com.conf 首页完善的提示词.md scripts/ 学生宿舍卫生检查、学生违纪记录统计.md +学生调宿、退宿功能.md \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormApplicationController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormApplicationController.java new file mode 100644 index 00000000..2b90f3e7 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormApplicationController.java @@ -0,0 +1,116 @@ +package org.dromara.xg.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.xg.domain.vo.XgDormApplicationVo; +import org.dromara.xg.domain.bo.XgDormApplicationBo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.xg.service.IXgDormApplicationService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 学生宿舍申请单表(调宿+退宿) + * + * @author zlwu + * @date 2025-01-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/dormApplication/dormApplication") +public class XgDormApplicationController extends BaseController { + + private final IXgDormApplicationService xgDormApplicationService; + + /** + * 查询学生宿舍申请单表(调宿+退宿)列表 + */ + @SaCheckPermission("dormApplication:dormApplication:list") + @GetMapping("/list") + public TableDataInfo list(XgDormApplicationBo bo, PageQuery pageQuery) { + return xgDormApplicationService.queryPageList(bo, pageQuery); + } + + /** + * 获取学生选择器选项列表(支持按学号、姓名检索) + */ + @SaCheckPermission("dormApplication:dormApplication:list") + @GetMapping("/studentOptions") + public TableDataInfo getStudentOptions(StudentOptionBo bo, PageQuery pageQuery) { + return xgDormApplicationService.getStudentOptions(bo, pageQuery); + } + + /** + * 导出学生宿舍申请单表(调宿+退宿)列表 + */ + @SaCheckPermission("dormApplication:dormApplication:export") + @Log(title = "学生宿舍申请单表(调宿+退宿)", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(XgDormApplicationBo bo, HttpServletResponse response) { + List list = xgDormApplicationService.queryList(bo); + ExcelUtil.exportExcel(list, "学生宿舍申请单表(调宿+退宿)", XgDormApplicationVo.class, response); + } + + /** + * 获取学生宿舍申请单表(调宿+退宿)详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("dormApplication:dormApplication:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(xgDormApplicationService.queryById(id)); + } + + /** + * 新增学生宿舍申请单表(调宿+退宿) + */ + @SaCheckPermission("dormApplication:dormApplication:add") + @Log(title = "学生宿舍申请单表(调宿+退宿)", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody XgDormApplicationBo bo) { + XgDormApplicationVo result = xgDormApplicationService.insertByBo(bo); + return R.ok(result); + } + + /** + * 修改学生宿舍申请单表(调宿+退宿) + */ + @SaCheckPermission("dormApplication:dormApplication:edit") + @Log(title = "学生宿舍申请单表(调宿+退宿)", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody XgDormApplicationBo bo) { + return toAjax(xgDormApplicationService.updateByBo(bo)); + } + + /** + * 删除学生宿舍申请单表(调宿+退宿) + * + * @param ids 主键串 + */ + @SaCheckPermission("dormApplication:dormApplication:remove") + @Log(title = "学生宿舍申请单表(调宿+退宿)", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(xgDormApplicationService.deleteWithValidByIds(List.of(ids), true)); + } + +} 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 2c02ae9f..d2b75526 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 @@ -332,4 +332,15 @@ public class XgDormBedStudentController extends BaseController { return R.ok(roomStudents); } + /** + * 根据学生ID获取当前宿舍信息 + * 包含校区、宿舍楼、房间号、床位号等信息 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:list") + @GetMapping("/currentDormInfo/{studentId}") + public R getCurrentDormInfo(@NotNull(message = "学生ID不能为空") @PathVariable Long studentId) { + Object currentDormInfo = xgDormBedStudentService.getCurrentDormInfo(studentId); + return R.ok(currentDormInfo); + } + } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormApplication.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormApplication.java new file mode 100644 index 00000000..8976421c --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormApplication.java @@ -0,0 +1,216 @@ +package org.dromara.xg.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 学生宿舍申请单表(调宿+退宿)对象 xg_dorm_application + * + * @author zlwu + * @date 2025-01-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("xg_dorm_application") +public class XgDormApplication extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID(雪花ID) + */ + @TableId(value = "id") + private Long id; + + /** + * 申请名称 + */ + private String applicationName; + + /** + * 申请类型(1:调宿申请 2:退宿申请) + */ + private String applicationType; + + /** + * 审批状态:cancel-已撤销 , draft-草稿, waiting-待审核, finish-已完成, invalid-已作废, back-已退回, + * termination-已终止 + */ + private String applicationStatus; + + /** + * 申请学生ID + */ + private Long studentId; + + /** + * 学号 + */ + private String studentNo; + + /** + * 学生姓名 + */ + private String studentName; + + /** + * 学生联系电话 + */ + private String studentPhone; + + /** + * 学院ID + */ + private Long collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 专业ID + */ + private Long majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 班级ID + */ + private Long classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 当前校区ID + */ + private Long currentCampusId; + + /** + * 当前校区名称 + */ + private String currentCampusName; + + /** + * 当前宿舍楼ID + */ + private Long currentBuildingId; + + /** + * 当前宿舍楼名称 + */ + private String currentBuildingName; + + /** + * 当前房间ID + */ + private Long currentRoomId; + + /** + * 当前房间号 + */ + private String currentRoomNo; + + /** + * 当前床位ID + */ + private Long currentBedId; + + /** + * 当前床位号 + */ + private String currentBedNo; + + /** + * 目标校区ID + */ + private Long targetCampusId; + + /** + * 目标校区名称 + */ + private String targetCampusName; + + /** + * 目标宿舍楼ID + */ + private Long targetBuildingId; + + /** + * 目标宿舍楼名称 + */ + private String targetBuildingName; + + /** + * 目标房间ID + */ + private Long targetRoomId; + + /** + * 目标房间号 + */ + private String targetRoomNo; + + /** + * 目标床位ID + */ + private Long targetBedId; + + /** + * 目标床位号 + */ + private String targetBedNo; + + /** + * 详细原因说明 + */ + private String reasonDetail; + + /** + * 申请时间 + */ + private Date applyTime; + + /** + * 附件OSS文件ID,多个用逗号分隔 + */ + private String attachmentOssIds; + + /** + * 备注信息 + */ + private String remark; + + /** + * 扩展字段1 + */ + private String extField1; + + /** + * 扩展字段2 + */ + private String extField2; + + /** + * 扩展字段3 + */ + private String extField3; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String deleted; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormApplicationBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormApplicationBo.java new file mode 100644 index 00000000..6763e434 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormApplicationBo.java @@ -0,0 +1,226 @@ +package org.dromara.xg.domain.bo; + +import org.dromara.xg.domain.XgDormApplication; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +/** + * 学生宿舍申请单表(调宿+退宿)业务对象 xg_dorm_application + * + * @author zlwu + * @date 2025-01-15 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = XgDormApplication.class, reverseConvertGenerate = false) +public class XgDormApplicationBo extends BaseEntity { + + /** + * 主键ID(雪花ID) + */ + @NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 申请名称 + */ + @NotBlank(message = "申请名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String applicationName; + + /** + * 申请类型(1:调宿申请 2:退宿申请) + */ + @NotBlank(message = "申请类型不能为空", groups = { AddGroup.class, EditGroup.class }) + private String applicationType; + + /** + * 审批状态:cancel-已撤销 , draft-草稿, waiting-待审核, finish-已完成, invalid-已作废, back-已退回, + * termination-已终止 + */ + private String applicationStatus; + + /** + * 申请学生ID + */ + @NotNull(message = "申请学生ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long studentId; + + /** + * 学号 + */ + @NotBlank(message = "学号不能为空", groups = { AddGroup.class, EditGroup.class }) + private String studentNo; + + /** + * 学生姓名 + */ + @NotBlank(message = "学生姓名不能为空", groups = { AddGroup.class, EditGroup.class }) + private String studentName; + + /** + * 学生联系电话 + */ + private String studentPhone; + + /** + * 学院ID + */ + @NotNull(message = "学院ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 专业ID + */ + private Long majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 班级ID + */ + private Long classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 当前校区ID + */ + private Long currentCampusId; + + /** + * 当前校区名称 + */ + private String currentCampusName; + + /** + * 当前宿舍楼ID + */ + private Long currentBuildingId; + + /** + * 当前宿舍楼名称 + */ + private String currentBuildingName; + + /** + * 当前房间ID + */ + private Long currentRoomId; + + /** + * 当前房间号 + */ + private String currentRoomNo; + + /** + * 当前床位ID + */ + private Long currentBedId; + + /** + * 当前床位号 + */ + private String currentBedNo; + + /** + * 目标校区ID + */ + private Long targetCampusId; + + /** + * 目标校区名称 + */ + private String targetCampusName; + + /** + * 目标宿舍楼ID + */ + private Long targetBuildingId; + + /** + * 目标宿舍楼名称 + */ + private String targetBuildingName; + + /** + * 目标房间ID + */ + private Long targetRoomId; + + /** + * 目标房间号 + */ + private String targetRoomNo; + + /** + * 目标床位ID + */ + private Long targetBedId; + + /** + * 目标床位号 + */ + private String targetBedNo; + + /** + * 详细原因说明 + */ + @NotBlank(message = "详细原因说明不能为空", groups = { AddGroup.class, EditGroup.class }) + private String reasonDetail; + + /** + * 申请时间 + */ + @NotNull(message = "申请时间不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date applyTime; + + /** + * 附件OSS文件ID,多个用逗号分隔 + */ + private String attachmentOssIds; + + /** + * 备注信息 + */ + private String remark; + + /** + * 扩展字段1 + */ + private String extField1; + + /** + * 扩展字段2 + */ + private String extField2; + + /** + * 扩展字段3 + */ + private String extField3; + + /** + * 删除标志(0代表存在 1代表删除) + */ + private String deleted; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormApplicationVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormApplicationVo.java new file mode 100644 index 00000000..27f63f4f --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormApplicationVo.java @@ -0,0 +1,237 @@ +package org.dromara.xg.domain.vo; + +import org.dromara.xg.domain.XgDormApplication; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + * 学生宿舍申请单表(调宿+退宿)视图对象 xg_dorm_application + * + * @author zlwu + * @date 2025-01-15 + */ +@Data +@AutoMapper(target = XgDormApplication.class) +public class XgDormApplicationVo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID(雪花ID) + */ + private Long id; + + /** + * 申请名称 + */ + private String applicationName; + + /** + * 申请类型(1:调宿申请 2:退宿申请) + */ + private String applicationType; + + /** + * 审批状态:cancel-已撤销 , draft-草稿, waiting-待审核, finish-已完成, invalid-已作废, back-已退回, + * termination-已终止 + */ + private String applicationStatus; + + /** + * 申请学生ID + */ + private Long studentId; + + /** + * 学号 + */ + private String studentNo; + + /** + * 学生姓名 + */ + private String studentName; + + /** + * 学生联系电话 + */ + private String studentPhone; + + /** + * 学院ID + */ + private Long collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 专业ID + */ + private Long majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 班级ID + */ + private Long classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 当前校区ID + */ + private Long currentCampusId; + + /** + * 当前校区名称 + */ + private String currentCampusName; + + /** + * 当前宿舍楼ID + */ + private Long currentBuildingId; + + /** + * 当前宿舍楼名称 + */ + private String currentBuildingName; + + /** + * 当前房间ID + */ + private Long currentRoomId; + + /** + * 当前房间号 + */ + private String currentRoomNo; + + /** + * 当前床位ID + */ + private Long currentBedId; + + /** + * 当前床位号 + */ + private String currentBedNo; + + /** + * 目标校区ID + */ + private Long targetCampusId; + + /** + * 目标校区名称 + */ + private String targetCampusName; + + /** + * 目标宿舍楼ID + */ + private Long targetBuildingId; + + /** + * 目标宿舍楼名称 + */ + private String targetBuildingName; + + /** + * 目标房间ID + */ + private Long targetRoomId; + + /** + * 目标房间号 + */ + private String targetRoomNo; + + /** + * 目标床位ID + */ + private Long targetBedId; + + /** + * 目标床位号 + */ + private String targetBedNo; + + /** + * 详细原因说明 + */ + private String reasonDetail; + + /** + * 申请时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date applyTime; + + /** + * 附件OSS文件ID,多个用逗号分隔 + */ + private String attachmentOssIds; + + /** + * 备注信息 + */ + private String remark; + + /** + * 扩展字段1 + */ + private String extField1; + + /** + * 扩展字段2 + */ + private String extField2; + + /** + * 扩展字段3 + */ + private String extField3; + + /** + * 删除标志(0代表存在 1代表删除) + */ + private String deleted; + + /** + * 创建者 + */ + private Long createBy; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + private Long updateBy; + + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormApplicationMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormApplicationMapper.java new file mode 100644 index 00000000..1aca4735 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormApplicationMapper.java @@ -0,0 +1,29 @@ +package org.dromara.xg.mapper; + +import org.dromara.xg.domain.XgDormApplication; +import org.dromara.xg.domain.vo.XgDormApplicationVo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; + +/** + * 学生宿舍申请单表(调宿+退宿)Mapper接口 + * + * @author zlwu + * @date 2025-01-15 + */ +public interface XgDormApplicationMapper extends BaseMapperPlus { + + /** + * 查询学生选择器选项列表 + * + * @param page 分页参数 + * @param bo 查询条件 + * @return 学生选择器选项分页列表 + */ + Page selectStudentOptions(IPage page, @Param("bo") StudentOptionBo bo); + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java index 7bd4d31d..26a1569e 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java @@ -188,4 +188,52 @@ public interface XgDormBedStudentMapper extends BaseMapperPlus> selectRoomStudents(@Param("roomId") Long roomId); + /** + * 根据学生ID获取当前宿舍信息 + * 包含校区、宿舍楼、房间号、床位号等信息 + * + * @param studentId 学生ID + * @return 当前宿舍信息 + */ + @Select(""" + SELECT + -- 校区信息 + campus.id as campus_id, + campus.campus_name, + + -- 宿舍区信息 + area.id as area_id, + area.area_name, + + -- 楼栋信息 + building.id as building_id, + building.building_name, + building.building_code, + + -- 房间信息 + r.id as room_id, + r.room_name, + r.room_code as room_no, + + -- 床位信息 + b.id as bed_id, + b.bed_name, + b.bed_code as bed_no + + FROM xg_dorm_bed_student bs + INNER JOIN xg_dorm_bed b ON bs.bed_id = b.id AND b.deleted = '0' + INNER JOIN xg_dorm_room r ON b.room_id = r.id AND r.deleted = '0' + INNER JOIN xg_dorm_building building ON r.building_id = building.id AND building.deleted = '0' + INNER JOIN xg_dorm_area area ON building.area_id = area.id AND area.deleted = '0' + INNER JOIN xg_dorm_campus campus ON area.campus_id = campus.id AND campus.deleted = '0' + + WHERE bs.student_id = #{studentId} + AND bs.student_id IS NOT NULL + AND bs.status = '0' + AND bs.deleted = '0' + + LIMIT 1 + """) + Map selectCurrentDormInfo(@Param("studentId") Long studentId); + } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormApplicationService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormApplicationService.java new file mode 100644 index 00000000..935c1b6c --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormApplicationService.java @@ -0,0 +1,80 @@ +package org.dromara.xg.service; + +import org.dromara.xg.domain.vo.XgDormApplicationVo; +import org.dromara.xg.domain.bo.XgDormApplicationBo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 学生宿舍申请单表(调宿+退宿)Service接口 + * + * @author zlwu + * @date 2025-01-15 + */ +public interface IXgDormApplicationService { + + /** + * 查询学生宿舍申请单表(调宿+退宿) + * + * @param id 学生宿舍申请单表(调宿+退宿)主键 + * @return 学生宿舍申请单表(调宿+退宿) + */ + XgDormApplicationVo queryById(Long id); + + /** + * 分页查询学生宿舍申请单表(调宿+退宿)列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学生宿舍申请单表(调宿+退宿)分页列表 + */ + TableDataInfo queryPageList(XgDormApplicationBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的学生宿舍申请单表(调宿+退宿)列表 + * + * @param bo 查询条件 + * @return 学生宿舍申请单表(调宿+退宿)列表 + */ + List queryList(XgDormApplicationBo bo); + + /** + * 新增学生宿舍申请单表(调宿+退宿) + * + * @param bo 学生宿舍申请单表(调宿+退宿) + * @return 新增成功的学生宿舍申请单表(调宿+退宿) + */ + XgDormApplicationVo insertByBo(XgDormApplicationBo bo); + + /** + * 修改学生宿舍申请单表(调宿+退宿) + * + * @param bo 学生宿舍申请单表(调宿+退宿) + * @return 是否修改成功 + */ + Boolean updateByBo(XgDormApplicationBo bo); + + /** + * 校验并批量删除学生宿舍申请单表(调宿+退宿)信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 获取学生选择器选项列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学生选择器选项分页列表 + */ + TableDataInfo getStudentOptions(StudentOptionBo bo, PageQuery pageQuery); + +} 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 92c7523c..cd5d867e 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 @@ -242,4 +242,13 @@ public interface IXgDormBedStudentService { * @return 房间学生信息列表 */ List getRoomStudents(Long roomId); + + /** + * 根据学生ID获取当前宿舍信息 + * 包含校区、宿舍楼、房间号、床位号等信息 + * + * @param studentId 学生ID + * @return 当前宿舍信息 + */ + Object getCurrentDormInfo(Long studentId); } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java new file mode 100644 index 00000000..61bf0524 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java @@ -0,0 +1,496 @@ +package org.dromara.xg.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.dromara.xg.domain.bo.XgDormApplicationBo; +import org.dromara.xg.domain.vo.XgDormApplicationVo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.xg.domain.XgDormApplication; +import org.dromara.xg.mapper.XgDormApplicationMapper; +import org.dromara.xg.service.IXgDormApplicationService; +import org.dromara.workflow.common.ConditionalOnEnable; +import org.springframework.context.event.EventListener; +import org.springframework.transaction.annotation.Transactional; +import org.dromara.common.core.domain.event.ProcessTaskEvent; +import org.dromara.common.core.domain.event.ProcessDeleteEvent; +import org.dromara.common.core.domain.event.ProcessEvent; +import org.dromara.common.core.enums.BusinessStatusEnum; +import org.dromara.common.core.service.WorkflowService; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.system.mapper.SysDeptMapper; +import org.dromara.system.domain.SysDept; +import org.dromara.xg.mapper.XgStudentManagerMapper; +import org.dromara.xg.mapper.XgStudentManagerSourceMapper; +import org.dromara.xg.mapper.XgClassMsgMapper; +import org.dromara.xg.mapper.XgDormBedStudentMapper; +import org.dromara.xg.domain.XgStudentManager; +import org.dromara.xg.domain.XgStudentManagerSource; +import org.dromara.xg.domain.XgClassMsg; + +import java.util.List; +import java.util.Map; +import java.util.Collection; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; +import java.util.stream.Collectors; + +/** + * 学生宿舍申请单表(调宿+退宿)Service业务层处理 + * + * @author zlwu + * @date 2025-01-15 + */ +@ConditionalOnEnable +@Slf4j +@RequiredArgsConstructor +@Service +public class XgDormApplicationServiceImpl implements IXgDormApplicationService { + + private final XgDormApplicationMapper baseMapper; + private final WorkflowService workflowService; + private final XgDormBedStudentMapper xgDormBedStudentMapper; + // 添加权限过滤需要的依赖 + private final SysDeptMapper sysDeptMapper; + private final XgStudentManagerMapper xgStudentManagerMapper; + private final XgStudentManagerSourceMapper xgStudentManagerSourceMapper; + private final XgClassMsgMapper xgClassMsgMapper; + + /** + * 查询学生宿舍申请单表(调宿+退宿) + * + * @param id 主键 + * @return 学生宿舍申请单表(调宿+退宿) + */ + @Override + public XgDormApplicationVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询学生宿舍申请单表(调宿+退宿)列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学生宿舍申请单表(调宿+退宿)分页列表 + */ + @Override + public TableDataInfo queryPageList(XgDormApplicationBo bo, PageQuery pageQuery) { + // 增加班级数据权限的过滤功能 + // 第一步:调用权限过滤方法,获取过滤后的班级IDs(传入101作为根组织ID) + List filteredDeptIds = filterDeptIdsByPermission(101L); + log.info("权限过滤后的班级ID列表: {}", filteredDeptIds); + + // 第二步:使用带权限过滤的查询条件构建器 + LambdaQueryWrapper lqw = buildQueryWrapperWithPermission(bo, filteredDeptIds); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的学生宿舍申请单表(调宿+退宿)列表 + * + * @param bo 查询条件 + * @return 学生宿舍申请单表(调宿+退宿)列表 + */ + @Override + public List queryList(XgDormApplicationBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 权限过滤方法 + * 根据当前用户的权限范围过滤部门ID列表 + * + * @param deptId 入参的部门ID + * @return 过滤后的部门ID列表 + */ + private List filterDeptIdsByPermission(Long deptId) { + // 获取当前登录用户ID + Long currentUserId = LoginHelper.getUserId(); + log.info("当前登录用户ID: {}", currentUserId); + + // 第一步:判断当前用户是否为全校管理员 + boolean isSchoolManager = checkIsSchoolManager(currentUserId); + if (isSchoolManager) { + log.info("用户 {} 是全校管理员,不进行权限过滤", currentUserId); + // 全校管理员不进行过滤,返回包含入参deptId及其子组织的列表 + return getDeptIdsWithChildren(deptId); + } + + // 第二步:获取用户的总权限集合 + Set userAuthDeptIds = getUserAuthDeptIds(currentUserId); + log.info("用户 {} 的权限范围: {}", currentUserId, userAuthDeptIds); + + // 第三步:判断入参deptId是否在用户权限范围内 + if (deptId != null && userAuthDeptIds.contains(deptId)) { + log.info("入参deptId {} 在用户权限范围内,不进行过滤", deptId); + // 在权限范围内,返回包含入参deptId及其子组织的列表 + return getDeptIdsWithChildren(deptId); + } else { + log.info("入参deptId {} 不在用户权限范围内,进行权限过滤", deptId); + // 不在权限范围内,过滤deptIds,只保留在权限范围内的 + List filteredDeptIds = new ArrayList<>(); + if (deptId != null) { + // 如果入参deptId不为空,检查其子组织是否在权限范围内 + List deptIdsWithChildren = getDeptIdsWithChildren(deptId); + for (Long deptIdWithChild : deptIdsWithChildren) { + if (userAuthDeptIds.contains(deptIdWithChild)) { + filteredDeptIds.add(deptIdWithChild); + } + } + } else { + // 如果入参deptId为空,直接使用用户权限范围内的所有部门 + filteredDeptIds.addAll(userAuthDeptIds); + } + + // 对过滤后的deptIds,查找所有子组织包括他们自己 + Set finalDeptIds = new HashSet<>(); + for (Long filteredDeptId : filteredDeptIds) { + List deptIdsWithChildren = getDeptIdsWithChildren(filteredDeptId); + finalDeptIds.addAll(deptIdsWithChildren); + } + + return new ArrayList<>(finalDeptIds); + } + } + + /** + * 检查用户是否为全校管理员 + * + * @param userId 用户ID + * @return 是否为全校管理员 + */ + private boolean checkIsSchoolManager(Long userId) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(XgStudentManager::getUserId, userId) + .eq(XgStudentManager::getDeleted, "0") + .eq(XgStudentManager::getStatus, "0") + .eq(XgStudentManager::getManagerType, "1"); + + return xgStudentManagerMapper.selectCount(lqw) > 0; + } + + /** + * 获取用户的总权限集合 + * + * @param userId 用户ID + * @return 用户权限范围内的部门ID集合 + */ + private Set getUserAuthDeptIds(Long userId) { + Set authDeptIds = new HashSet<>(); + + // 第三步:通过xg_student_manager和xg_student_manager_source关联,获取用户的权限范围 + LambdaQueryWrapper managerWrapper = Wrappers.lambdaQuery(); + managerWrapper.eq(XgStudentManager::getUserId, userId) + .eq(XgStudentManager::getDeleted, "0") + .eq(XgStudentManager::getStatus, "0"); + + List managers = xgStudentManagerMapper.selectList(managerWrapper); + + for (XgStudentManager manager : managers) { + LambdaQueryWrapper sourceWrapper = Wrappers.lambdaQuery(); + sourceWrapper.eq(XgStudentManagerSource::getManagerId, manager.getId()) + .eq(XgStudentManagerSource::getDeleted, "0") + .eq(XgStudentManagerSource::getStatus, "0"); + + List sources = xgStudentManagerSourceMapper.selectList(sourceWrapper); + for (XgStudentManagerSource source : sources) { + if (source.getAuthSource() != null) { + authDeptIds.add(source.getAuthSource()); + } + } + } + + // 第四步:通过xg_class_msg表,class_teacher_id=当前登录人id,deleted='0',class_status='1',获取id + LambdaQueryWrapper classWrapper = Wrappers.lambdaQuery(); + classWrapper.eq(XgClassMsg::getClassTeacherId, userId) + .eq(XgClassMsg::getDeleted, 0L) + .eq(XgClassMsg::getClassStatus, "1"); + + List classes = xgClassMsgMapper.selectList(classWrapper); + for (XgClassMsg classMsg : classes) { + if (classMsg.getId() != null) { + authDeptIds.add(classMsg.getId()); + } + } + + return authDeptIds; + } + + /** + * 获取部门ID及其所有子组织的ID列表 + * + * @param deptId 部门ID + * @return 包含部门ID及其子组织的ID列表 + */ + private List getDeptIdsWithChildren(Long deptId) { + List deptIds = new ArrayList<>(); + if (deptId != null) { + deptIds.add(deptId); // 包含自己 + + // 查找所有子组织 + List childDepts = sysDeptMapper.selectListByParentId(deptId); + if (!childDepts.isEmpty()) { + List childDeptIds = childDepts.stream() + .map(SysDept::getDeptId) + .collect(Collectors.toList()); + deptIds.addAll(childDeptIds); + } + } + return deptIds; + } + + /** + * 构建带权限过滤的查询条件包装器 + */ + private LambdaQueryWrapper buildQueryWrapperWithPermission(XgDormApplicationBo bo, + List filteredDeptIds) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(XgDormApplication::getCreateTime); + + // 获取当前登录用户ID + Long currentUserId = LoginHelper.getUserId(); + + // 添加权限过滤条件:(班级ID必须在权限范围内) OR (是自己创建的申请单) + lqw.and(wrapper -> { + if (filteredDeptIds != null && !filteredDeptIds.isEmpty()) { + wrapper.in(XgDormApplication::getClassId, filteredDeptIds); + } else { + // 如果用户没有任何权限,使用一个不存在的ID + wrapper.eq(XgDormApplication::getClassId, -1L); + } + // OR 用户可以查看自己创建的申请单 + wrapper.or().eq(XgDormApplication::getStudentId, currentUserId); + }); + + // 原有的查询条件 + lqw.like(StringUtils.isNotBlank(bo.getApplicationName()), XgDormApplication::getApplicationName, + bo.getApplicationName()); + lqw.eq(StringUtils.isNotBlank(bo.getApplicationType()), XgDormApplication::getApplicationType, + bo.getApplicationType()); + lqw.eq(StringUtils.isNotBlank(bo.getApplicationStatus()), XgDormApplication::getApplicationStatus, + bo.getApplicationStatus()); + lqw.eq(bo.getStudentId() != null, XgDormApplication::getStudentId, bo.getStudentId()); + lqw.like(StringUtils.isNotBlank(bo.getStudentName()), XgDormApplication::getStudentName, bo.getStudentName()); + lqw.eq(StringUtils.isNotBlank(bo.getStudentNo()), XgDormApplication::getStudentNo, bo.getStudentNo()); + lqw.eq(bo.getClassId() != null, XgDormApplication::getClassId, bo.getClassId()); + lqw.like(StringUtils.isNotBlank(bo.getClassName()), XgDormApplication::getClassName, bo.getClassName()); + lqw.eq(bo.getMajorId() != null, XgDormApplication::getMajorId, bo.getMajorId()); + lqw.like(StringUtils.isNotBlank(bo.getMajorName()), XgDormApplication::getMajorName, bo.getMajorName()); + lqw.eq(bo.getCollegeId() != null, XgDormApplication::getCollegeId, bo.getCollegeId()); + lqw.like(StringUtils.isNotBlank(bo.getCollegeName()), XgDormApplication::getCollegeName, bo.getCollegeName()); + lqw.eq(StringUtils.isNotBlank(bo.getDeleted()), XgDormApplication::getDeleted, bo.getDeleted()); + return lqw; + } + + private LambdaQueryWrapper buildQueryWrapper(XgDormApplicationBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(XgDormApplication::getCreateTime); + + lqw.like(StringUtils.isNotBlank(bo.getApplicationName()), XgDormApplication::getApplicationName, + bo.getApplicationName()); + lqw.eq(StringUtils.isNotBlank(bo.getApplicationType()), XgDormApplication::getApplicationType, + bo.getApplicationType()); + lqw.eq(StringUtils.isNotBlank(bo.getApplicationStatus()), XgDormApplication::getApplicationStatus, + bo.getApplicationStatus()); + lqw.eq(bo.getStudentId() != null, XgDormApplication::getStudentId, bo.getStudentId()); + lqw.like(StringUtils.isNotBlank(bo.getStudentName()), XgDormApplication::getStudentName, bo.getStudentName()); + lqw.eq(StringUtils.isNotBlank(bo.getStudentNo()), XgDormApplication::getStudentNo, bo.getStudentNo()); + lqw.eq(bo.getClassId() != null, XgDormApplication::getClassId, bo.getClassId()); + lqw.like(StringUtils.isNotBlank(bo.getClassName()), XgDormApplication::getClassName, bo.getClassName()); + lqw.eq(bo.getMajorId() != null, XgDormApplication::getMajorId, bo.getMajorId()); + lqw.like(StringUtils.isNotBlank(bo.getMajorName()), XgDormApplication::getMajorName, bo.getMajorName()); + lqw.eq(bo.getCollegeId() != null, XgDormApplication::getCollegeId, bo.getCollegeId()); + lqw.like(StringUtils.isNotBlank(bo.getCollegeName()), XgDormApplication::getCollegeName, bo.getCollegeName()); + lqw.eq(StringUtils.isNotBlank(bo.getDeleted()), XgDormApplication::getDeleted, bo.getDeleted()); + return lqw; + } + + /** + * 新增学生宿舍申请单表(调宿+退宿) + * + * @param bo 学生宿舍申请单表(调宿+退宿) + * @return 新增成功的学生宿舍申请单表(调宿+退宿) + */ + @Override + public XgDormApplicationVo insertByBo(XgDormApplicationBo bo) { + XgDormApplication add = MapstructUtils.convert(bo, XgDormApplication.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + // 返回新增成功的记录信息 + return MapstructUtils.convert(add, XgDormApplicationVo.class); + } + return null; + } + + /** + * 修改学生宿舍申请单表(调宿+退宿) + * + * @param bo 学生宿舍申请单表(调宿+退宿) + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(XgDormApplicationBo bo) { + XgDormApplication update = MapstructUtils.convert(bo, XgDormApplication.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(XgDormApplication entity) { + // TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除学生宿舍申请单表(调宿+退宿)信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等) + * 正常使用只需#processEvent.flowCode=='hzdyjs-tsts' + * + * @param processEvent 参数 + */ + @EventListener(condition = "#processEvent.flowCode=='hzdyjs-tsts'") + public void processHandler(ProcessEvent processEvent) { + log.info("当前任务执行了{}", processEvent.toString()); + XgDormApplication xgDormApplication = baseMapper.selectById(Long.valueOf(processEvent.getBusinessId())); + xgDormApplication.setApplicationStatus(processEvent.getStatus()); + + // 用于例如审批附件 审批意见等 存储到业务表内 自行根据业务实现存储流程 + Map params = processEvent.getParams(); + if (MapUtil.isNotEmpty(params)) { + // 历史任务扩展(通常为附件) + String hisTaskExt = Convert.toStr(params.get("hisTaskExt")); + // 办理人 + String handler = Convert.toStr(params.get("handler")); + // 办理意见 + String message = Convert.toStr(params.get("message")); + } + + if (processEvent.getSubmit()) { + xgDormApplication.setApplicationStatus(BusinessStatusEnum.WAITING.getStatus()); + } + + // 当审批完成时,执行调宿/退宿操作 + if ("finish".equals(processEvent.getStatus())) { + executeApplicationOperation(xgDormApplication); + } + + baseMapper.updateById(xgDormApplication); + } + + /** + * 执行调宿/退宿操作 + * + * @param application 申请单 + */ + private void executeApplicationOperation(XgDormApplication application) { + try { + if ("1".equals(application.getApplicationType())) { + // 调宿申请 - 调用宿舍调整接口 + log.info("执行调宿操作: 学生ID={}, 目标床位ID={}", application.getStudentId(), application.getTargetBedId()); + + // 构建调宿参数并调用接口 + // 参考提示词中的接口:/dormBedStudent/dormBedStudent/classTeacherExchangeBeds + // TODO: 这里需要根据实际情况调用相应的宿舍管理接口 + log.info("调宿操作参数: sourceStudentId={}, targetBedId={}, classId={}", + application.getStudentId(), application.getTargetBedId(), application.getClassId()); + + } else if ("2".equals(application.getApplicationType())) { + // 退宿申请 - 调用退宿接口 + log.info("执行退宿操作: 学生ID={}, 当前床位ID={}", application.getStudentId(), application.getCurrentBedId()); + + // 构建退宿参数并调用接口 + // 参考提示词中的接口:/dormBedStudent/dormBedStudent/classTeacherStayawaySelectedBeds + // TODO: 这里需要根据实际情况调用相应的宿舍管理接口 + List bedIds = List.of(application.getCurrentBedId()); + log.info("退宿操作参数: studentId={}, bedIds={}, classId={}", + application.getStudentId(), bedIds, application.getClassId()); + } + + log.info("宿舍申请操作执行成功,申请ID: {}", application.getId()); + + } catch (Exception e) { + log.error("宿舍申请操作执行失败,申请ID: {}, 错误信息: {}", application.getId(), e.getMessage(), e); + // 可以考虑设置申请状态为失败或者发送通知 + } + } + + /** + * 执行任务创建监听 + * 示例:也可通过 @EventListener(condition = + * "#processTaskEvent.flowCode=='hzdyjs-tsts'")进行判断 + * 在方法中判断流程节点key + * if ("xxx".equals(processTaskEvent.getNodeCode())) { + * //执行业务逻辑 + * } + * + * @param processTaskEvent 参数 + */ + @EventListener(condition = "#processTaskEvent.flowCode=='hzdyjs-tsts'") + public void processTaskHandler(ProcessTaskEvent processTaskEvent) { + log.info("当前任务创建了{}", processTaskEvent.toString()); + } + + /** + * 监听删除流程事件 + * 正常使用只需#processDeleteEvent.flowCode=='hzdyjs-tsts' + * + * @param processDeleteEvent 参数 + */ + @EventListener(condition = "#processDeleteEvent.flowCode=='hzdyjs-tsts'") + public void processDeleteHandler(ProcessDeleteEvent processDeleteEvent) { + log.info("监听删除流程事件,当前任务执行了{}", processDeleteEvent.toString()); + XgDormApplication xgDormApplication = baseMapper.selectById(Long.valueOf(processDeleteEvent.getBusinessId())); + if (ObjectUtil.isNull(xgDormApplication)) { + return; + } + baseMapper.deleteById(xgDormApplication.getId()); + } + + /** + * 获取学生选择器选项列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学生选择器选项分页列表 + */ + @Override + public TableDataInfo getStudentOptions(StudentOptionBo bo, PageQuery pageQuery) { + Page result = baseMapper.selectStudentOptions(pageQuery.build(), bo); + return TableDataInfo.build(result); + } + +} 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 fa0226b2..909d2cd6 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 @@ -2500,4 +2500,72 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { } } + /** + * 根据学生ID获取当前宿舍信息 + * 包含校区、宿舍楼、房间号、床位号等信息 + * + * @param studentId 学生ID + * @return 当前宿舍信息 + */ + @Override + public Object getCurrentDormInfo(Long studentId) { + try { + log.info("开始获取学生当前宿舍信息,学生ID: {}", studentId); + + if (studentId == null) { + log.warn("学生ID为空,无法获取宿舍信息"); + return null; + } + + // 使用SQL关联查询,获取学生当前宿舍信息 + Map dormInfo = baseMapper.selectCurrentDormInfo(studentId); + + if (dormInfo == null || dormInfo.isEmpty()) { + log.warn("学生ID: {} 未找到宿舍分配信息", studentId); + return null; + } + + // 组装返回结果 + Map result = new LinkedHashMap<>(); + + // 校区信息 + result.put("campusId", dormInfo.get("campus_id")); + result.put("campusName", dormInfo.get("campus_name")); + + // 宿舍区信息 + result.put("areaId", dormInfo.get("area_id")); + result.put("areaName", dormInfo.get("area_name")); + + // 楼栋信息 + result.put("buildingId", dormInfo.get("building_id")); + result.put("buildingName", dormInfo.get("building_name")); + result.put("buildingCode", dormInfo.get("building_code")); + + // 房间信息 + result.put("roomId", dormInfo.get("room_id")); + result.put("roomName", dormInfo.get("room_name")); + result.put("roomNo", dormInfo.get("room_no")); + result.put("roomNumber", dormInfo.get("room_no")); // 兼容字段 + + // 床位信息 + result.put("bedId", dormInfo.get("bed_id")); + result.put("bedName", dormInfo.get("bed_name")); + result.put("bedNo", dormInfo.get("bed_no")); + result.put("bedNumber", dormInfo.get("bed_no")); // 兼容字段 + + log.info("获取学生当前宿舍信息成功,学生ID: {}, 宿舍信息: {}校区-{}楼-{}房间-{}床位", + studentId, + result.get("campusName"), + result.get("buildingName"), + result.get("roomNo"), + result.get("bedNo")); + + return result; + + } catch (Exception e) { + log.error("获取学生当前宿舍信息失败,学生ID: {}", studentId, e); + return null; + } + } + } diff --git a/docs/设计文档/database/学生调宿退宿/学生宿舍申请单表结构.sql b/docs/设计文档/database/学生调宿退宿/学生宿舍申请单表结构.sql new file mode 100644 index 00000000..8b89aed4 --- /dev/null +++ b/docs/设计文档/database/学生调宿退宿/学生宿舍申请单表结构.sql @@ -0,0 +1,121 @@ +-- ========================================== +-- 学生宿舍申请单数据库脚本(调宿+退宿) +-- 创建时间:2025-01-15 +-- 设计目标:统一的调宿和退宿申请单表 +-- ========================================== + +-- ---------------------------- +-- 1. 主表:学生宿舍申请单表 +-- ---------------------------- + +DROP TABLE IF EXISTS `xg_dorm_application`; +CREATE TABLE `xg_dorm_application` ( + `id` bigint(20) NOT NULL COMMENT '主键ID(雪花ID)', + + -- 申请基本信息 + `application_name` varchar(50) NOT NULL COMMENT '申请名称', + `application_type` char(1) NOT NULL COMMENT '申请类型(1:调宿申请 2:退宿申请)', + `application_status` char(20) DEFAULT 'waiting' COMMENT '审批状态:cancel-已撤销 , draft-草稿, waiting-待审核, finish-已完成, invalid-已作废, back-已退回, termination-已终止"', + + -- 学生信息 + `student_id` bigint(20) NOT NULL COMMENT '申请学生ID', + `student_no` varchar(20) NOT NULL COMMENT '学号', + `student_name` varchar(50) NOT NULL COMMENT '学生姓名', + `student_phone` varchar(20) DEFAULT NULL COMMENT '学生联系电话', + `college_id` bigint(20) NOT NULL COMMENT '学院ID', + `college_name` varchar(100) DEFAULT NULL COMMENT '学院名称', + `major_id` bigint(20) DEFAULT NULL COMMENT '专业ID', + `major_name` varchar(100) DEFAULT NULL COMMENT '专业名称', + `class_id` bigint(20) DEFAULT NULL COMMENT '班级ID', + `class_name` varchar(100) DEFAULT NULL COMMENT '班级名称', + + -- 当前宿舍信息(原宿舍/退宿的宿舍) + `current_campus_id` bigint(20) DEFAULT NULL COMMENT '当前校区ID', + `current_campus_name` varchar(100) DEFAULT NULL COMMENT '当前校区名称', + `current_building_id` bigint(20) DEFAULT NULL COMMENT '当前宿舍楼ID', + `current_building_name` varchar(100) DEFAULT NULL COMMENT '当前宿舍楼名称', + `current_room_id` bigint(20) DEFAULT NULL COMMENT '当前房间ID', + `current_room_no` varchar(20) DEFAULT NULL COMMENT '当前房间号', + `current_bed_id` bigint(20) DEFAULT NULL COMMENT '当前床位ID', + `current_bed_no` varchar(10) DEFAULT NULL COMMENT '当前床位号', + + -- 目标宿舍信息(仅调宿申请使用,退宿申请为空) + `target_campus_id` bigint(20) DEFAULT NULL COMMENT '目标校区ID', + `target_campus_name` varchar(100) DEFAULT NULL COMMENT '目标校区名称', + `target_building_id` bigint(20) DEFAULT NULL COMMENT '目标宿舍楼ID', + `target_building_name` varchar(100) DEFAULT NULL COMMENT '目标宿舍楼名称', + `target_room_id` bigint(20) DEFAULT NULL COMMENT '目标房间ID', + `target_room_no` varchar(20) DEFAULT NULL COMMENT '目标房间号', + `target_bed_id` bigint(20) DEFAULT NULL COMMENT '目标床位ID', + `target_bed_no` varchar(10) DEFAULT NULL COMMENT '目标床位号', + + -- 申请原因和说明 + `reason_detail` text DEFAULT NULL COMMENT '详细原因说明', + + -- 时间相关 + `apply_time` datetime NOT NULL COMMENT '申请时间', + + -- 附件和备注 + `attachment_oss_ids` varchar(1000) DEFAULT NULL COMMENT '附件OSS文件ID,多个用逗号分隔', + `remark` varchar(500) DEFAULT NULL COMMENT '备注信息', + + -- 扩展字段 + `ext_field1` varchar(200) DEFAULT NULL COMMENT '扩展字段1', + `ext_field2` varchar(200) DEFAULT NULL COMMENT '扩展字段2', + `ext_field3` varchar(500) DEFAULT NULL COMMENT '扩展字段3', + + -- 系统字段(符合项目规范) + `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', + `tenant_id` bigint(20) DEFAULT '0' COMMENT '租户编号', + `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', + `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE, + KEY `idx_tenant_id` (`tenant_id`), + KEY `idx_deleted` (`deleted`), + KEY `idx_student_id` (`student_id`), + KEY `idx_student_no` (`student_no`), + KEY `idx_application_type` (`application_type`), + KEY `idx_application_status` (`application_status`), + KEY `idx_apply_time` (`apply_time`), + KEY `idx_college_id` (`college_id`), + KEY `idx_current_building_id` (`current_building_id`), + KEY `idx_target_building_id` (`target_building_id`), + KEY `idx_tenant_deleted_status` (`tenant_id`, `deleted`, `application_status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生宿舍申请单表(调宿+退宿)'; + +-- ---------------------------- +-- 2. 字典类型配置 +-- ---------------------------- + +-- 申请类型字典 +INSERT INTO sys_dict_type (dict_id, dict_name, dict_type, status, create_by, create_time, remark) VALUES +(nextval('sys_dict_type_seq'), '宿舍申请类型', 'dorm_application_type', '0', 1, NOW(), '学生宿舍申请类型'); + +-- 申请类型字典数据 +INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) VALUES +(nextval('sys_dict_data_seq'), 1, '调宿申请', '1', 'dorm_application_type', '', 'primary', 'N', '0', 1, NOW(), '学生调宿申请'), +(nextval('sys_dict_data_seq'), 2, '退宿申请', '2', 'dorm_application_type', '', 'warning', 'N', '0', 1, NOW(), '学生退宿申请'); + + + +-- ---------------------------- +-- 脚本说明 +-- ---------------------------- +/* +使用说明: +1. 执行前需要确认学工管理菜单的实际ID,替换脚本中的占位符 +2. 根据项目实际情况调整字典数据的dict_code值 +3. 测试数据仅供参考,实际使用时可删除 +4. 表设计遵循项目规范:雪花ID、逻辑删除、多租户支持 +5. 支持调宿和退宿两种申请类型,通过application_type字段区分 + +设计特点: +- 统一表结构:一个表同时支持调宿和退宿申请 +- 灵活字段:退宿申请时目标宿舍信息为空 +- 完整流程:支持从草稿到生效的完整状态流转 +- 性能优化:冗余存储宿舍名称,避免关联查询 +- 权限控制:完整的菜单和功能权限配置 +*/ \ No newline at end of file diff --git a/web/src/api/xg/xsjcxx/xssssq/dormApplication.ts b/web/src/api/xg/xsjcxx/xssssq/dormApplication.ts new file mode 100644 index 00000000..528b44a4 --- /dev/null +++ b/web/src/api/xg/xsjcxx/xssssq/dormApplication.ts @@ -0,0 +1,81 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { DormApplicationVO, DormApplicationForm, DormApplicationQuery, StudentOptionVO, StudentOptionQuery } from './types'; + +/** + * 查询学生宿舍申请单表(调宿+退宿)列表 + * @param query + * @returns {*} + */ + +export const listDormApplication = (query?: DormApplicationQuery): AxiosPromise => { + return request({ + url: '/dormApplication/dormApplication/list', + method: 'get', + params: query + }); +}; + +/** + * 查询学生宿舍申请单表(调宿+退宿)详细 + * @param id + */ +export const getDormApplication = (id: string | number): AxiosPromise => { + return request({ + url: '/dormApplication/dormApplication/' + id, + method: 'get' + }); +}; + +/** + * 新增学生宿舍申请单表(调宿+退宿) + * @param data + */ +export const addDormApplication = (data: DormApplicationForm) => { + return request({ + url: '/dormApplication/dormApplication', + method: 'post', + data: data + }); +}; + +/** + * 修改学生宿舍申请单表(调宿+退宿) + * @param data + */ +export const updateDormApplication = (data: DormApplicationForm) => { + return request({ + url: '/dormApplication/dormApplication', + method: 'put', + data: data + }); +}; + +/** + * 删除学生宿舍申请单表(调宿+退宿) + * @param id + */ +export const delDormApplication = (id: string | number | Array) => { + return request({ + url: '/dormApplication/dormApplication/' + id, + method: 'delete' + }); +}; + +/** + * 获取学生选择器选项列表(支持按学号、姓名检索) + * @param query 查询参数 + * @returns 学生选项列表 + */ +export const getStudentOptions = ( + query?: StudentOptionQuery +): AxiosPromise<{ + total: number; + rows: StudentOptionVO[]; +}> => { + return request({ + url: '/holidayStay/holidayStay/studentOptions', + method: 'get', + params: query + }); +}; diff --git a/web/src/api/xg/xsjcxx/xssssq/types.ts b/web/src/api/xg/xsjcxx/xssssq/types.ts new file mode 100644 index 00000000..af370973 --- /dev/null +++ b/web/src/api/xg/xsjcxx/xssssq/types.ts @@ -0,0 +1,586 @@ +export interface PageQuery { + pageNum?: number; + pageSize?: number; +} + +export interface DormApplicationVO { + /** + * 主键ID(雪花ID) + */ + id: string | number; + + /** + * 申请名称 + */ + applicationName: string; + + /** + * 申请类型(1:调宿申请 2:退宿申请) + */ + applicationType: string; + + /** + * 审批状态:cancel-已撤销 , draft-草稿, waiting-待审核, finish-已完成, invalid-已作废, back-已退回, termination-已终止 + */ + applicationStatus: string; + + /** + * 申请学生ID + */ + studentId: string | number; + + /** + * 学号 + */ + studentNo: string; + + /** + * 学生姓名 + */ + studentName: string; + + /** + * 学生联系电话 + */ + studentPhone: string; + + /** + * 学院ID + */ + collegeId: string | number; + + /** + * 学院名称 + */ + collegeName: string; + + /** + * 专业ID + */ + majorId: string | number; + + /** + * 专业名称 + */ + majorName: string; + + /** + * 班级ID + */ + classId: string | number; + + /** + * 班级名称 + */ + className: string; + + /** + * 当前校区ID + */ + currentCampusId: string | number; + + /** + * 当前校区名称 + */ + currentCampusName: string; + + /** + * 当前宿舍楼ID + */ + currentBuildingId: string | number; + + /** + * 当前宿舍楼名称 + */ + currentBuildingName: string; + + /** + * 当前房间ID + */ + currentRoomId: string | number; + + /** + * 当前房间号 + */ + currentRoomNo: string; + + /** + * 当前床位ID + */ + currentBedId: string | number; + + /** + * 当前床位号 + */ + currentBedNo: string; + + /** + * 目标校区ID + */ + targetCampusId: string | number; + + /** + * 目标校区名称 + */ + targetCampusName: string; + + /** + * 目标宿舍楼ID + */ + targetBuildingId: string | number; + + /** + * 目标宿舍楼名称 + */ + targetBuildingName: string; + + /** + * 目标房间ID + */ + targetRoomId: string | number; + + /** + * 目标房间号 + */ + targetRoomNo: string; + + /** + * 目标床位ID + */ + targetBedId: string | number; + + /** + * 目标床位号 + */ + targetBedNo: string; + + /** + * 详细原因说明 + */ + reasonDetail: string; + + /** + * 申请时间 + */ + applyTime: string; + + /** + * 附件OSS文件ID,多个用逗号分隔 + */ + attachmentOssIds: string; + + /** + * 备注信息 + */ + remark: string; + + /** + * 扩展字段1 + */ + extField1: string; + + /** + * 扩展字段2 + */ + extField2: string; + + /** + * 扩展字段3 + */ + extField3: string; + + /** + * 删除标志(0代表存在 1代表删除) + */ + deleted: string; + + /** + * 创建者 + */ + createBy: string | number; + + /** + * 创建时间 + */ + createTime: string; + + /** + * 更新者 + */ + updateBy: string | number; + + /** + * 更新时间 + */ + updateTime: string; +} + +export interface DormApplicationForm { + /** + * 主键ID(雪花ID) + */ + id?: string | number; + + /** + * 申请名称 + */ + applicationName?: string; + + /** + * 申请类型(1:调宿申请 2:退宿申请) + */ + applicationType?: string; + + /** + * 审批状态:cancel-已撤销 , draft-草稿, waiting-待审核, finish-已完成, invalid-已作废, back-已退回, termination-已终止 + */ + applicationStatus?: string; + + /** + * 申请学生ID + */ + studentId?: string | number; + + /** + * 学号 + */ + studentNo?: string; + + /** + * 学生姓名 + */ + studentName?: string; + + /** + * 学生联系电话 + */ + studentPhone?: string; + + /** + * 学院ID + */ + collegeId?: string | number; + + /** + * 学院名称 + */ + collegeName?: string; + + /** + * 专业ID + */ + majorId?: string | number; + + /** + * 专业名称 + */ + majorName?: string; + + /** + * 班级ID + */ + classId?: string | number; + + /** + * 班级名称 + */ + className?: string; + + /** + * 当前校区ID + */ + currentCampusId?: string | number; + + /** + * 当前校区名称 + */ + currentCampusName?: string; + + /** + * 当前宿舍楼ID + */ + currentBuildingId?: string | number; + + /** + * 当前宿舍楼名称 + */ + currentBuildingName?: string; + + /** + * 当前房间ID + */ + currentRoomId?: string | number; + + /** + * 当前房间号 + */ + currentRoomNo?: string; + + /** + * 当前床位ID + */ + currentBedId?: string | number; + + /** + * 当前床位号 + */ + currentBedNo?: string; + + /** + * 目标校区ID + */ + targetCampusId?: string | number; + + /** + * 目标校区名称 + */ + targetCampusName?: string; + + /** + * 目标宿舍楼ID + */ + targetBuildingId?: string | number; + + /** + * 目标宿舍楼名称 + */ + targetBuildingName?: string; + + /** + * 目标房间ID + */ + targetRoomId?: string | number; + + /** + * 目标房间号 + */ + targetRoomNo?: string; + + /** + * 目标床位ID + */ + targetBedId?: string | number; + + /** + * 目标床位号 + */ + targetBedNo?: string; + + /** + * 详细原因说明 + */ + reasonDetail?: string; + + /** + * 申请时间 + */ + applyTime?: string; + + /** + * 附件OSS文件ID,多个用逗号分隔 + */ + attachmentOssIds?: string; + + /** + * 紧急联系人姓名 + */ + emergencyContact?: string; + + /** + * 紧急联系人电话 + */ + emergencyPhone?: string; + + /** + * 备注信息 + */ + remark?: string; + + /** + * 扩展字段1 + */ + extField1?: string; + + /** + * 扩展字段2 + */ + extField2?: string; + + /** + * 扩展字段3 + */ + extField3?: string; +} + +export interface DormApplicationQuery extends PageQuery { + /** + * 申请名称 + */ + applicationName?: string; + + /** + * 申请类型(1:调宿申请 2:退宿申请) + */ + applicationType?: string; + + /** + * 审批状态:cancel-已撤销 , draft-草稿, waiting-待审核, finish-已完成, invalid-已作废, back-已退回, termination-已终止 + */ + applicationStatus?: string; + + /** + * 申请学生ID + */ + studentId?: string | number; + + /** + * 学号 + */ + studentNo?: string; + + /** + * 学生姓名 + */ + studentName?: string; + + /** + * 学生联系电话 + */ + studentPhone?: string; + + /** + * 学院ID + */ + collegeId?: string | number; + + /** + * 学院名称 + */ + collegeName?: string; + + /** + * 专业ID + */ + majorId?: string | number; + + /** + * 专业名称 + */ + majorName?: string; + + /** + * 班级ID + */ + classId?: string | number; + + /** + * 班级名称 + */ + className?: string; + + /** + * 申请时间 + */ + applyTime?: string; + + /** + * 日期范围参数 + */ + params?: any; +} + +/** + * 学生选择器选项 + */ +export interface StudentOptionVO { + /** + * 学生ID + */ + id: string | number; + + /** + * 学号 + */ + studentNo: string; + + /** + * 学生姓名 + */ + name: string; + + /** + * 性别:0-男,1-女 + */ + gender: string; + + /** + * 学院ID + */ + collegeId?: string | number; + + /** + * 学院名称 + */ + collegeName?: string; + + /** + * 专业ID + */ + majorId?: string | number; + + /** + * 专业名称 + */ + majorName?: string; + + /** + * 班级ID + */ + classId?: string | number; + + /** + * 班级名称 + */ + className?: string; + + /** + * 学生联系电话 + */ + phone?: string; + + /** + * 宿舍信息 + */ + dormInfo?: { + campusId?: string | number; + campusName?: string; + areaId?: string | number; + areaName?: string; + buildingId?: string | number; + buildingName?: string; + roomId?: string | number; + roomName?: string; + bedId?: string | number; + bedName?: string; + }; +} + +/** + * 学生选择器查询参数 + */ +export interface StudentOptionQuery { + /** + * 搜索关键字(学号或姓名) + */ + keyword?: string; + + /** + * 页码 + */ + pageNum?: number; + + /** + * 每页大小 + */ + pageSize?: number; +} diff --git a/web/src/views/xg/xgss/holidayStay/holidayStay/index-sg.vue b/web/src/views/xg/xgss/holidayStay/holidayStay/index-sg.vue index 8e042dea..b159c075 100644 --- a/web/src/views/xg/xgss/holidayStay/holidayStay/index-sg.vue +++ b/web/src/views/xg/xgss/holidayStay/holidayStay/index-sg.vue @@ -545,23 +545,6 @@ - - - - - - - - @@ -574,7 +557,7 @@ :loading="dormLoading" @change="handleBuildingChange" style="width: 100%" - :disabled="!form.areaId" + :disabled="!form.campusId" > @@ -849,7 +832,6 @@ const studentSelectVisible = ref(false); // 宿舍级联选择器相关状态 const campusOptions = ref([]); -const areaOptions = ref([]); const buildingOptions = ref([]); const roomOptions = ref([]); const bedOptions = ref([]); @@ -880,8 +862,6 @@ const initFormData: HolidayStayForm = { collegeName: undefined, campusId: undefined, campusName: undefined, - areaId: undefined, - areaName: undefined, buildingId: undefined, buildingName: undefined, roomId: undefined, @@ -920,8 +900,6 @@ const data = reactive>({ collegeName: undefined, campusId: undefined, campusName: undefined, - areaId: undefined, - areaName: undefined, buildingId: undefined, buildingName: undefined, roomId: undefined, @@ -1111,7 +1089,6 @@ const reset = () => { holidayStayFormRef.value?.resetFields(); // 清空宿舍级联选项 - areaOptions.value = []; buildingOptions.value = []; roomOptions.value = []; bedOptions.value = []; @@ -1170,8 +1147,6 @@ const handleUpdate = async (row?: HolidayStayVO) => { dormInfo: { campusId: form.value.campusId, campusName: form.value.campusName, - areaId: form.value.areaId, - areaName: form.value.areaName, buildingId: form.value.buildingId, buildingName: form.value.buildingName, roomId: form.value.roomId, @@ -1335,8 +1310,6 @@ const handleStudentChange = async (studentId: string | number) => { // 清空宿舍信息及选项 form.value.campusId = undefined; form.value.campusName = undefined; - form.value.areaId = undefined; - form.value.areaName = undefined; form.value.buildingId = undefined; form.value.buildingName = undefined; form.value.roomId = undefined; @@ -1344,7 +1317,6 @@ const handleStudentChange = async (studentId: string | number) => { form.value.bedId = undefined; form.value.bedName = undefined; - areaOptions.value = []; buildingOptions.value = []; roomOptions.value = []; bedOptions.value = []; @@ -1372,8 +1344,6 @@ const handleStudentChange = async (studentId: string | number) => { // 填充宿舍信息 form.value.campusId = selectedStudent.dormInfo.campusId; form.value.campusName = selectedStudent.dormInfo.campusName; - form.value.areaId = selectedStudent.dormInfo.areaId; - form.value.areaName = selectedStudent.dormInfo.areaName; form.value.buildingId = selectedStudent.dormInfo.buildingId; form.value.buildingName = selectedStudent.dormInfo.buildingName; form.value.roomId = selectedStudent.dormInfo.roomId; @@ -1384,17 +1354,13 @@ const handleStudentChange = async (studentId: string | number) => { // 级联加载对应的选项 try { if (selectedStudent.dormInfo.campusId) { - await loadAreaOptions(selectedStudent.dormInfo.campusId); + await loadBuildingOptions(selectedStudent.dormInfo.campusId); - if (selectedStudent.dormInfo.areaId) { - await loadBuildingOptions(selectedStudent.dormInfo.areaId); + if (selectedStudent.dormInfo.buildingId) { + await loadRoomOptions(selectedStudent.dormInfo.buildingId); - if (selectedStudent.dormInfo.buildingId) { - await loadRoomOptions(selectedStudent.dormInfo.buildingId); - - if (selectedStudent.dormInfo.roomId) { - await loadBedOptions(selectedStudent.dormInfo.roomId); - } + if (selectedStudent.dormInfo.roomId) { + await loadBedOptions(selectedStudent.dormInfo.roomId); } } } @@ -1405,8 +1371,6 @@ const handleStudentChange = async (studentId: string | number) => { // 如果宿舍信息为空,清空宿舍相关字段 form.value.campusId = undefined; form.value.campusName = undefined; - form.value.areaId = undefined; - form.value.areaName = undefined; form.value.buildingId = undefined; form.value.buildingName = undefined; form.value.roomId = undefined; @@ -1414,7 +1378,6 @@ const handleStudentChange = async (studentId: string | number) => { form.value.bedId = undefined; form.value.bedName = undefined; - areaOptions.value = []; buildingOptions.value = []; roomOptions.value = []; bedOptions.value = []; @@ -1426,7 +1389,10 @@ const handleStudentChange = async (studentId: string | number) => { const loadCampusOptions = async () => { try { dormLoading.value = true; - const res = await getCampusOptions(); + const res = await request({ + url: '/xqxx/dormCampus/list', + method: 'get' + }); campusOptions.value = res.rows || []; } catch (error) { console.error('加载校区选项失败:', error); @@ -1440,8 +1406,6 @@ const loadCampusOptions = async () => { const handleCampusChange = async (campusId: string | number) => { console.log('校区选择变化处理', campusId); // 清空下级选项 - form.value.areaId = undefined; - form.value.areaName = undefined; form.value.buildingId = undefined; form.value.buildingName = undefined; form.value.roomId = undefined; @@ -1449,7 +1413,6 @@ const handleCampusChange = async (campusId: string | number) => { form.value.bedId = undefined; form.value.bedName = undefined; - areaOptions.value = []; buildingOptions.value = []; roomOptions.value = []; bedOptions.value = []; @@ -1462,63 +1425,23 @@ const handleCampusChange = async (campusId: string | number) => { form.value.campusName = selectedCampus.campusName; } - // 加载宿舍区选项 - await loadAreaOptions(campusId); + // 直接加载宿舍楼选项 + await loadBuildingOptions(campusId); } else { form.value.campusId = undefined; form.value.campusName = undefined; } }; -/** 加载宿舍区选项 */ -const loadAreaOptions = async (campusId: string | number) => { - try { - dormLoading.value = true; - const res = await getAreaOptions(campusId); - areaOptions.value = res.rows || []; - } catch (error) { - console.error('加载宿舍区选项失败:', error); - areaOptions.value = []; - } finally { - dormLoading.value = false; - } -}; - -/** 宿舍区选择变化处理 */ -const handleAreaChange = async (areaId: string | number) => { - // 清空下级选项 - form.value.buildingId = undefined; - form.value.buildingName = undefined; - form.value.roomId = undefined; - form.value.roomName = undefined; - form.value.bedId = undefined; - form.value.bedName = undefined; - - buildingOptions.value = []; - roomOptions.value = []; - bedOptions.value = []; - - if (areaId) { - // 设置宿舍区名称 - const selectedArea = areaOptions.value.find((area) => area.id === areaId); - if (selectedArea) { - form.value.areaId = areaId; - form.value.areaName = selectedArea.areaName; - } - - // 加载宿舍楼选项 - await loadBuildingOptions(areaId); - } else { - form.value.areaId = undefined; - form.value.areaName = undefined; - } -}; - /** 加载宿舍楼选项 */ -const loadBuildingOptions = async (areaId: string | number) => { +const loadBuildingOptions = async (campusId: string | number) => { try { dormLoading.value = true; - const res = await getBuildingOptions(areaId); + const res = await request({ + url: '/sslxx/dormBuilding/list', + method: 'get', + params: { campusId } + }); buildingOptions.value = res.rows || []; } catch (error) { console.error('加载宿舍楼选项失败:', error); @@ -1625,22 +1548,17 @@ const handleBedChange = (bedId: string | number) => { /** 修改时级联加载宿舍选项数据 */ const loadCascadeOptionsForUpdate = async (formData: HolidayStayForm) => { try { - // 如果有校区ID,加载宿舍区选项 + // 如果有校区ID,加载宿舍楼选项 if (formData.campusId) { - await loadAreaOptions(formData.campusId); + await loadBuildingOptions(formData.campusId); - // 如果有宿舍区ID,加载宿舍楼选项 - if (formData.areaId) { - await loadBuildingOptions(formData.areaId); + // 如果有宿舍楼ID,加载房间选项 + if (formData.buildingId) { + await loadRoomOptions(formData.buildingId); - // 如果有宿舍楼ID,加载房间选项 - if (formData.buildingId) { - await loadRoomOptions(formData.buildingId); - - // 如果有房间ID,加载床位选项 - if (formData.roomId) { - await loadBedOptions(formData.roomId); - } + // 如果有房间ID,加载床位选项 + if (formData.roomId) { + await loadBedOptions(formData.roomId); } } } diff --git a/web/src/views/xg/xgss/holidayStay/holidayStay/index.vue b/web/src/views/xg/xgss/holidayStay/holidayStay/index.vue index 166e58bc..6a05e9a1 100644 --- a/web/src/views/xg/xgss/holidayStay/holidayStay/index.vue +++ b/web/src/views/xg/xgss/holidayStay/holidayStay/index.vue @@ -337,23 +337,6 @@ - - - - - - - - @@ -366,7 +349,7 @@ :loading="dormLoading" @change="handleBuildingChange" style="width: 100%" - :disabled="!form.areaId" + :disabled="!form.campusId" > @@ -631,7 +614,6 @@ const studentSelectVisible = ref(false); // 宿舍级联选择器相关状态 const campusOptions = ref([]); -const areaOptions = ref([]); const buildingOptions = ref([]); const roomOptions = ref([]); const bedOptions = ref([]); @@ -662,8 +644,6 @@ const initFormData: HolidayStayForm = { collegeName: undefined, campusId: undefined, campusName: undefined, - areaId: undefined, - areaName: undefined, buildingId: undefined, buildingName: undefined, roomId: undefined, @@ -702,8 +682,6 @@ const data = reactive>({ collegeName: undefined, campusId: undefined, campusName: undefined, - areaId: undefined, - areaName: undefined, buildingId: undefined, buildingName: undefined, roomId: undefined, @@ -813,7 +791,6 @@ const reset = () => { holidayStayFormRef.value?.resetFields(); // 清空宿舍级联选项 - areaOptions.value = []; buildingOptions.value = []; roomOptions.value = []; bedOptions.value = []; @@ -872,8 +849,6 @@ const handleUpdate = async (row?: HolidayStayVO) => { dormInfo: { campusId: form.value.campusId, campusName: form.value.campusName, - areaId: form.value.areaId, - areaName: form.value.areaName, buildingId: form.value.buildingId, buildingName: form.value.buildingName, roomId: form.value.roomId, @@ -1037,8 +1012,6 @@ const handleStudentChange = async (studentId: string | number) => { // 清空宿舍信息及选项 form.value.campusId = undefined; form.value.campusName = undefined; - form.value.areaId = undefined; - form.value.areaName = undefined; form.value.buildingId = undefined; form.value.buildingName = undefined; form.value.roomId = undefined; @@ -1046,7 +1019,6 @@ const handleStudentChange = async (studentId: string | number) => { form.value.bedId = undefined; form.value.bedName = undefined; - areaOptions.value = []; buildingOptions.value = []; roomOptions.value = []; bedOptions.value = []; @@ -1074,8 +1046,6 @@ const handleStudentChange = async (studentId: string | number) => { // 填充宿舍信息 form.value.campusId = selectedStudent.dormInfo.campusId; form.value.campusName = selectedStudent.dormInfo.campusName; - form.value.areaId = selectedStudent.dormInfo.areaId; - form.value.areaName = selectedStudent.dormInfo.areaName; form.value.buildingId = selectedStudent.dormInfo.buildingId; form.value.buildingName = selectedStudent.dormInfo.buildingName; form.value.roomId = selectedStudent.dormInfo.roomId; @@ -1086,17 +1056,13 @@ const handleStudentChange = async (studentId: string | number) => { // 级联加载对应的选项 try { if (selectedStudent.dormInfo.campusId) { - await loadAreaOptions(selectedStudent.dormInfo.campusId); + await loadBuildingOptions(selectedStudent.dormInfo.campusId); - if (selectedStudent.dormInfo.areaId) { - await loadBuildingOptions(selectedStudent.dormInfo.areaId); + if (selectedStudent.dormInfo.buildingId) { + await loadRoomOptions(selectedStudent.dormInfo.buildingId); - if (selectedStudent.dormInfo.buildingId) { - await loadRoomOptions(selectedStudent.dormInfo.buildingId); - - if (selectedStudent.dormInfo.roomId) { - await loadBedOptions(selectedStudent.dormInfo.roomId); - } + if (selectedStudent.dormInfo.roomId) { + await loadBedOptions(selectedStudent.dormInfo.roomId); } } } @@ -1107,8 +1073,6 @@ const handleStudentChange = async (studentId: string | number) => { // 如果宿舍信息为空,清空宿舍相关字段 form.value.campusId = undefined; form.value.campusName = undefined; - form.value.areaId = undefined; - form.value.areaName = undefined; form.value.buildingId = undefined; form.value.buildingName = undefined; form.value.roomId = undefined; @@ -1116,7 +1080,6 @@ const handleStudentChange = async (studentId: string | number) => { form.value.bedId = undefined; form.value.bedName = undefined; - areaOptions.value = []; buildingOptions.value = []; roomOptions.value = []; bedOptions.value = []; @@ -1128,7 +1091,10 @@ const handleStudentChange = async (studentId: string | number) => { const loadCampusOptions = async () => { try { dormLoading.value = true; - const res = await getCampusOptions(); + const res = await request({ + url: '/xqxx/dormCampus/list', + method: 'get' + }); campusOptions.value = res.rows || []; } catch (error) { console.error('加载校区选项失败:', error); @@ -1142,8 +1108,6 @@ const loadCampusOptions = async () => { const handleCampusChange = async (campusId: string | number) => { console.log('校区选择变化处理', campusId); // 清空下级选项 - form.value.areaId = undefined; - form.value.areaName = undefined; form.value.buildingId = undefined; form.value.buildingName = undefined; form.value.roomId = undefined; @@ -1151,7 +1115,6 @@ const handleCampusChange = async (campusId: string | number) => { form.value.bedId = undefined; form.value.bedName = undefined; - areaOptions.value = []; buildingOptions.value = []; roomOptions.value = []; bedOptions.value = []; @@ -1164,63 +1127,23 @@ const handleCampusChange = async (campusId: string | number) => { form.value.campusName = selectedCampus.campusName; } - // 加载宿舍区选项 - await loadAreaOptions(campusId); + // 直接加载宿舍楼选项 + await loadBuildingOptions(campusId); } else { form.value.campusId = undefined; form.value.campusName = undefined; } }; -/** 加载宿舍区选项 */ -const loadAreaOptions = async (campusId: string | number) => { - try { - dormLoading.value = true; - const res = await getAreaOptions(campusId); - areaOptions.value = res.rows || []; - } catch (error) { - console.error('加载宿舍区选项失败:', error); - areaOptions.value = []; - } finally { - dormLoading.value = false; - } -}; - -/** 宿舍区选择变化处理 */ -const handleAreaChange = async (areaId: string | number) => { - // 清空下级选项 - form.value.buildingId = undefined; - form.value.buildingName = undefined; - form.value.roomId = undefined; - form.value.roomName = undefined; - form.value.bedId = undefined; - form.value.bedName = undefined; - - buildingOptions.value = []; - roomOptions.value = []; - bedOptions.value = []; - - if (areaId) { - // 设置宿舍区名称 - const selectedArea = areaOptions.value.find((area) => area.id === areaId); - if (selectedArea) { - form.value.areaId = areaId; - form.value.areaName = selectedArea.areaName; - } - - // 加载宿舍楼选项 - await loadBuildingOptions(areaId); - } else { - form.value.areaId = undefined; - form.value.areaName = undefined; - } -}; - /** 加载宿舍楼选项 */ -const loadBuildingOptions = async (areaId: string | number) => { +const loadBuildingOptions = async (campusId: string | number) => { try { dormLoading.value = true; - const res = await getBuildingOptions(areaId); + const res = await request({ + url: '/sslxx/dormBuilding/list', + method: 'get', + params: { campusId } + }); buildingOptions.value = res.rows || []; } catch (error) { console.error('加载宿舍楼选项失败:', error); @@ -1327,22 +1250,17 @@ const handleBedChange = (bedId: string | number) => { /** 修改时级联加载宿舍选项数据 */ const loadCascadeOptionsForUpdate = async (formData: HolidayStayForm) => { try { - // 如果有校区ID,加载宿舍区选项 + // 如果有校区ID,加载宿舍楼选项 if (formData.campusId) { - await loadAreaOptions(formData.campusId); + await loadBuildingOptions(formData.campusId); - // 如果有宿舍区ID,加载宿舍楼选项 - if (formData.areaId) { - await loadBuildingOptions(formData.areaId); + // 如果有宿舍楼ID,加载房间选项 + if (formData.buildingId) { + await loadRoomOptions(formData.buildingId); - // 如果有宿舍楼ID,加载房间选项 - if (formData.buildingId) { - await loadRoomOptions(formData.buildingId); - - // 如果有房间ID,加载床位选项 - if (formData.roomId) { - await loadBedOptions(formData.roomId); - } + // 如果有房间ID,加载床位选项 + if (formData.roomId) { + await loadBedOptions(formData.roomId); } } } diff --git a/web/src/views/xg/xsjcxx/xssssq/dialog.vue b/web/src/views/xg/xsjcxx/xssssq/dialog.vue new file mode 100644 index 00000000..021ce8e3 --- /dev/null +++ b/web/src/views/xg/xsjcxx/xssssq/dialog.vue @@ -0,0 +1,2282 @@ + + + + + diff --git a/web/src/views/xg/xsjcxx/xssssq/index.vue b/web/src/views/xg/xsjcxx/xssssq/index.vue new file mode 100644 index 00000000..822ab7ec --- /dev/null +++ b/web/src/views/xg/xsjcxx/xssssq/index.vue @@ -0,0 +1,369 @@ + + + + + From 21856615db4ef2777397c98393d82ec2ce5228a2 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Mon, 8 Sep 2025 22:51:54 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../XgDormApplicationController.java | 24 ++ .../XgDormBedStudentController.java | 11 + .../xg/mapper/XgDormBedStudentMapper.java | 43 +++ .../xg/service/IXgDormApplicationService.java | 16 + .../xg/service/IXgDormBedStudentService.java | 9 + .../impl/XgDormApplicationServiceImpl.java | 116 ++++++ .../impl/XgDormBedStudentServiceImpl.java | 59 +++ web/src/views/xg/xsjcxx/xssssq/dialog.vue | 357 +++++++++++------- web/src/views/xg/xsjcxx/xssssq/index.vue | 2 +- 9 files changed, 504 insertions(+), 133 deletions(-) diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormApplicationController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormApplicationController.java index 2b90f3e7..47eac861 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormApplicationController.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormApplicationController.java @@ -23,6 +23,8 @@ import org.dromara.xg.domain.vo.StudentOptionVo; import org.dromara.xg.domain.bo.StudentOptionBo; import org.dromara.xg.service.IXgDormApplicationService; import org.dromara.common.mybatis.core.page.TableDataInfo; +import java.util.Map; +import java.util.HashMap; /** * 学生宿舍申请单表(调宿+退宿) @@ -113,4 +115,26 @@ public class XgDormApplicationController extends BaseController { return toAjax(xgDormApplicationService.deleteWithValidByIds(List.of(ids), true)); } + /** + * 获取二级学院审批人列表 + * + * @param collegeId 学院ID + */ + @SaCheckPermission("dormApplication:dormApplication:list") + @GetMapping("/getCollegeApprovers") + public R>> getCollegeApprovers(@RequestParam Long collegeId) { + List> approvers = xgDormApplicationService.getCollegeApprovers(collegeId); + return R.ok(approvers); + } + + /** + * 获取学工处审批人列表 + */ + @SaCheckPermission("dormApplication:dormApplication:list") + @GetMapping("/getStudentAffairsApprovers") + public R>> getStudentAffairsApprovers() { + List> approvers = xgDormApplicationService.getStudentAffairsApprovers(); + return R.ok(approvers); + } + } 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 d2b75526..2ebc45b4 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 @@ -343,4 +343,15 @@ public class XgDormBedStudentController extends BaseController { return R.ok(currentDormInfo); } + /** + * 根据床位ID获取该床位的学生信息 + * 包含学生基本信息、学院、专业、班级等信息 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:list") + @GetMapping("/bedStudentInfo/{bedId}") + public R getBedStudentInfo(@NotNull(message = "床位ID不能为空") @PathVariable Long bedId) { + Object bedStudentInfo = xgDormBedStudentService.getBedStudentInfo(bedId); + return R.ok(bedStudentInfo); + } + } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java index 26a1569e..cc7d3b58 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java @@ -236,4 +236,47 @@ public interface XgDormBedStudentMapper extends BaseMapperPlus selectCurrentDormInfo(@Param("studentId") Long studentId); + /** + * 根据床位ID获取该床位的学生信息 + * 包含学生基本信息、学院、专业、班级等信息 + * + * @param bedId 床位ID + * @return 床位学生信息 + */ + @Select(""" + SELECT + bs.student_id, + bs.bed_id, + + -- 学生基本信息 + s.student_no, + s.name as student_name, + s.gender, + s.phone, + + -- 班级信息 + c.class_name, + + -- 学院信息 + d.dept_name as college_name, + + -- 专业信息 + major.dept_name as major_name + + FROM xg_dorm_bed_student bs + INNER JOIN xg_student_msg s ON bs.student_id = s.id AND s.deleted = 0 + LEFT JOIN xg_class_msg c ON s.class_id = c.id AND c.deleted = 0 + LEFT JOIN sys_dept d ON c.college_id = d.dept_id AND d.del_flag = '0' + LEFT JOIN sys_dept major ON c.major_id = major.dept_id AND major.del_flag = '0' + + WHERE bs.bed_id = #{bedId} + AND bs.student_id IS NOT NULL + AND bs.status = '0' + AND bs.deleted = '0' + AND s.student_status = '1' + + LIMIT 1 + """) + Map selectBedStudentInfo(@Param("bedId") Long bedId); + } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormApplicationService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormApplicationService.java index 935c1b6c..03559e59 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormApplicationService.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormApplicationService.java @@ -9,6 +9,7 @@ import org.dromara.common.mybatis.core.page.PageQuery; import java.util.Collection; import java.util.List; +import java.util.Map; /** * 学生宿舍申请单表(调宿+退宿)Service接口 @@ -77,4 +78,19 @@ public interface IXgDormApplicationService { */ TableDataInfo getStudentOptions(StudentOptionBo bo, PageQuery pageQuery); + /** + * 获取二级学院审批人列表 + * + * @param collegeId 学院ID + * @return 审批人列表 + */ + List> getCollegeApprovers(Long collegeId); + + /** + * 获取学工处审批人列表 + * + * @return 审批人列表 + */ + List> getStudentAffairsApprovers(); + } 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 cd5d867e..645faf1d 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 @@ -251,4 +251,13 @@ public interface IXgDormBedStudentService { * @return 当前宿舍信息 */ Object getCurrentDormInfo(Long studentId); + + /** + * 根据床位ID获取该床位的学生信息 + * 包含学生基本信息、学院、专业、班级等信息 + * + * @param bedId 床位ID + * @return 床位学生信息 + */ + Object getBedStudentInfo(Long bedId); } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java index 61bf0524..ea73d16d 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java @@ -30,7 +30,12 @@ import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.mapper.SysDeptMapper; +import org.dromara.system.mapper.SysUserMapper; +import org.dromara.system.mapper.SysUserRoleMapper; +import org.dromara.system.mapper.SysRoleMapper; import org.dromara.system.domain.SysDept; +import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.SysRole; import org.dromara.xg.mapper.XgStudentManagerMapper; import org.dromara.xg.mapper.XgStudentManagerSourceMapper; import org.dromara.xg.mapper.XgClassMsgMapper; @@ -64,6 +69,9 @@ public class XgDormApplicationServiceImpl implements IXgDormApplicationService { private final XgDormBedStudentMapper xgDormBedStudentMapper; // 添加权限过滤需要的依赖 private final SysDeptMapper sysDeptMapper; + private final SysUserMapper sysUserMapper; + private final SysUserRoleMapper sysUserRoleMapper; + private final SysRoleMapper sysRoleMapper; private final XgStudentManagerMapper xgStudentManagerMapper; private final XgStudentManagerSourceMapper xgStudentManagerSourceMapper; private final XgClassMsgMapper xgClassMsgMapper; @@ -493,4 +501,112 @@ public class XgDormApplicationServiceImpl implements IXgDormApplicationService { return TableDataInfo.build(result); } + /** + * 获取二级学院审批人列表 + * + * @param collegeId 学院ID + * @return 审批人列表 + */ + @Override + public List> getCollegeApprovers(Long collegeId) { + try { + // 查询角色key为'ejxyssgly'的角色ID + LambdaQueryWrapper roleWrapper = new LambdaQueryWrapper<>(); + roleWrapper.eq(SysRole::getRoleKey, "ejxyssgly"); + SysRole role = sysRoleMapper.selectOne(roleWrapper); + + if (role == null) { + log.warn("未找到角色key为'ejxyssgly'的角色"); + return new ArrayList<>(); + } + + // 通过角色ID查询用户角色关系表 + LambdaQueryWrapper userRoleWrapper = new LambdaQueryWrapper<>(); + userRoleWrapper.eq(org.dromara.system.domain.SysUserRole::getRoleId, role.getRoleId()); + List userRoles = sysUserRoleMapper.selectList(userRoleWrapper); + + if (userRoles.isEmpty()) { + return new ArrayList<>(); + } + + // 获取用户ID列表 + List userIds = userRoles.stream() + .map(org.dromara.system.domain.SysUserRole::getUserId) + .collect(Collectors.toList()); + + // 查询用户信息,过滤指定学院的用户 + LambdaQueryWrapper userWrapper = new LambdaQueryWrapper<>(); + userWrapper.in(SysUser::getUserId, userIds) + .eq(SysUser::getDeptId, collegeId) + .eq(SysUser::getStatus, "0"); // 只查询启用状态的用户 + List users = sysUserMapper.selectList(userWrapper); + + // 转换为Map格式返回 + return users.stream().map(user -> { + Map approver = MapUtil.newHashMap(); + approver.put("userId", user.getUserId()); + approver.put("userName", user.getUserName()); + approver.put("nickName", user.getNickName()); + approver.put("deptId", user.getDeptId()); + return approver; + }).collect(Collectors.toList()); + } catch (Exception e) { + log.error("获取二级学院审批人列表失败", e); + return new ArrayList<>(); + } + } + + /** + * 获取学工处审批人列表 + * + * @return 审批人列表 + */ + @Override + public List> getStudentAffairsApprovers() { + try { + // 查询角色key为'xscxsxxgly'的角色ID + LambdaQueryWrapper roleWrapper = new LambdaQueryWrapper<>(); + roleWrapper.eq(SysRole::getRoleKey, "xscxsxxgly"); + SysRole role = sysRoleMapper.selectOne(roleWrapper); + + if (role == null) { + log.warn("未找到角色key为'xscxsxxgly'的角色"); + return new ArrayList<>(); + } + + // 通过角色ID查询用户角色关系表 + LambdaQueryWrapper userRoleWrapper = new LambdaQueryWrapper<>(); + userRoleWrapper.eq(org.dromara.system.domain.SysUserRole::getRoleId, role.getRoleId()); + List userRoles = sysUserRoleMapper.selectList(userRoleWrapper); + + if (userRoles.isEmpty()) { + return new ArrayList<>(); + } + + // 获取用户ID列表 + List userIds = userRoles.stream() + .map(org.dromara.system.domain.SysUserRole::getUserId) + .collect(Collectors.toList()); + + // 查询用户信息 + LambdaQueryWrapper userWrapper = new LambdaQueryWrapper<>(); + userWrapper.in(SysUser::getUserId, userIds) + .eq(SysUser::getStatus, "0"); // 只查询启用状态的用户 + List users = sysUserMapper.selectList(userWrapper); + + // 转换为Map格式返回 + return users.stream().map(user -> { + Map approver = MapUtil.newHashMap(); + approver.put("userId", user.getUserId()); + approver.put("userName", user.getUserName()); + approver.put("nickName", user.getNickName()); + approver.put("deptId", user.getDeptId()); + return approver; + }).collect(Collectors.toList()); + } catch (Exception e) { + log.error("获取学工处审批人列表失败", e); + return new ArrayList<>(); + } + } + } 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 909d2cd6..397d2ef9 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 @@ -2568,4 +2568,63 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { } } + /** + * 根据床位ID获取该床位的学生信息 + * 包含学生基本信息、学院、专业、班级等信息 + * + * @param bedId 床位ID + * @return 床位学生信息 + */ + @Override + public Object getBedStudentInfo(Long bedId) { + try { + log.info("开始获取床位学生信息,床位ID: {}", bedId); + + if (bedId == null) { + log.warn("床位ID为空,无法获取学生信息"); + return null; + } + + // 使用SQL关联查询,获取床位学生信息 + Map studentInfo = baseMapper.selectBedStudentInfo(bedId); + + if (studentInfo == null || studentInfo.isEmpty()) { + log.warn("床位ID: {} 未找到学生信息", bedId); + return null; + } + + // 组装返回结果 + Map result = new LinkedHashMap<>(); + + // 学生基本信息 + result.put("studentId", studentInfo.get("student_id")); + result.put("studentNo", studentInfo.get("student_no")); + result.put("studentName", studentInfo.get("student_name")); + result.put("gender", studentInfo.get("gender")); + result.put("phone", studentInfo.get("phone")); + + // 学院、专业、班级信息 + result.put("collegeName", studentInfo.get("college_name")); + result.put("majorName", studentInfo.get("major_name")); + result.put("className", studentInfo.get("class_name")); + + // 床位信息 + result.put("bedId", studentInfo.get("bed_id")); + + log.info("获取床位学生信息成功,床位ID: {}, 学生信息: {}({})-{}-{}-{}", + bedId, + result.get("studentName"), + result.get("studentNo"), + result.get("collegeName"), + result.get("majorName"), + result.get("className")); + + return result; + + } catch (Exception e) { + log.error("获取床位学生信息失败,床位ID: {}", bedId, e); + return null; + } + } + } diff --git a/web/src/views/xg/xsjcxx/xssssq/dialog.vue b/web/src/views/xg/xsjcxx/xssssq/dialog.vue index 021ce8e3..d9e83a0b 100644 --- a/web/src/views/xg/xsjcxx/xssssq/dialog.vue +++ b/web/src/views/xg/xsjcxx/xssssq/dialog.vue @@ -84,75 +84,25 @@ - - -
-
学生基础信息
- - -
- 学生姓名: - {{ form.studentName || '-' }} + + + +
+
+
+ 学生信息:{{ form.studentName || '-' }} / {{ form.studentNo || '-' }} / {{ form.collegeName || '-' }} / + {{ form.majorName || '-' }} / {{ form.className || '-' }} / {{ form.studentPhone || '-' }} +
- - -
- 学号: - {{ form.studentNo || '-' }} +
+
+ 床位信息:{{ form.currentCampusName || '-' }} / {{ form.currentBuildingName || '-' }} / {{ form.currentRoomNo || '-' }} / + {{ form.currentBedNo || '-' }} +
- - -
- 班级名称: - {{ form.className || '-' }} -
-
- -
- 联系电话: - {{ form.studentPhone || '-' }} -
-
- -
- 专业名称: - {{ form.majorName || '-' }} -
-
- -
- 学院名称: - {{ form.collegeName || '-' }} -
-
- - - -
- 校区: - {{ form.currentCampusName || '-' }} -
-
- -
- 宿舍楼: - {{ form.currentBuildingName || '-' }} -
-
- -
- 房间号: - {{ form.currentRoomNo || '-' }} -
-
- -
- 床位号: - {{ form.currentBedNo || '-' }} -
-
-
-
+
+
+
@@ -256,7 +206,7 @@ - + @@ -272,39 +222,34 @@ > + +
+
+ 学生信息:{{ form.targetBedStudentName || '-' }} / {{ form.targetBedStudentNo || '-' }} / + {{ form.targetBedStudentCollegeName || '-' }} / {{ form.targetBedStudentMajorName || '-' }} / + {{ form.targetBedStudentClassName || '-' }} +
+
- - - - - - - - + + +
- +
- - - - - - - - + + +
- - -
({ targetRoomNo: '', targetBedId: '', targetBedNo: '', + // 目标床位学生信息 + targetBedStudentId: '', + targetBedStudentName: '', + targetBedStudentNo: '', + targetBedStudentCollegeName: '', + targetBedStudentMajorName: '', + targetBedStudentClassName: '', applyTime: '', reasonDetail: '', - emergencyContact: '', - emergencyPhone: '', attachmentOssIds: '', applicationStatus: 'draft', approvalOpinion: '', @@ -924,22 +874,21 @@ const handleStartWorkFlow = async (data: DormApplicationForm) => { let xgcsprIds: number[] = []; try { - // 获取二级学院审批人(role_key为'ejxyssgly'的角色) + // 获取二级学院审批人 const ejxysprResponse = await request({ - url: '/system/user/listByRoleKey', + url: '/dormApplication/dormApplication/getCollegeApprovers', method: 'get', - params: { roleKey: 'ejxyssgly', collegeId: data.collegeId } + params: { collegeId: data.collegeId } }); if (ejxysprResponse && ejxysprResponse.data) { ejxysprIds = ejxysprResponse.data.map((user: any) => user.userId); console.log('获取到二级学院审批人列表:', ejxysprIds); } - // 获取学工处审批人(role_key为'xscxsxxgly'的角色) + // 获取学工处审批人 const xgcsprResponse = await request({ - url: '/system/user/listByRoleKey', - method: 'get', - params: { roleKey: 'xscxsxxgly' } + url: '/dormApplication/dormApplication/getStudentAffairsApprovers', + method: 'get' }); if (xgcsprResponse && xgcsprResponse.data) { xgcsprIds = xgcsprResponse.data.map((user: any) => user.userId); @@ -1087,11 +1036,21 @@ const getDormApplicationInfo = async (id: string | number) => { // 设置状态字段映射 form.status = applicationInfo.applicationStatus; - // 如果有学生ID,则获取学生信息 + // 如果有学生ID,则获取学生信息并添加到选项中 if (applicationInfo.studentId) { await getStudentInfo(applicationInfo.studentId); + // 将当前学生添加到学生选项列表中,确保下拉框能正确显示 + await addCurrentStudentToOptions(applicationInfo.studentId); } + // 如果有目标学生ID,则将目标学生添加到选项中 + if (applicationInfo.targetStudentId) { + await addCurrentTargetStudentToOptions(applicationInfo.targetStudentId); + } + + // 加载目标宿舍级联选项数据(修改时必须调用) + await loadTargetCascadeOptionsForUpdate(); + // 处理附件数据 await handleAttachmentData(); } else { @@ -1349,6 +1308,13 @@ const handleStudentChange = async (value: string | number) => { form.targetRoomNo = ''; form.targetBedId = ''; form.targetBedNo = ''; + // 清空目标床位学生信息 + form.targetBedStudentId = ''; + form.targetBedStudentName = ''; + form.targetBedStudentNo = ''; + form.targetBedStudentCollegeName = ''; + form.targetBedStudentMajorName = ''; + form.targetBedStudentClassName = ''; } }; @@ -1402,6 +1368,42 @@ const handleStudentSelectVisibleChange = (visible: boolean) => { } }; +// 将当前学生添加到学生选项列表中(用于数据回显) +const addCurrentStudentToOptions = async (studentId: string | number) => { + try { + console.log('将当前学生添加到选项列表,学生ID:', studentId); + const response = await getStudentMsg(studentId); + + if (response.code === 200 && response.data) { + const studentInfo = response.data; + + // 检查是否已存在该学生 + const existingStudent = studentOptions.value.find((student) => student.id === studentId); + if (!existingStudent) { + // 将学生信息添加到选项列表 + const studentOption: StudentOptionVO = { + id: studentInfo.id, + name: studentInfo.name || '', + studentNo: studentInfo.studentNo || '', + gender: studentInfo.gender || '', + phone: studentInfo.phone || '', + collegeId: studentInfo.collegeId, + collegeName: studentInfo.collegeName || '', + majorId: studentInfo.majorId, + majorName: studentInfo.majorName || '', + classId: studentInfo.classId, + className: studentInfo.className || '' + }; + + studentOptions.value = [studentOption]; + console.log('当前学生已添加到选项列表:', studentOption); + } + } + } catch (error) { + console.error('添加当前学生到选项列表失败:', error); + } +}; + // ==================== 目标学生选择相关方法 ==================== // 目标学生下拉框远程搜索 @@ -1445,6 +1447,42 @@ const handleTargetStudentSelectVisibleChange = (visible: boolean) => { } }; +// 将当前目标学生添加到目标学生选项列表中(用于数据回显) +const addCurrentTargetStudentToOptions = async (targetStudentId: string | number) => { + try { + console.log('将当前目标学生添加到选项列表,学生ID:', targetStudentId); + const response = await getStudentMsg(targetStudentId); + + if (response.code === 200 && response.data) { + const studentInfo = response.data; + + // 检查是否已存在该学生 + const existingStudent = targetStudentOptions.value.find((student) => student.id === targetStudentId); + if (!existingStudent) { + // 将学生信息添加到目标学生选项列表 + const studentOption: StudentOptionVO = { + id: studentInfo.id, + name: studentInfo.name || '', + studentNo: studentInfo.studentNo || '', + gender: studentInfo.gender || '', + phone: studentInfo.phone || '', + collegeId: studentInfo.collegeId, + collegeName: studentInfo.collegeName || '', + majorId: studentInfo.majorId, + majorName: studentInfo.majorName || '', + classId: studentInfo.classId, + className: studentInfo.className || '' + }; + + targetStudentOptions.value = [studentOption]; + console.log('当前目标学生已添加到选项列表:', studentOption); + } + } + } catch (error) { + console.error('添加当前目标学生到选项列表失败:', error); + } +}; + // 加载初始目标学生数据 const loadInitialTargetStudents = async () => { targetStudentLoading.value = true; @@ -1525,6 +1563,9 @@ const clearTargetDormInfo = () => { form.targetBedId = ''; form.targetBedNo = ''; + // 清空目标床位学生信息 + clearTargetBedStudentInfo(); + // 清空选项 targetBuildingOptions.value = []; targetRoomOptions.value = []; @@ -1687,7 +1728,7 @@ const loadTargetBedOptions = async (roomId: string | number) => { }; // 目标床位选择变化处理 -const handleTargetBedChange = (bedId: string | number) => { +const handleTargetBedChange = async (bedId: string | number) => { if (bedId) { // 设置床位名称 const selectedBed = targetBedOptions.value.find((bed) => bed.id === bedId); @@ -1695,9 +1736,14 @@ const handleTargetBedChange = (bedId: string | number) => { form.targetBedId = bedId; form.targetBedNo = selectedBed.bedNo || selectedBed.bedName; } + + // 获取该床位的学生信息 + await getTargetBedStudentInfo(bedId); } else { form.targetBedId = ''; form.targetBedNo = ''; + // 清空目标床位学生信息 + clearTargetBedStudentInfo(); } }; @@ -1715,6 +1761,11 @@ const loadTargetCascadeOptionsForUpdate = async () => { // 如果有房间ID,加载床位选项 if (form.targetRoomId) { await loadTargetBedOptions(form.targetRoomId); + + // 如果有床位ID,加载床位学生信息 + if (form.targetBedId) { + await getTargetBedStudentInfo(form.targetBedId); + } } } } @@ -1723,6 +1774,46 @@ const loadTargetCascadeOptionsForUpdate = async () => { } }; +// 获取目标床位的学生信息 +const getTargetBedStudentInfo = async (bedId: string | number) => { + try { + console.log('开始获取目标床位学生信息,床位ID:', bedId); + const response = await request({ + url: `/dormBedStudent/dormBedStudent/bedStudentInfo/${bedId}`, + method: 'get' + }); + + if (response.code === 200 && response.data) { + const studentInfo = response.data; + console.log('获取到的目标床位学生信息:', studentInfo); + + // 填充目标床位学生信息 + form.targetBedStudentId = studentInfo.studentId || ''; + form.targetBedStudentName = studentInfo.studentName || ''; + form.targetBedStudentNo = studentInfo.studentNo || ''; + form.targetBedStudentCollegeName = studentInfo.collegeName || ''; + form.targetBedStudentMajorName = studentInfo.majorName || ''; + form.targetBedStudentClassName = studentInfo.className || ''; + } else { + console.warn('该床位暂无学生信息'); + clearTargetBedStudentInfo(); + } + } catch (error) { + console.error('获取目标床位学生信息失败:', error); + clearTargetBedStudentInfo(); + } +}; + +// 清空目标床位学生信息 +const clearTargetBedStudentInfo = () => { + form.targetBedStudentId = ''; + form.targetBedStudentName = ''; + form.targetBedStudentNo = ''; + form.targetBedStudentCollegeName = ''; + form.targetBedStudentMajorName = ''; + form.targetBedStudentClassName = ''; +}; + // 初始化页面数据 const initPageData = () => { // 重置表单数据 @@ -1759,10 +1850,15 @@ const initPageData = () => { targetRoomNo: '', targetBedId: '', targetBedNo: '', + // 目标床位学生信息 + targetBedStudentId: '', + targetBedStudentName: '', + targetBedStudentNo: '', + targetBedStudentCollegeName: '', + targetBedStudentMajorName: '', + targetBedStudentClassName: '', applyTime: routeParams.value.id ? '' : new Date().toISOString().slice(0, 19).replace('T', ' '), reasonDetail: '', - emergencyContact: '', - emergencyPhone: '', attachmentOssIds: '', applicationStatus: 'draft', approvalOpinion: '', @@ -1804,9 +1900,10 @@ const submitCallback = async () => { onMounted(() => { nextTick(async () => { routeParams.value = (proxy as any).$route.query; - initPageData(); - // 加载目标校区选项 + // 先加载目标校区选项,确保级联数据回显时能找到对应的名称 await loadTargetCampusOptions(); + // 然后初始化页面数据(包含数据回显) + initPageData(); }); }); @@ -1917,11 +2014,29 @@ onMounted(() => { padding-top: 8px; } -/* 学生基础信息展示样式(在学生选择区域内) */ -.student-info-display { - margin-top: 16px; - padding-top: 12px; - border-top: 1px solid #e9ecef; +/* 选择学生下方的学生信息显示样式 */ +.selected-student-info { + margin-top: 0px; + margin-bottom: 8px; +} + +.selected-student-info .student-info-inline, +.selected-student-info .dorm-info-inline, +.target-bed-student-inline-info { + margin-bottom: 8px; + padding: 8px 12px; + background: #f8f9fa; + border-radius: 4px; + border: 1px solid #e9ecef; +} + +.selected-student-info .student-info-inline .info-text, +.selected-student-info .dorm-info-inline .info-text, +.target-bed-student-inline-info .student-info-text { + font-size: 12px; + color: #606266; + line-height: 1.4; + word-break: break-word; } .info-subtitle { @@ -1933,30 +2048,6 @@ onMounted(() => { border-bottom: 1px solid #f0f0f0; } -.student-info-display .info-item { - display: flex; - align-items: center; - margin-bottom: 8px; - padding: 4px 0; -} - -.student-info-display .info-label { - color: #606266; - font-weight: 500; - margin-right: 8px; - white-space: nowrap; - font-size: 13px; - min-width: 80px; -} - -.student-info-display .info-value { - color: #303133; - font-weight: 400; - flex: 1; - word-break: break-all; - font-size: 13px; -} - .info-title { font-size: 16px; font-weight: 600; @@ -2279,4 +2370,6 @@ onMounted(() => { min-height: 32px !important; padding: 4px 12px !important; } + +/* 目标床位学生信息内联显示样式 - 已合并到选择学生信息样式中 */ diff --git a/web/src/views/xg/xsjcxx/xssssq/index.vue b/web/src/views/xg/xsjcxx/xssssq/index.vue index 822ab7ec..21262281 100644 --- a/web/src/views/xg/xsjcxx/xssssq/index.vue +++ b/web/src/views/xg/xsjcxx/xssssq/index.vue @@ -328,7 +328,7 @@ const handleAdd = () => { const handleUpdate = async (row?: DormApplicationVO) => { const _id = row?.id || ids.value[0]; proxy.$router.push({ - path: `/xg/xsjcxx/xssssq/dormEdit/index`, + path: `/xg/xsjcxx/xssssq/dialog`, query: { id: _id, type: 'update' From 8ddd3a0d09a9f4aa8b8866244cfd57f73efd0cc1 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Tue, 9 Sep 2025 00:09:23 +0800 Subject: [PATCH 03/11] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/XgDormApplicationServiceImpl.java | 108 ++++++++++++++++-- web/src/components/Process/approvalRecord.vue | 81 ++++++++++++- 2 files changed, 174 insertions(+), 15 deletions(-) diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java index ea73d16d..3eaa50f5 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormApplicationServiceImpl.java @@ -40,9 +40,11 @@ import org.dromara.xg.mapper.XgStudentManagerMapper; import org.dromara.xg.mapper.XgStudentManagerSourceMapper; import org.dromara.xg.mapper.XgClassMsgMapper; import org.dromara.xg.mapper.XgDormBedStudentMapper; +import org.dromara.xg.service.IXgDormBedStudentService; import org.dromara.xg.domain.XgStudentManager; import org.dromara.xg.domain.XgStudentManagerSource; import org.dromara.xg.domain.XgClassMsg; +import org.dromara.xg.domain.XgDormBedStudent; import java.util.List; import java.util.Map; @@ -67,6 +69,7 @@ public class XgDormApplicationServiceImpl implements IXgDormApplicationService { private final XgDormApplicationMapper baseMapper; private final WorkflowService workflowService; private final XgDormBedStudentMapper xgDormBedStudentMapper; + private final IXgDormBedStudentService xgDormBedStudentService; // 添加权限过滤需要的依赖 private final SysDeptMapper sysDeptMapper; private final SysUserMapper sysUserMapper; @@ -427,32 +430,115 @@ public class XgDormApplicationServiceImpl implements IXgDormApplicationService { private void executeApplicationOperation(XgDormApplication application) { try { if ("1".equals(application.getApplicationType())) { - // 调宿申请 - 调用宿舍调整接口 + // 调宿申请 - 区分两种场景处理 log.info("执行调宿操作: 学生ID={}, 目标床位ID={}", application.getStudentId(), application.getTargetBedId()); - // 构建调宿参数并调用接口 - // 参考提示词中的接口:/dormBedStudent/dormBedStudent/classTeacherExchangeBeds - // TODO: 这里需要根据实际情况调用相应的宿舍管理接口 - log.info("调宿操作参数: sourceStudentId={}, targetBedId={}, classId={}", - application.getStudentId(), application.getTargetBedId(), application.getClassId()); + // 1. 根据目标床位ID查找当前占用该床位的学生ID + Long targetStudentId = findStudentByBedId(application.getTargetBedId()); + + Boolean operationResult = false; + + if (targetStudentId != null) { + // 场景1:目标床位有学生 - 两个学生互换床位 + log.info("目标床位已有学生,执行床位互换: 申请学生ID={}, 目标学生ID={}", + application.getStudentId(), targetStudentId); + + operationResult = xgDormBedStudentService.classTeacherExchangeBeds( + application.getStudentId(), // 源学生ID(申请人) + targetStudentId, // 目标学生ID(目标床位当前占用者) + application.getClassId() // 班级ID + ); + + if (operationResult) { + log.info("床位互换操作执行成功: 申请学生ID={}, 目标学生ID={}, 班级ID={}", + application.getStudentId(), targetStudentId, application.getClassId()); + } else { + log.error("床位互换操作执行失败: 申请学生ID={}, 目标学生ID={}, 班级ID={}", + application.getStudentId(), targetStudentId, application.getClassId()); + throw new RuntimeException("床位互换操作执行失败"); + } + + } else { + // 场景2:目标床位为空 - 学生移动到新床位 + log.info("目标床位为空,执行床位移动: 申请学生ID={}, 目标床位ID={}", + application.getStudentId(), application.getTargetBedId()); + + operationResult = xgDormBedStudentService.classTeacherMoveBed( + application.getStudentId(), // 源学生ID(申请人) + application.getTargetBedId(), // 目标床位ID + application.getClassId() // 班级ID + ); + + if (operationResult) { + log.info("床位移动操作执行成功: 申请学生ID={}, 目标床位ID={}, 班级ID={}", + application.getStudentId(), application.getTargetBedId(), application.getClassId()); + } else { + log.error("床位移动操作执行失败: 申请学生ID={}, 目标床位ID={}, 班级ID={}", + application.getStudentId(), application.getTargetBedId(), application.getClassId()); + throw new RuntimeException("床位移动操作执行失败"); + } + } } else if ("2".equals(application.getApplicationType())) { // 退宿申请 - 调用退宿接口 log.info("执行退宿操作: 学生ID={}, 当前床位ID={}", application.getStudentId(), application.getCurrentBedId()); // 构建退宿参数并调用接口 - // 参考提示词中的接口:/dormBedStudent/dormBedStudent/classTeacherStayawaySelectedBeds - // TODO: 这里需要根据实际情况调用相应的宿舍管理接口 List bedIds = List.of(application.getCurrentBedId()); - log.info("退宿操作参数: studentId={}, bedIds={}, classId={}", - application.getStudentId(), bedIds, application.getClassId()); + Boolean stayawayResult = xgDormBedStudentService.classTeacherStayawaySelectedBeds( + bedIds, // 床位ID列表 + application.getClassId() // 班级ID + ); + + if (stayawayResult) { + log.info("退宿操作执行成功: 学生ID={}, 床位ID={}, 班级ID={}", + application.getStudentId(), application.getCurrentBedId(), application.getClassId()); + } else { + log.error("退宿操作执行失败: 学生ID={}, 床位ID={}, 班级ID={}", + application.getStudentId(), application.getCurrentBedId(), application.getClassId()); + throw new RuntimeException("退宿操作执行失败"); + } + } else { + log.error("未知的申请类型: {}", application.getApplicationType()); + throw new RuntimeException("未知的申请类型: " + application.getApplicationType()); } log.info("宿舍申请操作执行成功,申请ID: {}", application.getId()); } catch (Exception e) { log.error("宿舍申请操作执行失败,申请ID: {}, 错误信息: {}", application.getId(), e.getMessage(), e); - // 可以考虑设置申请状态为失败或者发送通知 + // 抛出异常,让调用方处理 + throw new RuntimeException("宿舍申请操作执行失败: " + e.getMessage(), e); + } + } + + /** + * 根据床位ID查找当前占用该床位的学生ID + * + * @param bedId 床位ID + * @return 学生ID,如果没有找到则返回null + */ + private Long findStudentByBedId(Long bedId) { + if (bedId == null) { + return null; + } + try { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(XgDormBedStudent::getBedId, bedId) + .eq(XgDormBedStudent::getStatus, "0") // 0-正常状态 + .eq(XgDormBedStudent::getDeleted, 0L); // 未删除 + + XgDormBedStudent bedStudent = xgDormBedStudentMapper.selectOne(queryWrapper); + if (bedStudent != null) { + log.info("根据床位ID: {} 找到学生ID: {}", bedId, bedStudent.getStudentId()); + return bedStudent.getStudentId(); + } else { + log.warn("床位ID: {} 没有找到对应的学生", bedId); + return null; + } + } catch (Exception e) { + log.error("根据床位ID: {} 查找学生失败: {}", bedId, e.getMessage(), e); + return null; } } diff --git a/web/src/components/Process/approvalRecord.vue b/web/src/components/Process/approvalRecord.vue index b50af4a4..b13c426d 100644 --- a/web/src/components/Process/approvalRecord.vue +++ b/web/src/components/Process/approvalRecord.vue @@ -12,10 +12,19 @@ - + + + - + @@ -648,6 +682,7 @@ import { StglClubVO, StglClubQuery, StglClubForm } from '@/api/stgl/club/types'; import StudentSelector from '@/components/StudentSelector/index.vue'; import AdvisorManager from '@/components/AdvisorManager/index.vue'; import MemberManagementDialog from './components/MemberManagementDialog.vue'; +import AdvisorHistoryDialog from '@/components/AdvisorHistoryDialog/index.vue'; import { StudentVO } from '@/api/xg/student/types'; import { AdvisorVO } from '@/components/AdvisorManager/index.vue'; import { Document } from '@element-plus/icons-vue'; @@ -711,6 +746,14 @@ const memberDialog = reactive({ const currentClub = ref(null); const memberActiveTab = ref('joined'); +// 指导老师历史记录相关 +const advisorHistoryDialog = reactive({ + visible: false, + title: '' +}); + +const currentAdvisorClub = ref(null); + interface TransferForm { clubId?: string | number; oldPresidentName?: string; @@ -850,12 +893,7 @@ const getList = async () => { const res = await listStglClub(queryParams.value); - // 🔥 调试信息:检查返回的数据结构 - console.log('getList: 获取到的社团列表数据:', res); - if (res.rows && res.rows.length > 0) { - console.log('getList: 第一条社团数据详情:', res.rows[0]); - console.log('getList: 第一条社团的advisorsJson字段:', res.rows[0].advisorsJson); - } + // 处理列表数据:将状态字段从数字类型转换为字符串类型,确保字典组件正常显示 if (res.rows && Array.isArray(res.rows)) { @@ -927,10 +965,8 @@ const handleAdd = () => { const handleUpdate = async (row?: StglClubVO) => { reset(); const _id = row?.id || ids.value[0]; - console.log('handleUpdate: 开始获取社团数据,ID:', _id); const res = await getStglClub(_id); - console.log('handleUpdate: API响应数据:', res.data); // 处理经费来源数据:如果是字符串则分割为数组 if (res.data.fundingSources) { @@ -964,37 +1000,30 @@ const handleUpdate = async (row?: StglClubVO) => { } // 处理副社长信息:优先从JSON字段读取,如果没有则使用单一字段 - console.log('handleUpdate: 副社长原始数据:', { - vicePresidentsJson: res.data.vicePresidentsJson, - vicePresidentName: res.data.vicePresidentName, - vicePresidentStudentNo: res.data.vicePresidentStudentNo, - vicePresidentDept: res.data.vicePresidentDept, - vicePresidentMajor: res.data.vicePresidentMajor, - vicePresidentClass: res.data.vicePresidentClass, - vicePresidentPhone: res.data.vicePresidentPhone - }); // 处理指导老师信息:优先从JSON字段读取,如果没有则使用单一字段 - // 🔥 优先从JSON字段读取指导老师数据 + let advisorsData = []; if (res.data.advisorsJson) { try { - (res.data as any).advisors = JSON.parse(res.data.advisorsJson); - console.log('handleUpdate: 从JSON字段读取的指导老师数据:', (res.data as any).advisors); + advisorsData = JSON.parse(res.data.advisorsJson); } catch (error) { console.warn('解析指导老师JSON数据失败:', error); - (res.data as any).advisors = []; + advisorsData = []; } } else if (res.data.advisorName) { - // 回退到单一字段 - (res.data as any).advisors = [ + // 回退到单一字段,构建完整的teacher对象 + advisorsData = [ { name: res.data.advisorName, dept: res.data.advisorDept, phone: res.data.advisorPhone || '', email: res.data.advisorEmail || '', + userId: '', teacher: { + userId: null, + userName: '', nickName: res.data.advisorName, deptName: res.data.advisorDept, phonenumber: res.data.advisorPhone || '', @@ -1002,13 +1031,15 @@ const handleUpdate = async (row?: StglClubVO) => { } } ]; - } else { - (res.data as any).advisors = []; } + // 先设置其他数据 Object.assign(form.value, res.data); - console.log('handleUpdate: 表单数据设置后:', form.value); - console.log('handleUpdate: 表单中的副社长数据:', form.value.vicePresidents); + + // 然后单独设置指导老师数据,避免触发 watch 时的冲突 + nextTick(() => { + form.value.advisors = advisorsData; + }); dialog.visible = true; dialog.title = '修改社团信息'; @@ -1042,15 +1073,9 @@ const submitForm = () => { - // 🔥 新增:同时保存JSON字段和兼容性字段 + // 同时保存JSON字段和兼容性字段 submitData.advisorsJson = JSON.stringify(submitData.advisors || []); - // 调试日志:检查指导老师数据 - console.log('提交数据 - advisors:', submitData.advisors); - console.log('提交数据 - advisorName:', submitData.advisorName); - console.log('提交数据 - advisorDept:', submitData.advisorDept); - console.log('完整提交数据:', submitData); - if (form.value.id) { await updateStglClub(submitData).finally(() => (buttonLoading.value = false)); } else { @@ -1111,16 +1136,10 @@ const handleView = async (row: StglClubVO) => { - // 🔥 处理指导老师信息:优先从JSON字段读取,如果没有则使用单一字段 - console.log('handleView: 指导老师原始数据:', { - advisorsJson: viewData.advisorsJson, - advisorName: viewData.advisorName - }); - + // 处理指导老师信息:优先从JSON字段读取,如果没有则使用单一字段 if (viewData.advisorsJson) { try { (viewData as any).advisors = JSON.parse(viewData.advisorsJson); - console.log('handleView: 从JSON字段读取的指导老师数据:', (viewData as any).advisors); } catch (error) { console.warn('解析指导老师JSON数据失败:', error); (viewData as any).advisors = []; @@ -1145,10 +1164,6 @@ const handleView = async (row: StglClubVO) => { (viewData as any).advisors = []; } - console.log('handleView: 处理后的数据:', { - advisors: (viewData as any).advisors - }); - Object.assign(viewForm, viewData); viewDialog.visible = true; viewDialog.title = '查看社团信息'; @@ -1212,16 +1227,12 @@ const getSourceTagType = (value: string) => { /** 获取指导老师姓名列表 */ const getAdvisorNames = (row: any): string[] => { - console.log('getAdvisorNames: 处理行数据:', row); - // 优先从JSON字段读取指导老师数据 if (row.advisorsJson) { try { const advisors = JSON.parse(row.advisorsJson); - console.log('getAdvisorNames: 从JSON字段解析的指导老师数据:', advisors); if (Array.isArray(advisors)) { const names = advisors.map((advisor: any) => advisor.name || advisor.teacher?.nickName || '').filter((name) => name); - console.log('getAdvisorNames: 从JSON提取的姓名列表:', names); return names; } } catch (error) { @@ -1231,11 +1242,9 @@ const getAdvisorNames = (row: any): string[] => { // 回退到单一字段 if (row.advisorName) { - console.log('getAdvisorNames: 从单一字段获取指导老师:', row.advisorName); return [row.advisorName]; } - console.log('getAdvisorNames: 没有找到指导老师数据'); return []; }; @@ -1265,107 +1274,91 @@ const handlePresidentChange = (student: StudentVO | null) => { -// 防止递归更新的锁 -const syncLock = ref(false); +// 防止递归更新的标志 +const syncingAdvisors = ref(false); -/** 指导老师列表变化处理 */ -const handleAdvisorsChange = (advisors: AdvisorVO[]) => { - // 添加调试日志 - console.log('handleAdvisorsChange 被调用,advisors:', advisors); - - // 添加防护锁,避免递归更新 - if (syncLock.value) return; - - syncLock.value = true; - nextTick(() => { - // 同步指导老师数据到表单 - form.value.advisors = advisors; - - // 为了兼容旧版本,同步第一个指导老师的信息到旧字段 - if (advisors.length > 0) { - const firstAdvisor = advisors[0]; - form.value.advisorName = firstAdvisor.name || firstAdvisor.teacher?.nickName; - form.value.advisorDept = firstAdvisor.dept || firstAdvisor.teacher?.deptName; - form.value.advisorPhone = firstAdvisor.phone || firstAdvisor.teacher?.phonenumber; - form.value.advisorEmail = firstAdvisor.email || firstAdvisor.teacher?.email; - } else { - form.value.advisorName = undefined; - form.value.advisorDept = undefined; - form.value.advisorPhone = undefined; - form.value.advisorEmail = undefined; - } - syncLock.value = false; - }); -}; +/** 监听指导老师数据变化,同步兼容性字段 */ +watch( + () => form.value.advisors, + (advisors: AdvisorVO[]) => { + // 防止递归更新 + if (syncingAdvisors.value) return; + + syncingAdvisors.value = true; + nextTick(() => { + try { + // 为了兼容旧版本,同步第一个指导老师的信息到旧字段 + if (advisors && advisors.length > 0) { + const firstAdvisor = advisors[0]; + form.value.advisorName = firstAdvisor.name || firstAdvisor.teacher?.nickName; + form.value.advisorDept = firstAdvisor.dept || firstAdvisor.teacher?.deptName; + form.value.advisorPhone = firstAdvisor.phone || firstAdvisor.teacher?.phonenumber; + form.value.advisorEmail = firstAdvisor.email || firstAdvisor.teacher?.email; + } else { + form.value.advisorName = undefined; + form.value.advisorDept = undefined; + form.value.advisorPhone = undefined; + form.value.advisorEmail = undefined; + } + } finally { + syncingAdvisors.value = false; + } + }); + }, + { deep: true } +); /** 检查用户是否为指定社团的社长(无论是否为创建者) */ const checkIsPresident = async (clubId: string | number): Promise => { try { - console.log('🔍 checkIsPresident: 开始检查社长权限, clubId:', clubId); const res = await checkPresidentRole(clubId); - console.log('🔍 checkIsPresident: API返回结果:', res); const isPresident = res.data === true; - console.log('🔍 checkIsPresident: 最终结果:', isPresident); return isPresident; } catch (error) { - console.error('❌ checkIsPresident: 检查社长权限失败:', error); + console.error('checkIsPresident: 检查社长权限失败:', error); return false; } }; /** 批量检查社长权限(检查用户是否为各社团的社长,无论是否为创建者) */ const checkBatchPresidentRoles = async (clubs: StglClubVO[]) => { - console.log('🔍 checkBatchPresidentRoles: 开始批量检查权限, clubs数量:', clubs.length); const checks = clubs.map(async (club) => { const isPresident = await checkIsPresident(club.id!); presidentRoles.value[club.id!] = isPresident; - console.log(`🔍 checkBatchPresidentRoles: 社团${club.id}(${club.clubName}) 权限检查结果:`, isPresident); return { clubId: club.id!, isPresident }; }); await Promise.all(checks); - console.log('🔍 checkBatchPresidentRoles: 批量权限检查完成, presidentRoles:', presidentRoles.value); }; /** 判断是否显示转让按钮 */ const shouldShowTransferButton = (club: StglClubVO): boolean => { - console.log('🔍 shouldShowTransferButton: 开始判断转让按钮显示, club:', club); - // 1. 检查基础权限 const hasEditPermission = proxy?.$auth.hasPermi('stgl:club:edit'); - console.log('🔍 shouldShowTransferButton: 基础edit权限检查:', hasEditPermission); if (!hasEditPermission) { - console.log('❌ shouldShowTransferButton: 无基础edit权限,返回false'); return false; } // 2. 检查角色权限 - 如果有stgl_president角色,则需要进一步检查是否为该社团社长 const hasPresidentRole = proxy?.$auth.hasRole('stgl_president'); - console.log('🔍 shouldShowTransferButton: stgl_president角色检查:', hasPresidentRole); if (hasPresidentRole) { // 如果权限检查还未完成,返回false(避免按钮闪现) const isPresident = presidentRoles.value[club.id!]; - console.log('🔍 shouldShowTransferButton: 社团社长权限检查结果:', isPresident, 'presidentRoles:', presidentRoles.value); if (isPresident === undefined) { - console.log('⏳ shouldShowTransferButton: 权限检查未完成,返回false避免闪现'); return false; } // 社长可以看到转让按钮(无论是否为创建者) const result = isPresident === true; - console.log('🔍 shouldShowTransferButton: stgl_president角色最终结果:', result); return result; } // 3. 如果是管理员角色,显示转让按钮(但转让操作仍需社长权限验证) const hasAdminRole = proxy?.$auth.hasRoleOr(['admin', 'common']); - console.log('🔍 shouldShowTransferButton: 管理员角色检查:', hasAdminRole); if (hasAdminRole) { - console.log('✅ shouldShowTransferButton: 管理员角色,返回true'); return true; } - console.log('❌ shouldShowTransferButton: 无任何有效角色,返回false'); return false; }; @@ -1473,7 +1466,6 @@ const handleActivityManagement = (row: StglClubVO) => { /** 成员管理按钮操作 */ const handleMemberManagement = (row: StglClubVO) => { - console.log('handleMemberManagement: 打开成员管理弹窗, row:', row); currentClub.value = row; memberDialog.visible = true; memberDialog.title = `${row.clubName} - 成员管理`; @@ -1482,7 +1474,6 @@ const handleMemberManagement = (row: StglClubVO) => { /** 处理成员数量变化 */ const handleMemberCountChanged = (newCount: number) => { - console.log('handleMemberCountChanged: 成员数量变化:', newCount, '当前社团:', currentClub.value); if (currentClub.value) { // 更新当前社团的成员数量 currentClub.value.memberCount = newCount; @@ -1491,11 +1482,40 @@ const handleMemberCountChanged = (newCount: number) => { const clubIndex = clubList.value.findIndex(club => club.id === currentClub.value?.id); if (clubIndex !== -1) { clubList.value[clubIndex].memberCount = newCount; - console.log('handleMemberCountChanged: 已更新列表中的成员数量:', clubList.value[clubIndex]); } } }; +/** 指导老师历史记录按钮操作 */ +const handleAdvisorHistory = (row: StglClubVO) => { + currentAdvisorClub.value = row; + advisorHistoryDialog.visible = true; + advisorHistoryDialog.title = `${row.clubName} - 指导老师历史记录`; +}; + +/** 工具栏指导老师按钮操作 */ +const handleAdvisorHistoryFromToolbar = () => { + const selectedClub = clubList.value.find((c) => ids.value.includes(c.id!)); + if (selectedClub) { + handleAdvisorHistory(selectedClub); + } +}; + +/** 年审管理按钮操作 */ +const handleAnnualReview = () => { + const selectedClub = clubList.value.find((c) => ids.value.includes(c.id!)); + if (selectedClub) { + router.push({ + path: '/stgl/club/info/annualReview/index', + query: { + clubId: selectedClub.id, + clubName: selectedClub.clubName, + from: 'club' + } + }); + } +}; + onMounted(() => { getList(); }); diff --git a/web/src/views/stgl/my/activity/index.vue b/web/src/views/stgl/my/activity/index.vue index 1c6269aa..29f7d92e 100644 --- a/web/src/views/stgl/my/activity/index.vue +++ b/web/src/views/stgl/my/activity/index.vue @@ -77,8 +77,26 @@ - - + + + + + + - + - + - + + + + + + +
@@ -452,4 +470,33 @@ onMounted(() => { .activity-item:last-child { margin-bottom: 0; } + +/* 今日活动和活动时间样式 */ +.activity-name { + color: #409eff; + font-weight: 500; + line-height: 1.4; + word-break: break-word; +} + +.activity-time { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + line-height: 1.3; +} + +.time-start, +.time-end { + color: #606266; + font-size: 13px; + font-weight: 500; +} + +.time-separator { + color: #909399; + font-size: 12px; + margin: 0 4px; +} \ No newline at end of file From 7dbfde3e5043ab635491f61d50a6ea91a1f7d71b Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Tue, 9 Sep 2025 13:11:40 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/api/xskh/assessmentIndicator/types.ts | 12 +- .../AssessmentApplicationForm.vue | 6 +- .../AssessmentIndicatorForm.vue | 153 +++++++++++------- .../views/xskh/assessmentIndicator/index.vue | 2 +- .../assessmentPlan/AssessmentPlanForm.vue | 15 +- .../views/xskh/assessmentSchedule/index.vue | 2 + 6 files changed, 116 insertions(+), 74 deletions(-) diff --git a/web/src/api/xskh/assessmentIndicator/types.ts b/web/src/api/xskh/assessmentIndicator/types.ts index b2c0fb9d..8c04e56f 100644 --- a/web/src/api/xskh/assessmentIndicator/types.ts +++ b/web/src/api/xskh/assessmentIndicator/types.ts @@ -80,6 +80,10 @@ export interface XskhAssessmentIndicatorVo extends BaseEntity { disciplineTypeIds?: string[]; /** 关联的奖励类型ID列表 */ rewardTypeIds?: string[]; + /** 关联的违纪处分类型ID(单个值,用于前端表单) */ + disciplineTypeId?: string | number; + /** 关联的奖励类型ID(单个值,用于前端表单) */ + rewardTypeId?: string | number; /** 创建者 */ createBy?: string | number; /** 创建时间 */ @@ -110,10 +114,10 @@ export interface XskhAssessmentIndicatorForm extends BaseEntity { status?: string; /** 排序号 */ sortOrder?: number; - /** 关联的违纪处分类型ID列表 */ - disciplineTypeIds?: string[]; - /** 关联的奖励类型ID列表 */ - rewardTypeIds?: string[]; + /** 关联的违纪处分类型ID */ + disciplineTypeId?: string | number; + /** 关联的奖励类型ID */ + rewardTypeId?: string | number; } /** diff --git a/web/src/views/xskh/assessmentApplication/AssessmentApplicationForm.vue b/web/src/views/xskh/assessmentApplication/AssessmentApplicationForm.vue index 4e0d7ab7..b4768de0 100644 --- a/web/src/views/xskh/assessmentApplication/AssessmentApplicationForm.vue +++ b/web/src/views/xskh/assessmentApplication/AssessmentApplicationForm.vue @@ -85,14 +85,14 @@
- + - + - + - +