Переглянути джерело

[task-5459] 考勤重构:
休息天数:当天不存在正常出勤的班次才能统计为休息
应出勤天数 : ban_ci_ming_不等于休息的天数,当天存在多个班次一个班次为休息一个正常出勤应该统计为应出勤
实际出勤天数 :有打卡记录的统计为出勤,当天只要一个班次实际打卡就算实际出勤
正常天数 :统计日和之前的非休息日考勤状态为正常的天数
异常天数 : 统计日和之前的非休息日考勤状态为异常的统计(不统计旷工的)
异常次数合计 :每条记录的异常都要统计相加,只统计统计日和之前的非休息日异常数据(不统计旷工)
迟到次数: 每条记录的异常次数统计为迟到次数,只统计统计日和之前的非休息日异常数据
旷工次数 统计日之前的没有打卡记录的统计为旷工

xiexh 8 місяців тому
батько
коміт
c29f4e50da

+ 267 - 9
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/components/sqlzdy/Service/SwdlServiceImpl.java

@@ -18,7 +18,6 @@ import com.lc.ibps.cloud.provider.GenericProvider;
 import com.lc.ibps.org.api.IPartyPositionService;
 import com.lc.ibps.org.party.persistence.entity.PartyPositionPo;
 import com.lc.ibps.sysdata.dao.UpdateDataTableDao;
-import com.lc.ibps.untils.JsonUtil;
 import io.swagger.annotations.ApiParam;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -26,10 +25,10 @@ import org.springframework.stereotype.Service;
 import org.springframework.web.bind.annotation.RequestBody;
 
 import javax.annotation.Resource;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.time.LocalDate;
