|
@@ -3,16 +3,15 @@ package com.management.platform.service.impl;
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
import com.management.platform.entity.*;
|
|
import com.management.platform.entity.*;
|
|
import com.management.platform.mapper.AttendanceMapper;
|
|
import com.management.platform.mapper.AttendanceMapper;
|
|
|
|
+import com.management.platform.mapper.WxCorpInfoMapper;
|
|
import com.management.platform.service.*;
|
|
import com.management.platform.service.*;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.management.platform.util.ExcelUtil;
|
|
import com.management.platform.util.ExcelUtil;
|
|
import com.management.platform.util.HttpRespMsg;
|
|
import com.management.platform.util.HttpRespMsg;
|
|
-import com.management.platform.util.MessageUtils;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
-import org.apache.poi.EncryptedDocumentException;
|
|
|
|
-import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
|
|
|
import org.apache.poi.ss.usermodel.*;
|
|
import org.apache.poi.ss.usermodel.*;
|
|
-import org.apache.poi.xssf.usermodel.XSSFCell;
|
|
|
|
|
|
+
|
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.util.StringUtils;
|
|
import org.springframework.util.StringUtils;
|
|
@@ -20,18 +19,20 @@ import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
import javax.annotation.Resource;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
-import java.io.IOException;
|
|
|
|
|
|
+
|
|
import java.io.InputStream;
|
|
import java.io.InputStream;
|
|
-import java.text.DateFormat;
|
|
|
|
-import java.text.ParseException;
|
|
|
|
-import java.text.SimpleDateFormat;
|
|
|
|
-import java.time.DayOfWeek;
|
|
|
|
-import java.time.LocalDate;
|
|
|
|
-import java.time.LocalDateTime;
|
|
|
|
-import java.time.YearMonth;
|
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
+
|
|
|
|
+import java.time.*;
|
|
|
|
+import java.time.chrono.ChronoLocalDateTime;
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
+import java.time.format.TextStyle;
|
|
import java.util.*;
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
+import java.util.stream.IntStream;
|
|
|
|
+
|
|
|
|
+import static com.management.platform.constant.Constant.*;
|
|
|
|
+import static com.management.platform.service.impl.AttendanceStaffServiceImpl.calculateOvertimeHours;
|
|
|
|
|
|
/**
|
|
/**
|
|
* <p>
|
|
* <p>
|
|
@@ -47,6 +48,9 @@ public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attenda
|
|
@Resource
|
|
@Resource
|
|
private UserService userService;
|
|
private UserService userService;
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
+ private DepartmentService departmentService;
|
|
|
|
+
|
|
@Resource
|
|
@Resource
|
|
private AttendanceService attendanceService;
|
|
private AttendanceService attendanceService;
|
|
|
|
|
|
@@ -56,6 +60,22 @@ public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attenda
|
|
@Resource
|
|
@Resource
|
|
private AttendanceStaffService attendanceStaffService;
|
|
private AttendanceStaffService attendanceStaffService;
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
+ private ApplyFormService applyFormService;
|
|
|
|
+
|
|
|
|
+ @Value(value = "${upload.path}")
|
|
|
|
+ private String path;
|
|
|
|
+
|
|
|
|
+ @Resource
|
|
|
|
+ WxCorpInfoMapper wxCorpInfoMapper;
|
|
|
|
+
|
|
|
|
+ private static final List<String> holidays = Arrays.asList("2025-01-01",
|
|
|
|
+ "2025-01-28", "2025-01-29", "2025-01-30", "2025-01-31", "2025-02-03", "2025-02-04",
|
|
|
|
+ "2025-04-04",
|
|
|
|
+ "2025-05-01", "2025-05-02", "2025-05-05",
|
|
|
|
+ "2025-06-02",
|
|
|
|
+ "2025-10-01", "2025-10-02", "2025-10-03", "2025-10-06", "2025-10-07", "2025-10-08" );
|
|
|
|
+
|
|
@Override
|
|
@Override
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
public HttpRespMsg importAttendanceData(String month, MultipartFile file, HttpServletRequest request) {
|
|
public HttpRespMsg importAttendanceData(String month, MultipartFile file, HttpServletRequest request) {
|
|
@@ -224,6 +244,337 @@ public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attenda
|
|
return msg;
|
|
return msg;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
+ public HttpRespMsg exportAttendanceData(String month, HttpServletRequest request) {
|
|
|
|
+ HttpRespMsg msg=new HttpRespMsg();
|
|
|
|
+ User user = userService.getById(request.getHeader("token"));
|
|
|
|
+ WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", user.getCompanyId()));
|
|
|
|
+
|
|
|
|
+ Map<String, Object> monthInfo = getMonthInfo(month, holidays);
|
|
|
|
+ String firstDay = monthInfo.get("firstDay").toString();
|
|
|
|
+ String lastDay = monthInfo.get("lastDay").toString();
|
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
|
|
|
|
+ DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy/MM/dd");
|
|
|
|
+ DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
|
+ YearMonth yearMonth = YearMonth.parse(month, formatter);
|
|
|
|
+ LocalDate startDate = yearMonth.atDay(1);
|
|
|
|
+ LocalDate endDate = startDate.plusMonths(1);
|
|
|
|
+ int year = yearMonth.getYear();
|
|
|
|
+ int monthValue = yearMonth.getMonthValue();
|
|
|
|
+ String title=year+"年"+monthValue+"月考勤表";
|
|
|
|
+
|
|
|
|
+ ArrayList<ArrayList<String>> list = new ArrayList<>();
|
|
|
|
+ List<User> userList = userService.list(new QueryWrapper<User>().eq("is_active", 1));
|
|
|
|
+ List<Department> departmentList = departmentService.list(new QueryWrapper<Department>());
|
|
|
|
+ List<AttendanceStaff> staffList = attendanceStaffService.list(new QueryWrapper<AttendanceStaff>().between("clock_date", firstDay, lastDay));
|
|
|
|
+ List<ApplyForm> applyFormList = applyFormService.getListByFirstDateAndLastDate(startDate, endDate);
|
|
|
|
+
|
|
|
|
+ for (int i = 0; i < userList.size(); i++) {
|
|
|
|
+ ArrayList<String> strings = new ArrayList<>();
|
|
|
|
+ strings.add((i+1)+"");
|
|
|
|
+ User u = userList.get(i);
|
|
|
|
+ strings.add(u.getJobNumber());//员工编号
|
|
|
|
+ strings.add(u.getName());
|
|
|
|
+ Optional<Department> deptFirst = departmentList.stream().filter(d -> u.getDepartmentId() != null && d.getDepartmentId().equals(u.getDepartmentId())).findFirst();
|
|
|
|
+ if (deptFirst.isPresent()) {
|
|
|
|
+ strings.add(deptFirst.get().getDepartmentName());
|
|
|
|
+ }else {
|
|
|
|
+ strings.add("");
|
|
|
|
+ }
|
|
|
|
+ strings.add(u.getInductionDate()==null?"":u.getInductionDate().format(formatter1));//入职日期
|
|
|
|
+ strings.add(u.getInactiveDate()==null?"":u.getInactiveDate().format(formatter1));//离职日期
|
|
|
|
+ strings.add("");//备注
|
|
|
|
+
|
|
|
|
+ List<AttendanceStaff> staffCollect = staffList.stream().filter(s -> s.getJobNumber().equals(u.getJobNumber())).collect(Collectors.toList());
|
|
|
|
+ strings.add(staffCollect.size()+"");//实际出勤天
|
|
|
|
+
|
|
|
|
+ BigDecimal total8 = getDecimal(applyFormList,"病假",u.getJobNumber());
|
|
|
|
+ strings.add(total8.toString());
|
|
|
|
+ BigDecimal total9 = getDecimal(applyFormList,"带薪假",u.getJobNumber());
|
|
|
|
+ strings.add(total9.toString());
|
|
|
|
+ BigDecimal total10 = getDecimal(applyFormList,"育儿假",u.getJobNumber());
|
|
|
|
+ strings.add(total10.toString());
|
|
|
|
+ BigDecimal total11 = getDecimal(applyFormList,"工伤假",u.getJobNumber());
|
|
|
|
+ strings.add(total11.toString());
|
|
|
|
+ BigDecimal total12 = getDecimal(applyFormList,"旷工",u.getJobNumber());
|
|
|
|
+ strings.add(total12.toString());
|
|
|
|
+ BigDecimal total13 = getDecimal(applyFormList,"事假",u.getJobNumber());
|
|
|
|
+ strings.add(total13.toString());
|
|
|
|
+ BigDecimal total14 = getDecimal(applyFormList,"排休",u.getJobNumber());
|
|
|
|
+ strings.add(total14.toString());
|
|
|
|
+ BigDecimal total15 = getDecimal(applyFormList,"调休假",u.getJobNumber());
|
|
|
|
+ strings.add(total15.toString());
|
|
|
|
+ BigDecimal total16 = getDecimal(applyFormList,"年假",u.getJobNumber());
|
|
|
|
+ strings.add(total16.toString());
|
|
|
|
+ BigDecimal total17 = getDecimal(applyFormList,"出差",u.getJobNumber());
|
|
|
|
+ strings.add(total17.toString());
|
|
|
|
+ int countLess=0;
|
|
|
|
+ int countMore=0;
|
|
|
|
+ for (AttendanceStaff staff : staffCollect) {
|
|
|
|
+ Integer type = staff.getAttendanceType();
|
|
|
|
+ LocalDateTime clockStartTime = staff.getClockStartTime();
|
|
|
|
+ LocalDateTime clockEndTime = staff.getClockEndTime();
|
|
|
|
+ LocalTime startTime = clockStartTime.toLocalTime();
|
|
|
|
+ LocalTime endTime = clockEndTime.toLocalTime();
|
|
|
|
+
|
|
|
|
+ if (type == BAI_BAN) {
|
|
|
|
+ if (startTime.isAfter(LocalTime.of(8,0,0))) {
|
|
|
|
+ Duration duration = Duration.between(LocalTime.of(8,0,0),startTime );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (endTime.isBefore(LocalTime.of(17, 0, 0))) {
|
|
|
|
+ Duration duration = Duration.between(endTime,LocalTime.of(17,0,0) );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ else if (type == ZHONG_BAN) {
|
|
|
|
+ if (startTime.isAfter(LocalTime.of(13, 0, 0))) {
|
|
|
|
+ Duration duration = Duration.between(LocalTime.of(13,0,0),startTime);
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (endTime.isBefore(LocalTime.of(21, 0, 0))) {
|
|
|
|
+ Duration duration = Duration.between(endTime,LocalTime.of(21,0,0));
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (type == XIAO_YE_BAN) {
|
|
|
|
+ if (startTime.isAfter(LocalTime.of(16, 0, 0))) {
|
|
|
|
+
|
|
|
|
+ Duration duration = Duration.between(LocalTime.of(16,0,0),startTime );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (endTime.isBefore(LocalTime.of(23, 59, 59)) && endTime.isAfter(LocalTime.of(16, 0, 0))) {
|
|
|
|
+
|
|
|
|
+ Duration duration = Duration.between(endTime,LocalTime.of(23,59, 59) );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (type == XIAO_YE_BAN_2) {
|
|
|
|
+ if (startTime.isAfter(LocalTime.of(17, 0, 0))) {
|
|
|
|
+
|
|
|
|
+ Duration duration = Duration.between(LocalTime.of(17,0,0),startTime );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (endTime.isBefore(LocalTime.of(23, 59, 59)) && endTime.isAfter(LocalTime.of(17, 0, 0))) {
|
|
|
|
+ Duration duration = Duration.between(endTime,LocalTime.of(23,59, 59) );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }else if (endTime.isBefore(LocalTime.of(1, 0, 0))) {
|
|
|
|
+
|
|
|
|
+ Duration duration = Duration.between(endTime,LocalTime.of(1,0, 0) );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (type == DA_YE_BAN) {
|
|
|
|
+ if (startTime.isAfter(LocalTime.of(0, 0, 0))&&startTime.isBefore(LocalTime.of(4, 0, 0))) {
|
|
|
|
+ Duration duration = Duration.between(LocalTime.of(0, 0, 0),startTime );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (endTime.isBefore(LocalTime.of(8, 0, 0))) {
|
|
|
|
+
|
|
|
|
+ Duration duration = Duration.between(endTime,LocalTime.of(8,0, 0) );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (type==BAI_BAN_YI_CHANG_1) {
|
|
|
|
+ if (startTime.isAfter(LocalTime.of(9, 0, 0))) {
|
|
|
|
+ Duration duration = Duration.between(LocalTime.of(9,0,0),startTime );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (endTime.isBefore(LocalTime.of(18, 0, 0))) {
|
|
|
|
+ Duration duration = Duration.between(endTime,LocalTime.of(18,0,0) );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (type==BAI_BAN_YI_CHANG_2) {
|
|
|
|
+ if (startTime.isAfter(LocalTime.of(10, 0, 0))) {
|
|
|
|
+
|
|
|
|
+ Duration duration = Duration.between(LocalTime.of(10,0,0),startTime );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (endTime.isBefore(LocalTime.of(19, 0, 0))) {
|
|
|
|
+
|
|
|
|
+ Duration duration = Duration.between(endTime,LocalTime.of(19,0,0) );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (type == XIAO_YE_BAN_YI_CHANG_1) {
|
|
|
|
+ if (startTime.isAfter(LocalTime.of(14, 0, 0))) {
|
|
|
|
+ Duration duration = Duration.between(LocalTime.of(14,0,0),startTime );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (endTime.isBefore(LocalTime.of(22, 0, 0))) {
|
|
|
|
+ Duration duration = Duration.between(endTime,LocalTime.of(22,0, 0) );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (type == XIAO_YE_BAN_YI_CHANG_2) {
|
|
|
|
+ if (startTime.isAfter(LocalTime.of(15, 0, 0))) {
|
|
|
|
+ Duration duration = Duration.between(LocalTime.of(15,0,0),startTime );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (endTime.isBefore(LocalTime.of(23, 0, 0))&&endTime.isAfter(LocalTime.of(15,0,0))) {
|
|
|
|
+ Duration duration = Duration.between(endTime,LocalTime.of(23,0, 0) );
|
|
|
|
+ long v = duration.toMinutes();
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }else if (!endTime.isBefore(LocalTime.of(23, 0, 0))&&!endTime.isAfter(LocalTime.of(23,59,59))) {
|
|
|
|
+ double v = calculateOvertimeHours(LocalTime.of(23, 0, 0), endTime);
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ double v = calculateOvertimeHours(LocalTime.of(23, 0, 0), endTime);
|
|
|
|
+ if (v>30) {
|
|
|
|
+ countMore++;
|
|
|
|
+ }else {
|
|
|
|
+ countLess++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ strings.add(countLess+"");
|
|
|
|
+ strings.add(countMore+"");
|
|
|
|
+ List<ApplyForm> unDaKacollect = applyFormList.stream().filter(a -> a.getType() == 6&&a.getApplyId().equals(u.getJobNumber())).collect(Collectors.toList());
|
|
|
|
+ strings.add(unDaKacollect.size()+"");
|
|
|
|
+
|
|
|
|
+ int xiaoyeCount = (int) staffCollect.stream().filter(s -> s.getAttendanceTypeName().equals("小夜班")).count();
|
|
|
|
+ int dayeCount = (int) staffCollect.stream().filter(s -> s.getAttendanceTypeName().equals("大夜班")).count();
|
|
|
|
+ int size = staffCollect.size();
|
|
|
|
+ int other=size-xiaoyeCount-dayeCount;
|
|
|
|
+ strings.add(other+"");//其他
|
|
|
|
+ strings.add(xiaoyeCount+"");//小夜班
|
|
|
|
+ strings.add(dayeCount+"");//大夜班
|
|
|
|
+
|
|
|
|
+ BigDecimal zhouMoSum = applyFormList.stream().filter(a -> a.getApplyId().equals(u.getJobNumber())
|
|
|
|
+ && (a.getStartTime().toLocalDate().getDayOfWeek() == DayOfWeek.SATURDAY || a.getStartTime().toLocalDate().getDayOfWeek() == DayOfWeek.SUNDAY))
|
|
|
|
+ .map(ApplyForm::getSumTime)
|
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
|
+ BigDecimal faDingSum = applyFormList.stream().filter(a -> a.getApplyId().equals(u.getJobNumber())
|
|
|
|
+ && holidays.contains(a.getStartTime().toLocalDate().format(formatter2)))
|
|
|
|
+ .map(ApplyForm::getSumTime)
|
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
|
+ BigDecimal total = applyFormList.stream().filter(a -> a.getApplyId().equals(u.getJobNumber())).map(ApplyForm::getSumTime)
|
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
|
+ BigDecimal qiTaSum=total.subtract(faDingSum).subtract(zhouMoSum);
|
|
|
|
+ strings.add(zhouMoSum.toString());
|
|
|
|
+ strings.add(faDingSum.toString());
|
|
|
|
+ strings.add(qiTaSum.toString());
|
|
|
|
+
|
|
|
|
+ list.add(strings);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ msg.data= ExcelUtil.exportAttendanceList(title, firstDay, lastDay, monthInfo.get("workDays").toString(), list, path);
|
|
|
|
+ return msg;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static BigDecimal getDecimal(List<ApplyForm> applyFormList,String billName,String jobNum) {
|
|
|
|
+ return applyFormList.stream().filter(a -> a.getType() == 3
|
|
|
|
+ && a.getApplyBillName().equals(billName)
|
|
|
|
+ &&a.getApplyId().equals(jobNum)).map(ApplyForm::getSumTime).reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
|
+ }
|
|
|
|
+
|
|
private static String getDayWeekName(int value) {
|
|
private static String getDayWeekName(int value) {
|
|
String dayWeekName="";
|
|
String dayWeekName="";
|
|
switch (value) {
|
|
switch (value) {
|
|
@@ -309,6 +660,7 @@ public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attenda
|
|
private List<LocalDate> getCustomHolidays(YearMonth yearMonth) {
|
|
private List<LocalDate> getCustomHolidays(YearMonth yearMonth) {
|
|
List<LocalDate> holidays = new ArrayList<>();
|
|
List<LocalDate> holidays = new ArrayList<>();
|
|
|
|
|
|
|
|
+ //todo 手动去维护或者数据库手动维护
|
|
// 2025年中国法定节假日列表
|
|
// 2025年中国法定节假日列表
|
|
String[] holidayDates = {
|
|
String[] holidayDates = {
|
|
"2025-01-01", // 元旦
|
|
"2025-01-01", // 元旦
|
|
@@ -329,4 +681,60 @@ public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attenda
|
|
}
|
|
}
|
|
return holidays;
|
|
return holidays;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取月份信息
|
|
|
|
+ * @param yearMonthStr 格式 "yyyy-MM"(如 "2023-05")
|
|
|
|
+ * @param holidays 法定节假日列表(如 ["2023-05-01", "2023-05-02"])
|
|
|
|
+ * @return Map<String, Object> 包含月份名称、第一天、最后一天、工作日天数
|
|
|
|
+ */
|
|
|
|
+ public static Map<String, Object> getMonthInfo(String yearMonthStr, List<String> holidays) {
|
|
|
|
+ // 解析输入
|
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
|
|
|
|
+ YearMonth yearMonth = YearMonth.parse(yearMonthStr, formatter);
|
|
|
|
+
|
|
|
|
+ // 1. 月份名称(本地化)
|
|
|
|
+ String monthName = yearMonth.getMonth().getDisplayName(TextStyle.FULL, Locale.CHINA);
|
|
|
|
+
|
|
|
|
+ // 2. 月份的第一天
|
|
|
|
+ LocalDate firstDay = yearMonth.atDay(1);
|
|
|
|
+
|
|
|
|
+ // 3. 月份的最后一天
|
|
|
|
+ LocalDate lastDay = yearMonth.atEndOfMonth();
|
|
|
|
+
|
|
|
|
+ // 4. 计算工作日天数(排除周末和法定节假日)
|
|
|
|
+ long workDays = calculateWorkDays(yearMonth, holidays);
|
|
|
|
+
|
|
|
|
+ // 返回结果
|
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
|
+ result.put("monthName", monthName); // 月份名称(如 "五月")
|
|
|
|
+ result.put("firstDay", firstDay); // 月份第一天(LocalDate)
|
|
|
|
+ result.put("lastDay", lastDay); // 月份最后一天(LocalDate)
|
|
|
|
+ result.put("workDays", workDays); // 工作日天数(long)
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 计算某个月的工作日天数(排除周末和法定节假日)
|
|
|
|
+ */
|
|
|
|
+ private static long calculateWorkDays(YearMonth yearMonth, List<String> holidays) {
|
|
|
|
+ // 获取月份的所有日期
|
|
|
|
+ List<LocalDate> datesInMonth = IntStream.rangeClosed(1, yearMonth.lengthOfMonth())
|
|
|
|
+ .mapToObj(day -> yearMonth.atDay(day))
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
+
|
|
|
|
+ // 过滤:排除周末 + 法定节假日
|
|
|
|
+ return datesInMonth.stream()
|
|
|
|
+ .filter(date -> {
|
|
|
|
+ // 1. 排除周六、周日
|
|
|
|
+ DayOfWeek dayOfWeek = date.getDayOfWeek();
|
|
|
|
+ if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ // 2. 排除法定节假日
|
|
|
|
+ String dateStr = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
|
|
|
|
+ return !holidays.contains(dateStr);
|
|
|
|
+ })
|
|
|
|
+ .count();
|
|
|
|
+ }
|
|
}
|
|
}
|