QueryAttendanceReminderCmd.java 9.98 KB
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<String, Object> staffParams = new HashMap<>();
        staffParams.put("staffId", userId);
        List<Map> 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<String, Object> schedParams = new HashMap<>();
            schedParams.put("scheduleId", scheduleId);
            List<Map> 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<Map> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> timeParams = new HashMap<>();
                timeParams.put("dayId", dayId);
                List<Map> times = attendanceReminderV1ServiceDao.queryScheduleTimes(timeParams);

                if (times == null || times.isEmpty()) continue;

                // 3. Check if clocked in today
                Map<String, Object> 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<String, Object> 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();
    }
}