+import java.util.stream.Collectors;
 
 /**
  * @title: xiexh
@@ -38,6 +37,10 @@ import java.util.Map;
 @Service
 @Slf4j
 public class SwdlServiceImpl extends GenericProvider implements SwdlService {
+    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+    private static final String REST_SHIFT = "休息";
+    private static final String NORMAL_STATUS = "正常";
+    private static final String ABNORMAL_STATUS = "异常";
 
     @Autowired
     UpdateDataTableDao  updateDataTableDao;
@@ -189,7 +192,6 @@ public class SwdlServiceImpl extends GenericProvider implements SwdlService {
             int limit = Integer.parseInt(map.get("limit").toString());
             int startPage = limit*(pageNo-1);
             map.put("startPage",startPage);
-
             Map paramMap= getAttendanceMap(map);
             List<Map<String,Object>> list = updateDataTableDao.selectAttendanceData(paramMap);
             int totalCount = updateDataTableDao.selectAttendanceDataCount(paramMap);
@@ -218,10 +220,17 @@ public class SwdlServiceImpl extends GenericProvider implements SwdlService {
 
             Map paramMap= getAttendanceMap(map);
             List<Map<String,Object>> list = updateDataTableDao.selectAttendance2Data(paramMap);
-            int totalCount = updateDataTableDao.selectAttendance2DataCount(paramMap);
-            APIPageList<Map<String,Object>> pageList = getAPIPageList(list);
+           // int totalCount = updateDataTableDao.selectAttendance2DataCount(paramMap);
+            List<Map<String, Object>> data = calculateAttendance(list);
+            data = data.stream()
+                    .sorted(Comparator.comparing(map2 -> (String) map2.get("user_id_")))
+                    .collect(Collectors.toList());
+            //分页
+            int toIndex = Math.min(startPage + limit, data.size());
+            List<Map<String, Object>> pageData = getPageList(data, startPage, toIndex);
+            APIPageList<Map<String,Object>> pageList = getAPIPageList(pageData);
             APIPageResult pageResult = new APIPageResult();
-            pageResult.setTotalCount(totalCount);
+            pageResult.setTotalCount(data.size());
             pageResult.setLimit(limit);
             pageResult.setPage(pageNo);
             pageList.setPageResult(pageResult);
@@ -231,6 +240,17 @@ public class SwdlServiceImpl extends GenericProvider implements SwdlService {
         }
         return result;
     }
+
+    private List<Map<String, Object>> getPageList(List<Map<String, Object>> data, int fromIndex, int toIndex) {
+        //防止越界(如 pageNo 太大)
+        if (fromIndex >= data.size()) {
+            fromIndex = 0;
+            toIndex = 0;
+        }
+        List<Map<String, Object>> pagedList = data.subList(fromIndex, toIndex);
+        return pagedList;
+    }
+
     @Override
     public APIResult queryClassAdjustmentList(
             @ApiParam(name = "request", value = "传入查询请求map对象", required = true)
@@ -641,4 +661,242 @@ public class SwdlServiceImpl extends GenericProvider implements SwdlService {
         }
         return stringObjectHashMap;
     }
+    public List<Map<String, Object>> calculateAttendance(List<Map<String, Object>> attendanceRecords) {
+        // 获取当前系统日期
+        String systemDate = LocalDate.now().format(DATE_FORMATTER);
+
+        // 按用户ID分组
+        Map<String, List<Map<String, Object>>> recordsByUser = new HashMap<>();
+        for (Map<String, Object> record : attendanceRecords) {
+            String userId = (String) record.get("user_id_");
+            recordsByUser.computeIfAbsent(userId, k -> new ArrayList<>()).add(record);
+        }
+
+        // 存储每个用户的统计结果(改为List)
+        List<Map<String, Object>> resultList = new ArrayList<>();
+
+        // 遍历每个用户的考勤记录
+        for (Map.Entry<String, List<Map<String, Object>>> userEntry : recordsByUser.entrySet()) {
+            String userId = userEntry.getKey();
+            List<Map<String, Object>> userRecords = userEntry.getValue();
+
+            // 初始化统计指标
+            Map<String, Object> userStats = initUserStats(userRecords);
+            userStats.put("user_id_", userId); // 添加用户ID到结果中
+
+            // 按日期分组
+            Map<String, List<Map<String, Object>>> recordsByDate = new HashMap<>();
+            for (Map<String, Object> record : userRecords) {
+                String date = (String) record.get("ri_qi_");
+                recordsByDate.computeIfAbsent(date, k -> new ArrayList<>()).add(record);
+            }
+
+            // 统计每个日期的数据
+            for (Map.Entry<String, List<Map<String, Object>>> dateEntry : recordsByDate.entrySet()) {//统计天数
+                String date = dateEntry.getKey();
+                List<Map<String, Object>> dateRecords = dateEntry.getValue();//某个日期下的多个班次数据,同时存在早班,晚班等
+
+                boolean isRestDay = false;
+                boolean hasAttendance = false;
+                boolean isNormalDay = true;
+                boolean isAbnormalDay = false;
+
+                boolean existAttendanceDay = false;// 非休息班次标记,当天只有有一个非休息班次就统计为true
+                // 比较当前记录日期与系统日期
+                int dateCompare = date.compareTo(systemDate);
+                //应出勤天数过滤,一天需要出勤多个班次只统计为一天
+                Map shouldAttendance = new HashMap<String,Object>();
+
+                for (Map<String, Object> record : dateRecords) {//统计次数放里面
+                    String shiftName = BeanUtils.isNotEmpty("ban_ci_ming_") ? (String) record.get("ban_ci_ming_") : "";
+                    String status1 = BeanUtils.isNotEmpty("zhuang_tai_1_") ? (String) record.get("zhuang_tai_1_") : "";
+                    String status2 = BeanUtils.isNotEmpty("zhuang_tai_2_") ? (String) record.get("zhuang_tai_2_") : "";
+                    String checkIn1 = BeanUtils.isNotEmpty("da_ka_shi_jian_1_") ? (String) record.get("da_ka_shi_jian_1_") : "";
+                    String checkIn2 = BeanUtils.isNotEmpty("da_ka_shi_jian_2_") ? (String) record.get("da_ka_shi_jian_2_") : "";
+                    String attendanceStatus = BeanUtils.isNotEmpty("kao_qin_zhuang_ta") ? (String) record.get("kao_qin_zhuang_ta") : "";
+                    String lateDuration ="";
+                    String shiftDuration = "";
+                    String workDuration ="";
+                    if(BeanUtils.isNotEmpty(record.get("chi_dao_shi_chang"))){
+                         lateDuration = record.get("chi_dao_shi_chang").toString();
+                    }
+                    if(BeanUtils.isNotEmpty(record.get("ban_ci_shi_chang_"))){
+                        shiftDuration = record.get("ban_ci_shi_chang_").toString();
+                    }
+                    if(BeanUtils.isNotEmpty(record.get("gong_zuo_shi_chan"))){
+                        workDuration = record.get("gong_zuo_shi_chan").toString();
+                    }
+
+                    // 1. 应出勤天数统计(多个班次只有一个不是休息就算应出勤)
+                    if (!REST_SHIFT.equals(shiftName) && !shouldAttendance.containsKey(date)) {
+                        increment(userStats, "ying_chu_qin_tian_shu", 1);
+                        shouldAttendance.put(date,1);
+                        existAttendanceDay = true;
+                    }
+
+                    // 2. 休息天数统计
+                    if (REST_SHIFT.equals(shiftName)) {
+                       // increment(userStats, "xiu_xi_tian_shu", 1);
+                        isRestDay = true;
+                        continue; // 休息日不参与其他统计
+                    }
+
+                    // 3. 实际出勤天数统计(有打卡记录)
+                    if ((checkIn1 != null && !checkIn1.isEmpty()) ||
+                            (checkIn2 != null && !checkIn2.isEmpty())) {
+                        hasAttendance = true;
+                    }
+
+                    // 4. 异常次数统计(不再把旷工统计到异常里面,只有异常统计为异常)
+                    if (dateCompare <= 0) { // 只统计今天及之前的记录
+                        if (dateCompare < 0) {
+                            // 历史日期
+                            //if (status1 == null || status1.isEmpty() || ABNORMAL_STATUS.equals(status1)) {
+                            if (ABNORMAL_STATUS.equals(status1)) {
+                                increment(userStats, "yi_chang_ci_shu", 1);
+                            }
+                            //if (status2 == null || status2.isEmpty() || ABNORMAL_STATUS.equals(status2)) {
+                            if (ABNORMAL_STATUS.equals(status2)) {
+                                increment(userStats, "yi_chang_ci_shu", 1);
+                            }
+                        } else {
+                            // 当天日期
+                            if (ABNORMAL_STATUS.equals(status1)) {
+                                increment(userStats, "yi_chang_ci_shu", 1);
+                            }
+                            if (ABNORMAL_STATUS.equals(status2)) {
+                                increment(userStats, "yi_chang_ci_shu", 1);
+                            }
+                        }
+                    }
+
+                    // 5. 迟到次数统计-上班异常算迟到,提前下班也算迟到(迟到/早退)
+                    if (ABNORMAL_STATUS.equals(status1)) {
+                        increment(userStats, "chi_dao_ci_shu", 1);
+                    }
+                    if (ABNORMAL_STATUS.equals(status2)) {
+                        increment(userStats, "chi_dao_ci_shu", 1);
+                    }
+
+                    // 6. 迟到时长统计
+                    if (lateDuration != null && !lateDuration.isEmpty()) {
+                        try {
+                            double duration = Double.parseDouble(lateDuration);
+                            addTo(userStats, "chi_dao_shi_chang", duration);
+                        } catch (NumberFormatException e) {
+                            // 忽略格式错误
+                        }
+                    }
+
+                    // 7. 旷工次数统计(只统计历史日期)
+                    if (dateCompare < 0) {
+                        if (checkIn1 == null || checkIn1.isEmpty()) {
+                            increment(userStats, "kuang_gong_ci_shu", 1);
+                        }
+                        if (checkIn2 == null || checkIn2.isEmpty()) {
+                            increment(userStats, "kuang_gong_ci_shu", 1);
+                        }
+                    }
+
+                    // 8. 班次时长统计
+                    if (shiftDuration != null && !shiftDuration.isEmpty()) {
+                        try {
+                            double duration = Double.parseDouble(shiftDuration);
+                            addTo(userStats, "ban_ci_shi_chang", duration);
+                        } catch (NumberFormatException e) {
+                            // 忽略格式错误
+                        }
+                    }
+
+                    // 9. 实际工作时长统计
+                    if (workDuration != null && !workDuration.isEmpty()) {
+                        try {
+                            double duration = Double.parseDouble(workDuration);
+                            addTo(userStats, "gong_zuo_shi_chang", duration);
+                        } catch (NumberFormatException e) {
+                            // 忽略格式错误
+                        }
+                    }
+
+                    // 10. 正常/异常天数判断
+                    if (dateCompare <= 0) { // 只统计今天及之前的记录
+                        if (dateCompare < 0) {
+                            // 历史日期
+                            if (!NORMAL_STATUS.equals(attendanceStatus)) {
+                                isNormalDay = false;
+                                isAbnormalDay = true;
+                            }
+                        } else {
+                            // 当天日期
+                            if (ABNORMAL_STATUS.equals(status1) || ABNORMAL_STATUS.equals(status2)) {
+                                isNormalDay = false;
+                                isAbnormalDay = true;
+                            }
+                        }
+                    }
+
+                }
+                //(当天不存在正常出勤的班次才能统计为休息)
+                if(!existAttendanceDay && isRestDay){
+                    isRestDay = true;
+                }else{
+                    isRestDay = false;
+                }
+
+                // 休息天数统计
+                if (isRestDay) {
+                    increment(userStats, "xiu_xi_tian_shu", 1);
+                    continue;
+                }
+                // 实际出勤天数统计(按日期)
+                if (!isRestDay && hasAttendance) {
+                    increment(userStats, "shi_ji_chu_qin_tian_shu", 1);
+                }
+
+                // 正常天数统计(按日期)
+                if (!isRestDay && isNormalDay && dateCompare <= 0) {
+                    increment(userStats, "zheng_chang_tian_shu", 1);
+                }
+
+                // 异常天数统计(按日期)
+                if (!isRestDay && isAbnormalDay && dateCompare <= 0) {
+                    increment(userStats, "yi_chang_tian_shu", 1);
+                }
+            }
+
+            // 将用户统计结果添加到List中
+            resultList.add(userStats);
+        }
+
+        return resultList;
+    }
+
+    private Map<String, Object> initUserStats(List<Map<String, Object>> userRecords) {
+        Map<String, Object> stats = new HashMap<>();
+        stats.put("yi_chang_ci_shu", 0);
+        stats.put("chi_dao_ci_shu", 0);
+        stats.put("chi_dao_shi_chang", 0.0);
+        stats.put("kuang_gong_ci_shu", 0);
+        stats.put("ban_ci_shi_chang", 0.0);
+        stats.put("gong_zuo_shi_chang", 0.0);
+        stats.put("ying_chu_qin_tian_shu", 0);
+        stats.put("shi_ji_chu_qin_tian_shu", 0);
+        stats.put("xiu_xi_tian_shu", 0);
+        stats.put("zheng_chang_tian_shu", 0);
+        stats.put("yi_chang_tian_shu", 0);
+        stats.put("user_name_",userRecords.get(0).get("user_name_"));
+        stats.put("pos_name_",userRecords.get(0).get("pos_name_"));
+        stats.put("gong_hao_",userRecords.get(0).get("gong_hao_"));
+        return stats;
+    }
+
+    private void increment(Map<String, Object> stats, String key, int amount) {
+        int current = (int) stats.getOrDefault(key, 0);
+        stats.put(key, current + amount);
+    }
+
+    private void addTo(Map<String, Object> stats, String key, double amount) {
+        double current = (double) stats.getOrDefault(key, 0.0);
+        stats.put(key, current + amount);
+    }
 }

+ 1 - 43
ibps-provider-root/modules/provider-business/src/main/resources/com/lc/ibps/klimsibps/mapping/UpdateDataTableMapper.xml

@@ -761,9 +761,7 @@
     </select>
     <select id="selectAttendance2Data" resultType="java.util.Map" parameterType="java.util.Map">
         SELECT
-        (
-        SELECT
-        COUNT( DISTINCT USER_ID_ )
+          *
         FROM
         v_attendance_statistics
         <where>
@@ -784,48 +782,8 @@
                 AND gong_hao_ = #{map.gongHao}
             </if>
         </where>
-        ) AS total_count,
-        USER_ID_,
-        ri_qi_,
-        user_name_,
-        pos_name_,
-        gong_hao_,
-        COUNT( ri_qi_ ) AS ying_chu_qin_shu,
-        SUM( CASE WHEN ( da_ka_shi_jian_1_ IS NULL OR da_ka_shi_jian_1_ = '' ) AND ( da_ka_shi_jian_2_ IS NULL OR da_ka_shi_jian_2_ = '' ) THEN 1 ELSE 0 END ) AS xiu_xi_shu,
-        COUNT( ri_qi_ ) - SUM( CASE WHEN ( da_ka_shi_jian_1_ IS NULL OR da_ka_shi_jian_1_ = '' ) AND ( da_ka_shi_jian_2_ IS NULL OR da_ka_shi_jian_2_ = '' ) THEN 1 ELSE 0 END ) AS shi_ji_chu_qin_shu,
-        SUM( kao_qin_zhuang_ta ) AS zheng_chang_shu,
-        SUM( CASE WHEN kao_qin_zhuang_ta = '异常' OR kao_qin_zhuang_ta IS NULL OR kao_qin_zhuang_ta = '' THEN 1 ELSE 0 END ) AS yi_chang_shu,
-        ROUND( SUM( ban_ci_shi_chang_ ) / 60.0, 2 ) AS total_ban_ci_shi_chang,
-        ROUND( SUM( gong_zuo_shi_chan ) / 60.0, 2 ) AS total_gong_zuo_shi_chan,
-        SUM( CASE WHEN zhuang_tai_1_ = '异常' OR zhuang_tai_2_ = '异常' THEN 1 ELSE 0 END ) AS chi_dao_ci_shu,
-        SUM( chi_dao_shi_chang ) AS total_chi_dao_shi_chang,
-        SUM( CASE WHEN zhuang_tai_1_ IS NULL OR zhuang_tai_1_ = '' THEN 1 ELSE 0 END ) + SUM( CASE WHEN zhuang_tai_2_ IS NULL OR zhuang_tai_2_ = '' THEN 1 ELSE 0 END ) AS kuang_gong_ci_shu,
-        SUM( CASE WHEN zhuang_tai_1_ = '异常' THEN 1 ELSE 0 END ) + SUM( CASE WHEN zhuang_tai_2_ = '异常' THEN 1 ELSE 0 END ) + SUM( CASE WHEN zhuang_tai_1_ IS NULL OR zhuang_tai_1_ = '' THEN 1 ELSE 0 END ) + SUM( CASE WHEN zhuang_tai_2_ IS NULL OR zhuang_tai_2_ = '' THEN 1 ELSE 0 END ) AS yi_chang_he_ji
-        FROM
-        v_attendance_statistics
-        <where>
-            di_dian_ = #{map.locationId}
-            <if test="@o.Ognl@isNotEmpty(map.buMen)">
-                AND bu_men_ like CONCAT('%', #{map.buMen}, '%')
-            </if>
-            <if test="@o.Ognl@isNotEmpty(map.startDate)">
-                AND ri_qi_ >= #{map.startDate}
-            </if>
-            <if test="@o.Ognl@isNotEmpty(map.endDate)">
-                AND ri_qi_ <![CDATA[ <= ]]> #{map.endDate}
-            </if>
-            <if test="@o.Ognl@isNotEmpty(map.yongHuId)">
-                AND FIND_IN_SET(yong_hu_id_, #{map.yongHuId})
-            </if>
-            <if test="@o.Ognl@isNotEmpty(map.gongHao)">
-                AND gong_hao_ = #{map.gongHao}
-            </if>
-        </where>
-        GROUP BY
-        USER_ID_
         ORDER BY
         USER_ID_
-        limit ${map.startPage},${map.limit}
     </select>
     <select id="selectAttendance2DataCount" resultType="java.lang.Integer" parameterType="java.util.Map">
         SELECT