From 845c7fd9423a9c6742cc5f0f8507b2f78e432ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cwuzhen=E2=80=9D?= Date: Mon, 26 Oct 2026 09:36:38 +0800 Subject: [PATCH] =?UTF-8?q?fix(dorm):=20=E5=81=87=E6=9C=9F=E7=95=99?= =?UTF-8?q?=E5=AE=BF=E6=8C=89=E6=97=B6=E5=88=BB=E5=88=A4=E5=AE=9A=EF=BC=8C?= =?UTF-8?q?=E7=82=B9=E5=90=8D=E5=8F=AF=E5=90=8C=E6=97=B6=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E8=AF=B7=E5=81=87=E4=B8=8E=E7=95=99=E5=AE=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 结束时刻过后不再整天标留宿;请假与假期留宿可同时展示,标签仅显示状态名。 Co-authored-by: Cursor --- .../xg/service/IXgHolidayStayService.java | 2 +- .../impl/XgDormBedStudentServiceImpl.java | 4 +- .../impl/XgDormRollCallServiceImpl.java | 14 ++- .../impl/XgHolidayStayServiceImpl.java | 17 +-- docs/开发文档/API/宿舍点名假期留宿标识说明.md | 14 ++- .../ssdm/components/RoomRollCallPopup.vue | 102 ++++++++++++------ .../dormRollCall/RollCallDetailDrawer.vue | 26 +++-- .../dormRollCall/RoomRollCallPopup.vue | 87 +++++++++------ 8 files changed, 169 insertions(+), 97 deletions(-) diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgHolidayStayService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgHolidayStayService.java index 3180b40c..7d1deca0 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgHolidayStayService.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgHolidayStayService.java @@ -119,7 +119,7 @@ public interface IXgHolidayStayService { Boolean revoke(XgHolidayStayAuditBo auditBo); /** - * 按检查时刻查询学生当前有效的假期留宿(已通过,且日期落在留宿起止日内,结束日当天整天有效) + * 按检查时刻查询学生当前有效的假期留宿(已通过,且检查时刻落在留宿起止 datetime 内,含边界) * * @param studentIds 学生ID列表 * @param checkDate 检查时刻 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 d4ad946a..0f44f9b8 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 @@ -2579,9 +2579,9 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { leaveInfoMap.put("leaveEndTime", leaveInfo.getEndTime()); studentInfo.put("leaveInfo", leaveInfoMap); } - // 请假优先;无请假时再挂假期留宿 + // 请假与假期留宿可同时生效,一并返回供点名双状态展示 XgHolidayStayVo holidayStay = holidayStayMap.get(studentIdValue); - if (leaveInfo == null && holidayStay != null) { + if (holidayStay != null) { Map holidayStayInfoMap = new LinkedHashMap<>(); holidayStayInfoMap.put("holidayStayId", holidayStay.getId()); holidayStayInfoMap.put("holidayType", holidayStay.getHolidayType()); diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRollCallServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRollCallServiceImpl.java index f15e2545..fa337b8f 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRollCallServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRollCallServiceImpl.java @@ -343,15 +343,21 @@ public class XgDormRollCallServiceImpl implements IXgDormRollCallService { for (XgDormRollCallDetail detail : allStudentDetails) { String status = detail.getRollCallStatus(); - if (!"1".equals(status) && !"2".equals(status) && !"4".equals(status)) { + boolean isLeave = "1".equals(status) || detail.getLeaveId() != null; + boolean isHolidayStay = "4".equals(status) || detail.getHolidayStayId() != null; + boolean isAbsent = "2".equals(status); + if (!isLeave && !isHolidayStay && !isAbsent) { continue; } DormRollCallTodayDetailItemVo item = toTodayDetailItem(detail, rollCallMap.get(detail.getRollCallId())); - if ("1".equals(status)) { + // 请假与假期留宿可同时存在,分别计入两类明细 + if (isLeave) { leaveDetails.add(item); - } else if ("4".equals(status)) { + } + if (isHolidayStay) { holidayStayDetails.add(item); - } else { + } + if (isAbsent && !isLeave && !isHolidayStay) { absentDetails.add(item); } } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgHolidayStayServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgHolidayStayServiceImpl.java index 4721c4c9..82f73095 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgHolidayStayServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgHolidayStayServiceImpl.java @@ -39,10 +39,7 @@ import java.util.HashMap; import java.util.Calendar; import java.text.SimpleDateFormat; import java.util.Date; -import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; import org.springframework.beans.BeanUtils; /** @@ -1006,7 +1003,8 @@ public class XgHolidayStayServiceImpl implements IXgHolidayStayService { /** * 按检查时刻查询学生当前有效的假期留宿。 - * 条件:已通过;检查日落在 [applyStartDate, applyEndDate](按日期比较,结束日当天整天有效)。 + * 条件:已通过;检查时刻落在 [applyStartDate, applyEndDate](按 datetime 比较,与请假判定一致)。 + * 例:结束为 10-25 08:00,则 25 日 21 点点名不再显示假期留宿。 */ @Override public Map queryStudentHolidayStaysByCheckDate(List studentIds, LocalDateTime checkDate) { @@ -1014,18 +1012,13 @@ public class XgHolidayStayServiceImpl implements IXgHolidayStayService { return Map.of(); } - ZoneId zone = ZoneId.systemDefault(); - LocalDate checkDay = checkDate.toLocalDate(); - Date dayStart = Date.from(checkDay.atStartOfDay(zone).toInstant()); - Date dayEnd = Date.from(LocalDateTime.of(checkDay, LocalTime.MAX).atZone(zone).toInstant()); - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.in(XgHolidayStay::getStudentId, studentIds); // 仅已通过 lqw.eq(XgHolidayStay::getApplyStatus, "2"); - // 开始日 <= 检查日末,结束日 >= 检查日初(结束日当天整天有效) - lqw.le(XgHolidayStay::getApplyStartDate, dayEnd); - lqw.ge(XgHolidayStay::getApplyEndDate, dayStart); + // 与请假一致:按时刻落在起止区间内(含边界) + lqw.le(XgHolidayStay::getApplyStartDate, checkDate); + lqw.ge(XgHolidayStay::getApplyEndDate, checkDate); lqw.isNotNull(XgHolidayStay::getApplyStartDate); lqw.isNotNull(XgHolidayStay::getApplyEndDate); lqw.orderByDesc(XgHolidayStay::getCreateTime); diff --git a/docs/开发文档/API/宿舍点名假期留宿标识说明.md b/docs/开发文档/API/宿舍点名假期留宿标识说明.md index 3be7df30..1ef82026 100644 --- a/docs/开发文档/API/宿舍点名假期留宿标识说明.md +++ b/docs/开发文档/API/宿舍点名假期留宿标识说明.md @@ -7,16 +7,24 @@ ## 有效留宿判定(后端统一) -接口:`getRoomStudents` 在返回学生信息时附加 `holidayStayInfo`(有有效请假时不返回,请假优先)。 +接口:`getRoomStudents` 在返回学生信息时附加 `leaveInfo` / `holidayStayInfo`(**可同时返回**)。 -条件: +条件(假期留宿): 1. `apply_status = '2'`(已通过) -2. 检查日落在 `[apply_start_date, apply_end_date]`(按日期比较,**结束日当天整天有效**) +2. 检查**时刻**落在 `[apply_start_date, apply_end_date]`(按 datetime 比较,与请假一致,含边界) 3. 同一学生多条记录时取最新一条 +例:留宿结束为 `2026-10-25 08:00:00`,则 25 日 21 点宿舍点名**不再**显示「假期留宿」,恢复默认「在寝」。 + +若同一学生当前既有有效请假又有有效假期留宿:点名页**同时展示两个标签**;落库主状态仍为请假(`1`),并同时写入 `leave_*` 与 `holiday_*` 字段;今日汇总两类明细均可计入该生。 + 假期结束后,接口不再返回 `holidayStayInfo`,前端恢复默认「在寝」可切换。 +> **变更说明(2026-10-26)**: +> 1. 原先按「结束日当天整天有效」判定 → 已改为按时刻判定。 +> 2. 原先请假优先时不返回假期留宿 → 已改为两者可同时展示。 + ## 点名状态字典 `xg_dorm_roll_call_status` | 值 | 含义 | diff --git a/mobile/src/pages/xgss/ssdm/components/RoomRollCallPopup.vue b/mobile/src/pages/xgss/ssdm/components/RoomRollCallPopup.vue index 4b209d28..880ee6d1 100644 --- a/mobile/src/pages/xgss/ssdm/components/RoomRollCallPopup.vue +++ b/mobile/src/pages/xgss/ssdm/components/RoomRollCallPopup.vue @@ -18,14 +18,23 @@ {{ bed.studentNo }} 空床位 - - + + + + - - - - - {{ getStatusLabel(bed.rollCallStatus) }} + + {{ getStatusLabel(bed) }} @@ -112,11 +121,21 @@ export default { let present = 0, leave = 0, holidayStay = 0, absent = 0, empty = 0; this.bedDetails.forEach((bed) => { const status = bed.rollCallStatus; - if (status === '0') present++; - else if (status === '1') leave++; - else if (status === '4') holidayStay++; - else if (status === '2') absent++; - else if (status === '3') empty++; + if (status === '3') { + empty++; + return; + } + if (status === '2') { + absent++; + return; + } + if (status === '0') { + present++; + return; + } + // 请假/留宿可同时计入(同一人可同时出现在两类统计) + if (bed.isLeaveAuto || status === '1') leave++; + if (bed.isHolidayStayAuto || status === '4') holidayStay++; }); return { present, leave, holidayStay, absent, empty }; }, @@ -215,21 +234,27 @@ export default { if (!bed.studentId) { bed.rollCallStatus = '3'; - } else if (item.leaveInfo) { - // 请假优先 - bed.rollCallStatus = '1'; - bed.isLeaveAuto = true; - bed.leaveId = item.leaveInfo.leaveId; - bed.leaveTypeName = item.leaveInfo.leaveTypeName; - bed.leaveStartTime = item.leaveInfo.leaveStartTime; - bed.leaveEndTime = item.leaveInfo.leaveEndTime; - } else if (item.holidayStayInfo) { - bed.rollCallStatus = '4'; - bed.isHolidayStayAuto = true; - bed.holidayStayId = item.holidayStayInfo.holidayStayId; - bed.holidayType = item.holidayStayInfo.holidayType; - bed.holidayStartDate = item.holidayStayInfo.applyStartDate; - bed.holidayEndDate = item.holidayStayInfo.applyEndDate; + } else { + if (item.leaveInfo) { + bed.isLeaveAuto = true; + bed.leaveId = item.leaveInfo.leaveId; + bed.leaveTypeName = item.leaveInfo.leaveTypeName; + bed.leaveStartTime = item.leaveInfo.leaveStartTime; + bed.leaveEndTime = item.leaveInfo.leaveEndTime; + } + if (item.holidayStayInfo) { + bed.isHolidayStayAuto = true; + bed.holidayStayId = item.holidayStayInfo.holidayStayId; + bed.holidayType = item.holidayStayInfo.holidayType; + bed.holidayStartDate = item.holidayStayInfo.applyStartDate; + bed.holidayEndDate = item.holidayStayInfo.applyEndDate; + } + // 主状态:请假优先落库;仅留宿时为 4;两者都有时仍为 1,但双标签都展示 + if (bed.isLeaveAuto) { + bed.rollCallStatus = '1'; + } else if (bed.isHolidayStayAuto) { + bed.rollCallStatus = '4'; + } } return bed; @@ -250,6 +275,7 @@ export default { }, getBedCardClass(bed) { + if (bed.isLeaveAuto && bed.isHolidayStayAuto) return 'bed-leave bed-holiday-stay-both'; if (bed.rollCallStatus === '0') return 'bed-present'; if (bed.rollCallStatus === '1') return 'bed-leave'; if (bed.rollCallStatus === '4') return 'bed-holiday-stay'; @@ -258,7 +284,9 @@ export default { return ''; }, - getStatusLabel(status) { + getStatusLabel(bed) { + if (bed && bed.isLeaveAuto && bed.isHolidayStayAuto) return '请假+假期留宿'; + const status = typeof bed === 'string' ? bed : bed && bed.rollCallStatus; if (status === '0') return '在寝'; if (status === '1') return '请假'; if (status === '4') return '假期留宿'; @@ -267,7 +295,9 @@ export default { return ''; }, - getStatusColorClass(status) { + getStatusColorClass(bed) { + if (bed && bed.isLeaveAuto && bed.isHolidayStayAuto) return 'color-leave'; + const status = typeof bed === 'string' ? bed : bed && bed.rollCallStatus; if (status === '0') return 'color-present'; if (status === '1') return 'color-leave'; if (status === '4') return 'color-holiday-stay'; @@ -277,12 +307,14 @@ export default { }, handleBedClick(bed) { - if (bed.isLeaveAuto) { - uni.showToast({ title: '该学生处于请假状态,不可修改', icon: 'none' }); - return; - } - if (bed.isHolidayStayAuto) { - uni.showToast({ title: '该学生处于假期留宿状态,不可修改', icon: 'none' }); + if (bed.isLeaveAuto || bed.isHolidayStayAuto) { + const tip = + bed.isLeaveAuto && bed.isHolidayStayAuto + ? '该学生同时处于请假与假期留宿,不可修改' + : bed.isLeaveAuto + ? '该学生处于请假状态,不可修改' + : '该学生处于假期留宿状态,不可修改'; + uni.showToast({ title: tip, icon: 'none' }); return; } if (!bed.studentId) { diff --git a/web/src/views/xg/xgss/dormRollCall/dormRollCall/RollCallDetailDrawer.vue b/web/src/views/xg/xgss/dormRollCall/dormRollCall/RollCallDetailDrawer.vue index 2ee75acf..dd728664 100644 --- a/web/src/views/xg/xgss/dormRollCall/dormRollCall/RollCallDetailDrawer.vue +++ b/web/src/views/xg/xgss/dormRollCall/dormRollCall/RollCallDetailDrawer.vue @@ -22,19 +22,31 @@ - + - + diff --git a/web/src/views/xg/xgss/dormRollCall/dormRollCall/RoomRollCallPopup.vue b/web/src/views/xg/xgss/dormRollCall/dormRollCall/RoomRollCallPopup.vue index 03ed1cfa..68a06922 100644 --- a/web/src/views/xg/xgss/dormRollCall/dormRollCall/RoomRollCallPopup.vue +++ b/web/src/views/xg/xgss/dormRollCall/dormRollCall/RoomRollCallPopup.vue @@ -15,14 +15,13 @@
{{ bed.studentNo }}
空床位
-
- {{ bed.leaveTypeName || '请假' }} +
+ + 请假 + 假期留宿
-
- 假期留宿 -
-
- {{ getStatusLabel(bed.rollCallStatus) }} +
+ {{ getStatusLabel(bed) }}
@@ -88,16 +87,26 @@ const summary = computed(() => { let present = 0, leave = 0, holidayStay = 0, absent = 0, empty = 0; bedDetails.value.forEach(bed => { const status = bed.rollCallStatus; - if (status === '0') present++; - else if (status === '1') leave++; - else if (status === '4') holidayStay++; - else if (status === '2') absent++; - else if (status === '3') empty++; + if (status === '3') { + empty++; + return; + } + if (status === '2') { + absent++; + return; + } + if (status === '0') { + present++; + return; + } + if (bed.isLeaveAuto || status === '1') leave++; + if (bed.isHolidayStayAuto || status === '4') holidayStay++; }); return { present, leave, holidayStay, absent, empty }; }); const getBedCardClass = (bed: BedItem) => { + if (bed.isLeaveAuto && bed.isHolidayStayAuto) return 'bed-leave'; const status = bed.rollCallStatus; if (status === '0') return 'bed-present'; if (status === '1') return 'bed-leave'; @@ -107,7 +116,11 @@ const getBedCardClass = (bed: BedItem) => { return ''; }; -const getStatusLabel = (status?: string) => { +const getStatusLabel = (bed: BedItem | string) => { + if (typeof bed !== 'string' && bed.isLeaveAuto && bed.isHolidayStayAuto) { + return '请假+假期留宿'; + } + const status = typeof bed === 'string' ? bed : bed.rollCallStatus; if (status === '0') return '在寝'; if (status === '1') return '请假'; if (status === '4') return '假期留宿'; @@ -117,12 +130,14 @@ const getStatusLabel = (status?: string) => { }; const handleBedClick = (bed: BedItem) => { - if (bed.isLeaveAuto) { - ElMessage.info('该学生处于请假状态,不可修改'); - return; - } - if (bed.isHolidayStayAuto) { - ElMessage.info('该学生处于假期留宿状态,不可修改'); + if (bed.isLeaveAuto || bed.isHolidayStayAuto) { + ElMessage.info( + bed.isLeaveAuto && bed.isHolidayStayAuto + ? '该学生同时处于请假与假期留宿,不可修改' + : bed.isLeaveAuto + ? '该学生处于请假状态,不可修改' + : '该学生处于假期留宿状态,不可修改' + ); return; } if (!bed.studentId) { @@ -206,20 +221,26 @@ const loadBedDetails = async () => { if (!bed.studentId) { bed.rollCallStatus = '3'; - } else if (item.leaveInfo) { - bed.rollCallStatus = '1'; - bed.isLeaveAuto = true; - bed.leaveId = item.leaveInfo.leaveId; - bed.leaveTypeName = item.leaveInfo.leaveTypeName; - bed.leaveStartTime = item.leaveInfo.leaveStartTime; - bed.leaveEndTime = item.leaveInfo.leaveEndTime; - } else if (item.holidayStayInfo) { - bed.rollCallStatus = '4'; - bed.isHolidayStayAuto = true; - bed.holidayStayId = item.holidayStayInfo.holidayStayId; - bed.holidayType = item.holidayStayInfo.holidayType; - bed.holidayStartDate = item.holidayStayInfo.applyStartDate; - bed.holidayEndDate = item.holidayStayInfo.applyEndDate; + } else { + if (item.leaveInfo) { + bed.isLeaveAuto = true; + bed.leaveId = item.leaveInfo.leaveId; + bed.leaveTypeName = item.leaveInfo.leaveTypeName; + bed.leaveStartTime = item.leaveInfo.leaveStartTime; + bed.leaveEndTime = item.leaveInfo.leaveEndTime; + } + if (item.holidayStayInfo) { + bed.isHolidayStayAuto = true; + bed.holidayStayId = item.holidayStayInfo.holidayStayId; + bed.holidayType = item.holidayStayInfo.holidayType; + bed.holidayStartDate = item.holidayStayInfo.applyStartDate; + bed.holidayEndDate = item.holidayStayInfo.applyEndDate; + } + if (bed.isLeaveAuto) { + bed.rollCallStatus = '1'; + } else if (bed.isHolidayStayAuto) { + bed.rollCallStatus = '4'; + } } bed.originalStatus = bed.rollCallStatus;