Browse Source

员工考勤,日历编写

yusm 2 tuần trước cách đây
mục cha
commit
c7e9646932

+ 7 - 0
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/constant/Constant.java

@@ -46,4 +46,11 @@ public class Constant {
     public static final String[] LEAVE_CHECK= {"审核结果","审核人","请假时间","备注"};
 
     public static final String[] DEFAULT_PROD_CATEGORY = {"铝铆焊件", "钢铆焊件", "非铆焊件"};
+    public static final int BAI_BAN=0;
+    public static final int DA_YE_BAN=1;
+    public static final int XIAO_YE_BAN=2;
+    public static final int ZHONG_BAN=3;
+    public static final int YI_CHANG=4;
+    public static final int XIU_XI=5;
+    public static final int QING_JIA=6;
 }

+ 53 - 0
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/controller/AttendanceStaffController.java

@@ -0,0 +1,53 @@
+package com.management.platform.controller;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.entity.AttendanceStaff;
+import com.management.platform.service.AttendanceService;
+import com.management.platform.service.AttendanceStaffService;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-06-17
+ */
+@RestController
+@RequestMapping("/attendance-staff")
+public class AttendanceStaffController {
+
+    @Resource
+    private AttendanceStaffService attendanceStaffService;
+
+
+
+    //更新考勤数据
+    @RequestMapping("/refreshData")
+    private HttpRespMsg refreshData(String month) {
+        return attendanceStaffService.refreshData(month);
+    }
+
+    //考勤列表
+    @RequestMapping("/getListData")
+    private HttpRespMsg getListData(String month) {
+        return attendanceStaffService.getListData(month);
+    }
+
+
+    @RequestMapping("/getAttendanceUserData")
+    private HttpRespMsg getAttendanceUserData(String month, String date, String userId, HttpServletRequest request) {
+        return attendanceStaffService.getAttendanceUserData(month,date,userId,request);
+    }
+
+
+}
+

+ 7 - 1
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/entity/Attendance.java

@@ -5,11 +5,15 @@ import com.baomidou.mybatisplus.extension.activerecord.Model;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableField;
 import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.Date;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
 
 
 /**
@@ -58,7 +62,9 @@ public class Attendance extends Model<Attendance> {
      * 打卡时间
      */
     @TableField("clock_time")
