package com.java110.user.cmd.property; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.java110.core.annotation.Java110Cmd; import com.java110.core.context.ICmdDataFlowContext; import com.java110.core.event.cmd.Cmd; import com.java110.core.event.cmd.CmdEvent; import com.java110.user.dao.property.IAttendanceReminderV1ServiceDao; import com.java110.utils.exception.CmdException; import com.java110.utils.util.Assert; import com.java110.vo.ResultVo; import org.springframework.beans.factory.annotation.Autowired; import java.text.SimpleDateFormat; import java.util.*; @Java110Cmd(serviceCode = "property.queryAttendanceReminder") public class QueryAttendanceReminderCmd extends Cmd { @Autowired private IAttendanceReminderV1ServiceDao attendanceReminderV1ServiceDao; @Override public void validate(CmdEvent event, ICmdDataFlowContext context, JSONObject reqJson) { Assert.hasKeyAndValue(reqJson, "userId", "未包含用户ID"); } @Override public void doCmd(CmdEvent event, ICmdDataFlowContext context, JSONObject reqJson) { String userId = reqJson.getString("userId"); String today = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); Calendar cal = Calendar.getInstance(); int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH); int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); // Calendar.SUNDAY=1, MONDAY=2, ..., but schedule_classes_day uses week_flag differently // week_flag: 1=周一, 2=周二, ..., 7=周日 int weekFlag = dayOfWeek == Calendar.SUNDAY ? 7 : dayOfWeek - 1; JSONArray overdueShifts = new JSONArray(); boolean hasReminder = false; // 1. Query staff's schedule assignments Map staffParams = new HashMap<>(); staffParams.put("staffId", userId); List staffSchedules = attendanceReminderV1ServiceDao.queryStaffSchedules(staffParams); if (staffSchedules == null || staffSchedules.isEmpty()) { // No schedule assigned → default to "always working", check if clocked in today checkDefaultReminder(userId, today, overdueShifts); if (!overdueShifts.isEmpty()) hasReminder = true; JSONObject result = new JSONObject(); result.put("hasReminder", hasReminder); result.put("overdueShifts", overdueShifts); context.setResponseEntity(ResultVo.createResponseEntity(result)); return; } // 2. For each schedule, determine today's work times for (Map staffSchedule : staffSchedules) { String scheduleId = (String) staffSchedule.get("scheduleId"); // Query schedule definition Map schedParams = new HashMap<>(); schedParams.put("scheduleId", scheduleId); List schedules = attendanceReminderV1ServiceDao.queryScheduleById(schedParams); if (schedules == null || schedules.isEmpty()) continue; Map schedule = schedules.get(0); String scheduleType = (String) schedule.get("scheduleType"); if (!"1001".equals(schedule.get("state"))) continue; // not started List dayEntries = null; if ("1001".equals(scheduleType)) { // Day-based schedule int scheduleCycle = Integer.parseInt(safeString(schedule.get("scheduleCycle"))); String computeTimeStr = safeString(schedule.get("computeTime")); try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date computeDate = sdf.parse(computeTimeStr); long daysBetween = (cal.getTimeInMillis() - computeDate.getTime()) / (24 * 3600 * 1000); int dayInCycle = (int) ((daysBetween % scheduleCycle) + 1); Map dayParams = new HashMap<>(); dayParams.put("scheduleId", scheduleId); dayParams.put("day", String.valueOf(dayInCycle)); dayEntries = attendanceReminderV1ServiceDao.queryScheduleDay(dayParams); } catch (Exception e) { continue; } } else if ("2002".equals(scheduleType)) { // Week-based schedule Map dayParams = new HashMap<>(); dayParams.put("scheduleId", scheduleId); dayParams.put("day", String.valueOf(weekFlag)); dayParams.put("weekFlag", String.valueOf(weekFlag)); dayEntries = attendanceReminderV1ServiceDao.queryScheduleDay(dayParams); } else if ("3003".equals(scheduleType)) { // Month-based schedule Map dayParams = new HashMap<>(); dayParams.put("scheduleId", scheduleId); dayParams.put("day", String.valueOf(dayOfMonth)); dayEntries = attendanceReminderV1ServiceDao.queryScheduleDay(dayParams); } if (dayEntries == null || dayEntries.isEmpty()) continue; for (Map dayEntry : dayEntries) { String workday = (String) dayEntry.get("workday"); if ("2002".equals(workday)) continue; // rest day String dayId = (String) dayEntry.get("dayId"); Map timeParams = new HashMap<>(); timeParams.put("dayId", dayId); List times = attendanceReminderV1ServiceDao.queryScheduleTimes(timeParams); if (times == null || times.isEmpty()) continue; // 3. Check if clocked in today Map punchParams = new HashMap<>(); punchParams.put("userId", userId); punchParams.put("punchType", "ON"); Map todayPunch = attendanceReminderV1ServiceDao.queryTodayPunch(punchParams); for (Map time : times) { String startTime = (String) time.get("startTime"); String endTime = (String) time.get("endTime"); String fullStartTime = today + " " + startTime + ":00"; try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date startDt = sdf.parse(fullStartTime); Date now = new Date(); long diffMinutes = (now.getTime() - startDt.getTime()) / (60 * 1000); // Within 5 minutes after start time, or after if (diffMinutes >= 0 && diffMinutes <= 120) { boolean clockedIn = todayPunch != null; if (!clockedIn) { hasReminder = true; JSONObject shift = new JSONObject(); shift.put("startTime", startTime); shift.put("endTime", endTime); shift.put("fullStartTime", fullStartTime); shift.put("scheduleName", staffSchedule.getOrDefault("scheduleName", "")); shift.put("overdueMinutes", diffMinutes); overdueShifts.add(shift); } } } catch (Exception ignored) { } } } } // Fallback: if no overdue shifts found from schedule, but staff has schedule and no clockin, // use default time window to check if (!hasReminder && overdueShifts.isEmpty()) { Map punchParams = new HashMap<>(); punchParams.put("userId", userId); punchParams.put("punchType", "ON"); Map todayPunch = attendanceReminderV1ServiceDao.queryTodayPunch(punchParams); if (todayPunch == null) { checkDefaultReminder(userId, today, overdueShifts); if (!overdueShifts.isEmpty()) hasReminder = true; } } JSONObject result = new JSONObject(); result.put("hasReminder", hasReminder); result.put("overdueShifts", overdueShifts); context.setResponseEntity(ResultVo.createResponseEntity(result)); } /** * Default reminder check: when no schedule, assume staff should be working. * Checks if current time is past 8:00 AM and no clock-in today. */ private void checkDefaultReminder(String userId, String today, JSONArray overdueShifts) { try { // Default work window: 08:00 - 18:00 String defaultStart = today + " 08:00:00"; String defaultEnd = today + " 18:00:00"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date startDt = sdf.parse(defaultStart); Date endDt = sdf.parse(defaultEnd); Date now = new Date(); // Only remind between 8:00 and 18:00, and at least 5 min past start if (now.after(startDt) && now.before(endDt)) { long diffMinutes = (now.getTime() - startDt.getTime()) / (60 * 1000); if (diffMinutes >= 0 && diffMinutes <= 600) { // within 10 hours JSONObject shift = new JSONObject(); shift.put("startTime", "08:00"); shift.put("endTime", "18:00"); shift.put("fullStartTime", defaultStart); shift.put("scheduleName", "默认班次"); shift.put("overdueMinutes", diffMinutes); overdueShifts.add(shift); } } } catch (Exception ignored) { } } private String safeString(Object obj) { if (obj == null) return ""; if (obj instanceof String) return (String) obj; if (obj instanceof java.sql.Timestamp) { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((java.sql.Timestamp) obj); } return obj.toString(); } }