Browse Source

哈克同步, 已部署

pruple_boy 1 month ago
parent
commit
bdc04039e3

+ 10 - 3
mjava-hake/src/main/java/com/malk/hake/controller/HKController.java

@@ -11,6 +11,7 @@ import com.malk.server.common.McREnum;
 import com.malk.service.aliwork.YDClient;
 import com.malk.service.aliwork.YDService;
 import com.malk.service.dingtalk.DDClient_Event;
+import com.malk.utils.UtilDateTime;
 import com.malk.utils.UtilMap;
 import com.malk.utils.UtilServlet;
 import lombok.SneakyThrows;
@@ -20,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -130,14 +132,17 @@ public class HKController {
     }
 
     /**
-     * 增量补卡数据同步
+     * 数据初始化 [临时]
      */
-    @PostMapping("attendance/reissue")
+    @PostMapping("attendance/correction")
     McR reissue(HttpServletRequest request) {
-        
+//        hkClient._correctionAttendance();
         return McR.success();
     }
 
+
+    ////////////////////////////////////////////////  TEST  ////////////////////////////////////////////////
+
     @Autowired
     private YDClient ydClient;
 
@@ -157,6 +162,8 @@ public class HKController {
     @GetMapping("test")
     McR test() {
 
+        log.info("测试, {}", UtilDateTime.formatDate(new Date(new Date().getTime() - 24 * 60 * 60 * 1000)));
+
 //        List<Map> dataList = ydService.queryFormData_all(YDParam.builder()
 //                .formUuid("FORM-96C8BF4B18044DF783ADFE1CF7931FE9P4BW")
 //                .build());

+ 13 - 11
mjava-hake/src/main/java/com/malk/hake/schedule/HKScheduleTask.java

@@ -1,6 +1,7 @@
 package com.malk.hake.schedule;
 
 import com.malk.hake.service.HKClient;
+import com.malk.utils.UtilDateTime;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -8,6 +9,8 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.Scheduled;
 
+import java.util.Date;
+
 /**
  * @EnableScheduling 开启定时任务 [配置参考McScheduleTask]
  */
@@ -69,20 +72,19 @@ public class HKScheduleTask {
         }
     }
 
-//    /**
-//     * 清理打卡标记位: 每天早晨7点
-//     */
-//    @Scheduled(cron = "0 0 7 * * ? ")
-//    public void timer_5() {
-//        hkClient.clearClockStatus();
-//    }
-
     /**
-     * 全量同步考勤
+     * 清理打卡标记位: 每天早晨7点 [废弃, 通过全量同步处理]
      */
     @Scheduled(cron = "0 0 7 * * ? ")
     public void timer_5() {
-        // 同步前一日全量考勤数据
-//         hkClient.syncAttendance();
+        //hkClient.clearClockStatus();
+    }
+
+    /**
+     * 同步前一日全量考勤数据
+     */
+    @Scheduled(cron = "0 50 7 * * ? ")
+    public void timer_6() {
+        hkClient.syncAttendance(UtilDateTime.formatDate(new Date(new Date().getTime() - 24 * 60 * 60 * 1000)), null);
     }
 }

+ 8 - 1
mjava-hake/src/main/java/com/malk/hake/service/HKClient.java

@@ -32,12 +32,19 @@ public interface HKClient {
     void callbackAttendance(Map data);
 
     /**
-     * 清理标记位
+     * 清理标记位 [废弃, 通过全量同步处理]
      */
+    @Deprecated
     void clearClockStatus();
 
     /**
      * 全量同步考勤
      */
     void syncAttendance(String tDate, List<String> userIds);
+
+    /**
+     * prd 数据初始化, 修正统一下班状态 [ tmp ]
+     */
+    @Deprecated
+    void _correctionAttendance();
 }

+ 56 - 3
mjava-hake/src/main/java/com/malk/hake/service/impl/HKImplClient.java