-    private Date clockTime;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime clockTime;
 
     /**
      * 周几

+ 110 - 0
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/entity/AttendanceStaff.java

@@ -0,0 +1,110 @@
+package com.management.platform.entity;
+
+import java.math.BigDecimal;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-06-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class AttendanceStaff extends Model<AttendanceStaff> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 月份
+     */
+    @TableField("month")
+    private String month;
+
+    /**
+     * 打卡日期
+     */
+    @TableField("clock_date")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate clockDate;
+
+    /**
+     * 工号
+     */
+    @TableField("job_number")
+    private String jobNumber;
+
+    /**
+     * 名称
+     */
+    @TableField("name")
+    private String name;
+
+    /**
+     * 部门名称
+     */
+    @TableField("dept_name")
+    private String deptName;
+
+    /**
+     * 上班打卡时间
+     */
+    @TableField("clock_start_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime clockStartTime;
+
+    /**
+     * 下班打卡时间
+     */
+    @TableField("clock_end_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime clockEndTime;
+
+    /**
+     * 打卡时长
+     */
+    @TableField("work_hour")
+    private BigDecimal workHour;
+
+    /**
+     * 考勤状态 0白班,1大夜班,2小夜班,3中班,4班次异常,5休息,6调休,7请假
+     */
+    @TableField("attendance_type")
+    private Integer attendanceType;
+
+    @TableField(exist = false)
+    private List<HashMap<String,Object>> maplist;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 5 - 1
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/entity/HighTemperatureSet.java

@@ -8,9 +8,11 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import java.io.Serializable;
 import java.util.Date;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
 
 /**
  * <p>
@@ -40,7 +42,9 @@ public class HighTemperatureSet extends Model<HighTemperatureSet> {
      * 结束日期
      */
     @TableField("end_date")
-    private Date endDate;
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate endDate;
 
     /**
      * 描述

+ 6 - 1
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/entity/SpecialDateSet.java

@@ -5,11 +5,14 @@ import com.baomidou.mybatisplus.extension.activerecord.Model;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableField;
 import java.io.Serializable;
+import java.time.LocalDate;
 import java.util.Date;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
 
 /**
  * <p>
@@ -33,7 +36,9 @@ public class SpecialDateSet extends Model<SpecialDateSet> {
      * 特殊日期
      */
     @TableField("special_date")
-    private Date specialDate;
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate specialDate;
 
     /**
      * 日期类型: 0节假日,1工作日

+ 16 - 0
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/mapper/AttendanceStaffMapper.java

@@ -0,0 +1,16 @@
+package com.management.platform.mapper;
+
+import com.management.platform.entity.AttendanceStaff;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-06-17
+ */
+public interface AttendanceStaffMapper extends BaseMapper<AttendanceStaff> {
+
+}

+ 24 - 0
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/service/AttendanceStaffService.java

@@ -0,0 +1,24 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.AttendanceStaff;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.util.HttpRespMsg;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-06-17
+ */
+public interface AttendanceStaffService extends IService<AttendanceStaff> {
+
+    HttpRespMsg refreshData(String month);
+
+    HttpRespMsg getListData(String month);
+
+    HttpRespMsg getAttendanceUserData(String month, String date, String userId, HttpServletRequest request);
+}

+ 14 - 18
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/service/impl/AttendanceServiceImpl.java

@@ -125,9 +125,10 @@ public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attenda
                     String timeValue = stringCellValue.substring(0,index-1);
                     String weekValue = stringCellValue.substring(index);
 
-                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-                    Date date = simpleDateFormat.parse(timeValue);
-                    attendance.setClockTime(date);
+//                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+//                    Date date = simpleDateFormat.parse(timeValue);
+                    LocalDateTime parse = LocalDateTime.parse(timeValue, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+                    attendance.setClockTime(parse);
                     attendance.setWeek(weekValue);
 
                 }else {
@@ -172,9 +173,6 @@ public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attenda
             //msg.setError("文件加密状态,需要先解除加密状态再上传");
             msg.setError(MessageUtils.message("file.encryption"));
             return msg;
-        } catch (ParseException e) {
-            msg.setError("日期解析有问题");
-            return msg;
         }
     }
 
@@ -242,12 +240,11 @@ public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attenda
                 .between("special_date", startOfMonth, endOfMonth).eq("type",1));
         List<String> collect2 = new ArrayList<>();
         if (!dateSetList.isEmpty()){
-            List<Date> collect = dateSetList.stream().map(SpecialDateSet::getSpecialDate).collect(Collectors.toList());
-            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
-            List<String> collect1 = collect.stream()
-                    .map(sdf::format)
-                    .collect(Collectors.toList());
-           collect2.addAll(collect1);
+            String[] array = dateSetList.stream()
+                    .map(SpecialDateSet::getSpecialDate)
+                    .map(date -> date.format(DateTimeFormatter.ISO_LOCAL_DATE))
+                    .toArray(String[]::new);
+            collect2.addAll(Arrays.stream(array).collect(Collectors.toList()));
         }
         // 将字符串日期转换为LocalDate并添加到列表中
         DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
@@ -270,12 +267,11 @@ public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attenda
         List<SpecialDateSet> dateSetList = specialDateSetService.list(new QueryWrapper<SpecialDateSet>()
                 .between("special_date", startOfMonth, endOfMonth).eq("type",0));
         if (!dateSetList.isEmpty()){
-            List<Date> collect = dateSetList.stream().map(SpecialDateSet::getSpecialDate).collect(Collectors.toList());
-            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
-            List<String> collect1 = collect.stream()
-                    .map(sdf::format)
-                    .collect(Collectors.toList());
-            collect2.addAll(collect1);
+            String[] array = dateSetList.stream()
+                    .map(SpecialDateSet::getSpecialDate)
+                    .map(date -> date.format(DateTimeFormatter.ISO_LOCAL_DATE))
+                    .toArray(String[]::new);
+            collect2.addAll(Arrays.stream(array).collect(Collectors.toList()));
         }
         // 将字符串日期转换为LocalDate并添加到列表中
         DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;

+ 398 - 0
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/service/impl/AttendanceStaffServiceImpl.java

@@ -0,0 +1,398 @@
+package com.management.platform.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.entity.Attendance;
+import com.management.platform.entity.AttendanceStaff;
+import com.management.platform.entity.DayInfo;
+import com.management.platform.entity.User;
+import com.management.platform.mapper.AttendanceStaffMapper;
+import com.management.platform.service.AttendanceService;
+import com.management.platform.service.AttendanceStaffService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.service.UserService;
+import com.management.platform.util.HttpRespMsg;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.management.platform.constant.Constant.*;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-06-17
+ */
+@Service
+public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMapper, AttendanceStaff> implements AttendanceStaffService {
+
+    @Resource
+    private AttendanceService attendanceService;
+
+    @Resource
+    private UserService userService;
+
+    @Override
+    public HttpRespMsg refreshData(String month) {
+        HttpRespMsg msg = new HttpRespMsg();
+        this.remove(new QueryWrapper<AttendanceStaff>().eq("month", month));
+
+
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
+        YearMonth yearMonth = YearMonth.parse(month, formatter);
+        LocalDate localDate = yearMonth.plusMonths(1).atDay(2);//查询条件
+
+        LocalDate startDate = yearMonth.atDay(1);//本月开始日期
+        LocalDate endDate = yearMonth.atEndOfMonth();//本月结束日期
+        ArrayList<LocalDate> localDates = new ArrayList<>();//本月所有日期
+        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
+           localDates.add(date);
+        }
+
+        List<Attendance> attendanceList = attendanceService.list(new QueryWrapper<Attendance>()
+                .eq("month", month).or().le("clock_time", localDate).orderByAsc("job_number","clock_time"));
+
+        List<AttendanceStaff> addList = new ArrayList<>();
+        Map<String, List<Attendance>> listMap = attendanceList.stream().collect(Collectors.groupingBy(Attendance::getJobNumber));
+        Set<Map.Entry<String, List<Attendance>>> entries = listMap.entrySet();
+        for (Map.Entry<String, List<Attendance>> entry : entries) {
+            String jobNumber = entry.getKey();
+            List<Attendance> attendances = entry.getValue();
+            if (!attendances.isEmpty()) {
+                Attendance attendance = attendances.get(0);
+                //遍历本月的日期,根据考勤记录,确定考勤的上班打卡时间,下班打卡时间,上班时间, 班次类型:白班,中班,小夜班,大夜班
+                for (LocalDate date : localDates) {
+
+
+                    LocalDateTime dayStartTime = date.atStartOfDay();//当日的最早时间
+                    LocalDateTime dayEndTime = date.plusDays(1).atStartOfDay().plusHours(10);//假设的当日大夜班到第二天的下班最晚时间
+                    int year = date.getYear();
+                    int monthValue = date.getMonthValue();
+                    int dayValue = date.getDayOfMonth();
+                    //早班上下班时间判断
+                    Optional<Attendance> zaoShangCount = attendances.stream().filter(a -> a.getClockTime().isAfter(LocalDateTime.of(year, monthValue, dayValue, 7, 0))
+                            && a.getClockTime().isBefore(LocalDateTime.of(year, monthValue, dayValue, 9, 0))
+                    ).findFirst();
+                    Optional<Attendance> zaoXiaCount = attendances.stream().filter(a -> a.getClockTime().isAfter(LocalDateTime.of(year, monthValue, dayValue, 16, 0))
+                            && a.getClockTime().isBefore(LocalDateTime.of(year, monthValue, dayValue, 19, 0))
+                    ).max(Comparator.comparing(Attendance::getClockTime));
+                    //中班上下班时间判断
+                    Optional<Attendance> zhongShangCount = attendances.stream().filter(a -> a.getClockTime().isAfter(LocalDateTime.of(year, monthValue, dayValue, 12, 0))
+                            && a.getClockTime().isBefore(LocalDateTime.of(year, monthValue, dayValue, 14, 0))
+                    ).findFirst();
+
+                    Optional<Attendance> zhongXiaCount = attendances.stream().filter(a -> a.getClockTime().isAfter(LocalDateTime.of(year, monthValue, dayValue, 20, 0))
+                            && a.getClockTime().isBefore(LocalDateTime.of(year, monthValue, dayValue, 23, 0))
+                    ).max(Comparator.comparing(Attendance::getClockTime));
+                    //小夜班上下班时间判断
+                    Optional<Attendance> xiaoYeShangCount = attendances.stream().filter(a -> a.getClockTime().isAfter(LocalDateTime.of(year, monthValue, dayValue, 15, 0))
+                            && a.getClockTime().isBefore(LocalDateTime.of(year, monthValue, dayValue, 19, 0))
+                    ).findFirst();
+
+                    boolean isLastDay = endDate.isEqual(LocalDate.of(year, monthValue, dayValue));//是否是该月份最后一天
+                    Optional<Attendance> xiaoYeXiaCount = isLastDay ?
+                            attendances.stream().filter(a -> a.getClockTime().isAfter(LocalDateTime.of(year, monthValue, dayValue, 22, 0))
+                            && a.getClockTime().isBefore(LocalDateTime.of(year, monthValue+1, 1, 3, 0))
+                    ).max(Comparator.comparing(Attendance::getClockTime)) :
+                            attendances.stream().filter(a -> a.getClockTime().isAfter(LocalDateTime.of(year, monthValue, dayValue, 22, 0))
+                            && a.getClockTime().isBefore(LocalDateTime.of(year, monthValue, dayValue+1, 3, 0))
+                    ).max(Comparator.comparing(Attendance::getClockTime));
+                    //大夜班上下班时间判断
+                    Optional<Attendance> daYeShangCount =isLastDay?
+                            attendances.stream().filter(a -> a.getClockTime().isAfter(LocalDateTime.of(year, monthValue, dayValue, 23, 0))
+                                    && a.getClockTime().isBefore(LocalDateTime.of(year, monthValue+1, 1, 1, 0))
+                            ).findFirst():
+                     attendances.stream().filter(a -> a.getClockTime().isAfter(LocalDateTime.of(year, monthValue, dayValue, 23, 0))
+                            && a.getClockTime().isBefore(LocalDateTime.of(year, monthValue, dayValue + 1, 1, 0))
+                    ).findFirst();
+
+                    Optional<Attendance> daYeXiaCount =isLastDay?
+                            attendances.stream().filter(a -> a.getClockTime().isAfter(LocalDateTime.of(year, monthValue+1, 1, 6, 0))
+                                    && a.getClockTime().isBefore(LocalDateTime.of(year, monthValue+1,  1, 10, 0))
+                            ).max(Comparator.comparing(Attendance::getClockTime))
+                            :
+                            attendances.stream().filter(a -> a.getClockTime().isAfter(LocalDateTime.of(year, monthValue, dayValue + 1, 6, 0))
+                            && a.getClockTime().isBefore(LocalDateTime.of(year, monthValue, dayValue + 1, 10, 0))
+                    ).max(Comparator.comparing(Attendance::getClockTime));
+
+                    AttendanceStaff staff = new AttendanceStaff();
+                    staff.setJobNumber(jobNumber);
+                    staff.setMonth(month);
+                    staff.setName(attendance.getName());
+                    staff.setDeptName(attendance.getDeptName());
+                    staff.setClockDate(date);
+                    /**
+                     * 考勤状态 0白班,1大夜班,2小夜班,3中班,4班次异常,5休息,6调休,7请假
+                     */
+
+                    //1先判断白班
+                    if (zaoShangCount.isPresent()&&zaoXiaCount.isPresent()) {
+                        LocalDateTime startClockTime = zaoShangCount.get().getClockTime();
+                        LocalDateTime endClockTime = zaoXiaCount.get().getClockTime();
+                        staff.setClockStartTime(startClockTime);
+                        staff.setClockEndTime(endClockTime);
+                        BigDecimal bigDecimal = calculateWorkHours(startClockTime, endClockTime);
+                        staff.setWorkHour(bigDecimal);
+                        staff.setAttendanceType(BAI_BAN);
+                    }
+
+                    //2再判断中班
+                    else  if (zhongShangCount.isPresent()&&zhongXiaCount.isPresent()) {
+                        LocalDateTime startClockTime = zhongShangCount.get().getClockTime();
+                        LocalDateTime endClockTime = zhongXiaCount.get().getClockTime();
+                        staff.setClockStartTime(startClockTime);
+                        staff.setClockEndTime(endClockTime);
+                        BigDecimal bigDecimal = calculateWorkHours(startClockTime, endClockTime);
+                        staff.setWorkHour(bigDecimal);
+                        staff.setAttendanceType(ZHONG_BAN);
+                    }
+
+                    //3判断小夜班
+                    else if (xiaoYeShangCount.isPresent()&&xiaoYeXiaCount.isPresent()) {
+                        LocalDateTime startClockTime = xiaoYeShangCount.get().getClockTime();
+                        LocalDateTime endClockTime = xiaoYeXiaCount.get().getClockTime();
+                        staff.setClockStartTime(startClockTime);
+                        staff.setClockEndTime(endClockTime);
+                        BigDecimal bigDecimal = calculateWorkHours(startClockTime, endClockTime);
+                        staff.setWorkHour(bigDecimal);
+                        staff.setAttendanceType(XIAO_YE_BAN);
+                    }
+
+                    //4判断大夜班
+                    else if (daYeShangCount.isPresent()&&daYeXiaCount.isPresent()) {
+                        LocalDateTime startClockTime = daYeShangCount.get().getClockTime();
+                        LocalDateTime endClockTime = daYeXiaCount.get().getClockTime();
+                        staff.setClockStartTime(startClockTime);
+                        staff.setClockEndTime(endClockTime);
+                        BigDecimal bigDecimal = calculateWorkHours(startClockTime, endClockTime);
+                        staff.setWorkHour(bigDecimal);
+                        staff.setAttendanceType(DA_YE_BAN);
+                    }
+                    if (staff.getClockStartTime()!=null){
+                        addList.add(staff);
+                    }
+
+                }
+            }
+
+        }
+        saveBatch(addList);
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg getListData(String month) {
+        HttpRespMsg msg = new HttpRespMsg();
+        List<AttendanceStaff> list = list(new QueryWrapper<AttendanceStaff>().eq("month", month));
+        msg.setData(list);
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg getAttendanceUserData(String month, String date, String userId, HttpServletRequest request) {
+        HttpRespMsg msg = new HttpRespMsg();
+        QueryWrapper<AttendanceStaff> wrapper = new QueryWrapper<AttendanceStaff>()
+                .eq("month", month).eq("date", date);
+        if (userId==null|| StringUtils.isEmpty(userId)){
+            User user = userService.getById(request.getHeader("Token"));
+            wrapper.eq("job_number", user.getJobNumber());
+        }else {
+            User user = userService.getById(userId);
+            wrapper.eq("job_number", user.getJobNumber());
+        }
+        List<AttendanceStaff> list = list(wrapper);
+        for (AttendanceStaff staff : list) {
+            Integer type = staff.getAttendanceType();
+            LocalDate clockDate = staff.getClockDate();
+            LocalDateTime clockStartTime = staff.getClockStartTime();
+            LocalTime startTime = clockStartTime.toLocalTime();
+            LocalDateTime clockEndTime = staff.getClockEndTime();
+            LocalTime endTime = clockEndTime.toLocalTime();
+
+            List<HashMap<String,Object>> maplist = new ArrayList<>();
+            switch (type) {
+                case BAI_BAN:
+                    if (startTime.isAfter(LocalTime.of(8,0,0))){
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",startTime+"上班考勤打卡");
+                        map.put("res","晚点");
+                        maplist.add(map);
+                    }else {
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",startTime+"上班考勤打卡");
+                        map.put("res","正常");
+                        maplist.add(map);
+                    }
+
+                    if (endTime.isBefore(LocalTime.of(15,0,0))){
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",endTime+"下班考勤打卡");
+                        map.put("res","早退");
+                        maplist.add(map);
+                    }else {
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",endTime+"下班考勤打卡");
+                        map.put("res","正常");
+                        double v = calculateOvertimeHours(startTime, endTime);
+                        if (v>0) {
+                            map.put("extra", "(17:00-"+endTime+"加班"+v+"小时)");
+                        }
+                        maplist.add(map);
+                    }
+                    break;
+                case ZHONG_BAN:
+                    if (startTime.isAfter(LocalTime.of(13,0,0))){
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",startTime+"上班考勤打卡");
+                        map.put("res","晚点");
+                        maplist.add(map);
+                    }else {
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",startTime+"上班考勤打卡");
+                        map.put("res","正常");
+                        maplist.add(map);
+                    }
+
+                    if (endTime.isBefore(LocalTime.of(21,0,0))){
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",endTime+"下班考勤打卡");
+                        map.put("res","早退");
+                        maplist.add(map);
+                    }else {
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",endTime+"下班考勤打卡");
+                        map.put("res","正常");
+                        double v = calculateOvertimeHours(startTime, endTime);
+                        if (v>0) {
+                            map.put("extra", "(13:00-"+endTime+"加班"+v+"小时)");
+                        }
+                        maplist.add(map);
+                    }
+                    break;
+                case XIAO_YE_BAN:
+                    if (startTime.isAfter(LocalTime.of(16,0,0))){
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",startTime+"上班考勤打卡");
+                        map.put("res","晚点");
+                        maplist.add(map);
+                    }else {
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",startTime+"上班考勤打卡");
+                        map.put("res","正常");
+                        maplist.add(map);
+                    }
+
+                    if (endTime.isBefore(LocalTime.of(23,59,59))){
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",endTime+"下班考勤打卡");
+                        map.put("res","早退");
+                        maplist.add(map);
+                    }else {
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",endTime+"下班考勤打卡");
+                        map.put("res","正常");
+                        double v = calculateOvertimeHours(startTime, endTime);
+                        if (v>0) {
+                            map.put("extra", "(00:00-"+endTime+"加班"+v+"小时)");
+                        }
+                        maplist.add(map);
+                    }
+                    break;
+                case DA_YE_BAN:
+                    if (startTime.isAfter(LocalTime.of(23,59,59))){
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",startTime+"上班考勤打卡");
+                        map.put("res","晚点");
+                        maplist.add(map);
+                    }else {
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",startTime+"上班考勤打卡");
+                        map.put("res","正常");
+                        maplist.add(map);
+                    }
+
+                    if (endTime.isBefore(LocalTime.of(8,0,0))){
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",endTime+"下班考勤打卡");
+                        map.put("res","早退");
+                        maplist.add(map);
+                    }else {
+                        HashMap<String, Object> map = new HashMap<>();
+                        map.put("msg",endTime+"下班考勤打卡");
+                        map.put("res","正常");
+                        double v = calculateOvertimeHours(startTime, endTime);
+                        if (v>0) {
+                            map.put("extra", "(08:00-"+endTime+"加班"+v+"小时)");
+                        }
+                        maplist.add(map);
+                    }
+                    break;
+
+            }
+        }
+        msg.setData(list);
+        return null;
+    }
+
+    public static BigDecimal calculateWorkHours(LocalDateTime start, LocalDateTime end) {
+        // 1. 计算时间差
+        Duration duration = Duration.between(start, end);
+
+        // 2. 转换为小时数(double)
+        double hours = duration.toMinutes() / 60.0;
+
+        // 3. 转换为BigDecimal并四舍五入保留1位小数
+        return BigDecimal.valueOf(hours)
+                .setScale(1, RoundingMode.HALF_UP);
+    }
+
+
+    /**
+     * 计算加班时长(以30分钟为单位)
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 加班单位数(每30分钟为一个单位)
+     */
+    public static int calculateOvertimeUnits(LocalTime startTime, LocalTime endTime) {
+        Duration workDuration;
+
+        // 处理跨午夜情况
+        if (endTime.isBefore(startTime)) {
+            workDuration = Duration.between(startTime, LocalTime.MAX)
+                    .plus(Duration.between(LocalTime.MIN, endTime))
+                    .plusMinutes(1); // 加上午夜这一分钟
+        } else {
+            workDuration = Duration.between(startTime, endTime);
+        }
+
+        long totalMinutes = workDuration.toMinutes();
+
+        if (totalMinutes <= 60) return 0;
+
+        long overtimeMinutes = totalMinutes - 60;
+        return (int) Math.round(overtimeMinutes / 30.0);
+    }
+
+
+    /**
+     * 计算加班时长(返回小时数,保留1位小数)
+     */
+    public static double calculateOvertimeHours(LocalTime startTime, LocalTime endTime) {
+        int units = calculateOvertimeUnits(startTime, endTime);
+        return units * 0.5; // 每个单位是0.5小时
+    }
+
+
+
+}

+ 24 - 0
fhKeeper/formulahousekeeper/management-workshop/src/main/resources/mapper/AttendanceStaffMapper.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.AttendanceStaffMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.AttendanceStaff">
+        <id column="id" property="id" />
+        <result column="month" property="month" />
+        <result column="clock_date" property="clockDate" />
+        <result column="job_number" property="jobNumber" />
+        <result column="name" property="name" />
+        <result column="dept_name" property="deptName" />
+        <result column="clock_start_time" property="clockStartTime" />
+        <result column="clock_end_time" property="clockEndTime" />
+        <result column="work_hour" property="workHour" />
+        <result column="attendance_type" property="attendanceType" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, month, clock_date, job_number, name, dept_name, clock_start_time, clock_end_time, work_hour, attendance_type
+    </sql>
+
+</mapper>