@@ -387,6 +387,10 @@ public class HKImplClient implements HKClient {
     private void _syncCheckingRecord(boolean isIn, String time, String employeeId) {
         String type = isIn ? "ClockIn" : "ClockOut";
         String url = _getMonitorApi("/api/v1/TimeRecording/RecordingDays/" + type);
+        // 若不传递 TimeStamp, 则会以当前时间
+        if (StringUtils.isBlank(time)) {
+            time = UtilDateTime.formatDateTime(new Date());
+        }
         Map param = UtilMap.map("EmployeeId, TimeStamp", employeeId, time);
         okRequest(url, _getAuthSessionId(), param);
     }
@@ -407,7 +411,7 @@ public class HKImplClient implements HKClient {
     private Map<String, Boolean> checkStatus = new HashMap();
 
     /**
-     * prd monitor clockIn, clockOut 必须成对出现, 且后一次时间必须在前一次时间之后, 否则均同步异常;
+     * prd monitor clockIn, clockOut 必须成对出现, 且后一次时间必须在前一次时间之后, 否则均同步异常; ppExt: prd 25-05-06 考勤方案修正为全量, 确保上下班正常
      * [方案: in/out 若是同一个时间可以反复调用]
      * 1. 钉钉已设置为严格打卡模式, 一次上班卡\一次下班卡, 成对出现, 不取值最早和最晚时间
      * 2. 每天早上7点重置打卡标记状态, 避免通过标记第一次打卡就是 clock-out 情况, 导致后续与实际考勤一直异常
@@ -434,7 +438,7 @@ public class HKImplClient implements HKClient {
             _syncCheckingRecord(isIn, timeStamp, employeeId);
         } catch (Exception e) {
             // fixme:  因 monitor 写入要求, 重复下班 expected Out got In 报错; 重复上班 expected In got Out 报错
-            if (e.getMessage().contains("expected In got Out") || e.getMessage().contains("expected In got Out")) {
+            if (e.getMessage().contains("expected In got Out") || e.getMessage().contains("expected Out got In")) {
                 String tTime = UtilDateTime.formatDate(new Date(UtilMap.getLong(data, "checkTime") - 24 * 60 * 60 * 1000));
                 if (e.getMessage().contains("expected In got Out")) {
                     tTime = tTime + " 23:59:58";
@@ -470,7 +474,6 @@ public class HKImplClient implements HKClient {
             userIds = syncContact(true);
         }
         List<Map> reflects = getUserIdReflect(); // 钉钉Id -> monitorId
-//        List<Map> reflects = Arrays.asList(UtilMap.map("textField_m4sjp8pf, textField_m4sjp8pg", "131022580326061279, 1082858538248001906"));
         log.info("考勤同步信息, 人员数量 = {}, id映射表数据 = {}", userIds.size(), reflects.size());
 
         for (String userId : userIds) {
@@ -481,10 +484,27 @@ public class HKImplClient implements HKClient {
                 continue;
             }
             String employeeId = UtilMap.getString((Map) optional.get(), "textField_m4sjp8pg");
+            if (employeeId.contains("_")) {
+                employeeId = employeeId.split("_")[1];
+            }
             Map record = ddClient_attendance.getAttendanceRecord(ddClient.getAccessToken(), userId, tDate);
             List<Map> checks = UtilMap.getList(record, "attendance_result_list");
+            // 缺卡处理逻辑
+            if (checks.size() == 1) {
+                Map tmp = UtilMap.empty();
+                long tms = UtilDateTime.parseDateTime(UtilMap.getString(checks.get(0), "user_check_time")).getTime();
+                if ("OffDuty".equals(UtilMap.getString(checks.get(0), "check_type"))) {
+                    tmp.put("check_type", "OnDuty");
+                    tmp.put("user_check_time", UtilDateTime.formatDateTime(new Date(tms - 1000L)));
+                } else {
+                    tmp.put("check_type", "OffDuty");
+                    tmp.put("user_check_time", UtilDateTime.formatDateTime(new Date(tms + 1000L)));
+                }
+                checks.add(tmp);
+            }
             checks.sort(Comparator.comparingLong(item -> UtilDateTime.parseDateTime(UtilMap.getString(item, "user_check_time")).getTime())); // 排序
             log.info("考勤记录, {}, result = {}, record = {}", UtilMap.getString((Map) optional.get(), "employeeField_lo47byyj"), checks.size(), UtilMap.getList(record, "check_record_list").size());
+
             for (Map check : checks) {
                 boolean isIn = !"OffDuty".equals(UtilMap.getString(check, "check_type"));
                 try {
@@ -508,5 +528,38 @@ public class HKImplClient implements HKClient {
                 .formDataJson(JSON.toJSONString(formData))
                 .build(), YDConf.FORM_OPERATION.create);
     }
+
+    /**
+     * prd 数据修正 2025-05-06
+     * - fixme: 存在钉钉考勤回调历史原因, 若已有当前时间打卡, 则之前时间数据无法写入. 更新当前生产环境所有人考勤下班时间为 17:00
+     * - ppExt: 若 报错为 Recording template is not valid: FutureRecordingsDetected, 则存在有记录已大于当前传递考勤时间
+     */
+    @Override
+    public void _correctionAttendance() {
+
+        List<String> userIds = syncContact(true);
+        List<Map> reflects = getUserIdReflect(); // 钉钉Id -> monitorId
+        log.info("考勤同步信息, 人员数量 = {}, id映射表数据 = {}", userIds.size(), reflects.size());
+
+        for (String userId : userIds) {
+            Optional optional = reflects.stream().filter(item -> userId.equals(UtilMap.getString(item, "textField_m4sjp8pf"))).findAny();
+            if (!optional.isPresent()) {
+                Map formData = UtilMap.map("dateField_lo47byyd, employeeField_lo47byyj, textareaField_lo47byyo, textareaField_lo47sanj", new Date().getTime(), userId, "monitor id 未匹配, 请管理员维护", "");
+                _attendanceExceptionRecord(formData);
+                continue;
+            }
+            String employeeId = UtilMap.getString((Map) optional.get(), "textField_m4sjp8pg");
+            if (employeeId.contains("_")) {
+                employeeId = employeeId.split("_")[1];
+            }
+            try {
+                _syncCheckingRecord(false, "2025-05-06 17:00:00", employeeId);
+            } catch (McException e) {
+                if (!(e.getMessage().contains("expected In got Out") || e.getMessage().contains("expected Out got In"))) {
+                    log.error(UtilMap.getString((Map) optional.get(), "employeeField_lo47byyj"), e.getMessage(), e);
+                }
+            }
+        }
+    }
 }