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

1.单据同步钉钉相关插件

Tyx 1 тиждень тому
батько
коміт
2937a66933

+ 29 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/hrmp/hbpm/task/SyncUtil.java

@@ -507,4 +507,33 @@ public class SyncUtil {
         return partTimeMap;
     }
 
+    /**
+     * 自助用获取人员信息sql
+     * @param personId
+     * @return
+     */
+    public static StringBuilder getPersonSqlForHrobs (Long personId) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("/*dialect*/ select a.fid personId, \n");
+        sb.append("        to_char(b.fjoincomdate,'yyyy-MM-dd') joinDate, \n");
+        sb.append("        to_char(c.fstartdate,'yyyy-MM-dd') startDate, \n");
+        sb.append("        to_char(c.fenddate,'yyyy-MM-dd') endDate, \n");
+        sb.append("        e.fname jobLevel, \n");
+        sb.append("        wp.fname placeName \n");
+        sb.append(" from t_hrpi_person a \n");
+        sb.append(" left join t_hrpi_perserlen b on a.fid = b.fpersonid and b.fiscurrentversion = '1' and b.fbusinessstatus = '1' and b.fdatastatus = '1' \n");
+        sb.append(" left join ( select row_number() over (partition by fpersonid order by fmodifytime desc) r, fpersonid, fstartdate, fenddate \n");
+        sb.append("             from t_hlcm_contract where fiscurrentversion = '1' ) c on c.fpersonid = a.fid and c.r = 1 \n");
+        sb.append(" left join t_hrpi_empjobrel d on a.fid = d.fpersonid and d.fiscurrentversion = '1' and d.fbusinessstatus = '1' and d.fdatastatus = '1' \n");
+        sb.append(" left join t_hbjm_joblevel e on e.fjoblevelid = d.fjoblevelid \n");
+        sb.append(" left join t_hrpi_baselocation f on f.fpersonid = a.fid and f.fiscurrentversion = '1' and f.fbusinessstatus = '1' and f.fdatastatus = '1' \n");
+        sb.append(" left join t_hbss_workplace wp on wp.fid = f.flocationId \n");
+        sb.append(" where a.fiscurrentversion = '1'  \n");
+        sb.append("   and a.fdatastatus = '1' \n");
+        sb.append("   and a.fid = '"+personId+"' \n");
+        return sb;
+    }
+
+
+
 }

+ 72 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/hrmp/hrobs/formplugin/portal/pc/PcEmpInfoPluginEx.java

@@ -0,0 +1,72 @@
+package nckd.jimin.jyyy.hr.hrmp.hrobs.formplugin.portal.pc;
+
+import com.kingdee.util.StringUtils;
+import kd.bos.algo.DataSet;
+import kd.bos.algo.Row;
+import kd.bos.db.DB;
+import kd.bos.db.DBRoute;
+import kd.bos.entity.datamodel.IDataModel;
+import kd.bos.form.IFormView;
+import kd.bos.form.control.Label;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.hr.hbp.formplugin.web.HRDynamicFormBasePlugin;
+import kd.hrmp.hrobs.business.domain.service.portal.IEmpInfoService;
+import nckd.jimin.jyyy.hr.hrmp.hbpm.task.SyncUtil;
+
+import java.util.EventObject;
+import java.util.Map;
+
+/**
+ * description: 员工自助人员信息卡片扩展,携带入职日期,劳动合同开始/结束时间,职级,办公地
+ * @author Tyx
+ * @date  2025/6/3
+ */
+public class PcEmpInfoPluginEx extends HRDynamicFormBasePlugin {
+    private static final Log LOGGER = LogFactory.getLog(PcEmpInfoPluginEx.class);
+
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        super.afterCreateNewData(e);
+        Long startTime = System.currentTimeMillis();
+        this.initHeadCard();
+        Long endTime = System.currentTimeMillis();
+        LOGGER.info("PcEmpInfoPluginEx_initHeadCard total time : {}", endTime - startTime);
+    }
+
+    private void initHeadCard() {
+        IDataModel model = this.getModel();
+        IFormView view = this.getView();
+        // personInfo格式:{person=2199974856575944704, cmpemp=2199974861768493056, personindexid=2199974856575944704, mid=2199974856584332288, employee=2199974856584332288, user=2199974880760301568}
+        Map<String, Long> personInfo = IEmpInfoService.getInstance().getPersonModelId();
+        Long personId = personInfo.get("person");
+        //获取入职日期,劳动合同签订开始/结束时间,职级,办公地
+        StringBuilder sb = SyncUtil.getPersonSqlForHrobs(personId);
+        DataSet dataSet = DB.queryDataSet(this.getClass().getName(), DBRoute.of("hr"), sb.toString());
+        while(dataSet.hasNext()) {
+            Label lblJoinDate = (Label)this.getView().getControl("nckd_joindate");
+            Label lblPeriod = (Label)this.getView().getControl("nckd_contractperiod");
+            Label lblJobGrade = (Label)this.getView().getControl("nckd_jobgrade");
+            Label lblPlace = (Label)this.getView().getControl("nckd_place");
+            Row rowData = dataSet.next();
+            lblJoinDate.setText(rowData.getString("joinDate"));
+            lblJobGrade.setText(rowData.getString("jobLevel"));
+            lblPlace.setText(rowData.getString("placeName"));
+
+            String startDate = rowData.getString("startDate");
+            String endDate = rowData.getString("endDate");
+
+            //如果结束时间为空 则是无期限
+            if(!StringUtils.isEmpty(startDate) && StringUtils.isEmpty(endDate)) {
+                lblPeriod.setText(startDate + " ~ 无期限");
+            }
+            else if(StringUtils.isEmpty(startDate) && StringUtils.isEmpty(endDate)) {
+                lblPeriod.setText("未签订");
+            }
+            else if(!StringUtils.isEmpty(startDate) && !StringUtils.isEmpty(endDate)) {
+                lblPeriod.setText(startDate + " ~ " + endDate);
+            }
+        }
+    }
+}

+ 5 - 5
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/tsrsc/plugin/operate/CasRecrApplyUnAuditValidator.java

@@ -48,11 +48,11 @@ public class CasRecrApplyUnAuditValidator extends AbstractOperationServicePlugIn
                                     return String.valueOf(entry.getPkValue());
                                 })
                                 .collect(Collectors.toList());
-//                        DynamicObject[] load = BusinessDataServiceHelper.load("tstpm_srscarfmrsm", "id", (new QFilter("nckd_mokahcnum", QCP.in, objectList)).toArray());
-//                        if(load.length > 0){
-//                            this.addErrorMessage(dataEntity, "已存在候选人信息,不允许反审核!");
-//                            break;
-//                        }
+                        DynamicObject[] load = BusinessDataServiceHelper.load("tstpm_srscarfmrsm", "id", (new QFilter("nckd_mokahcnum", QCP.in, objectList)).toArray());
+                        if(load.length > 0){
+                            this.addErrorMessage(dataEntity, "已存在候选人信息,不允许反审核!");
+                            break;
+                        }
                     }
                 }
             }

+ 64 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/wtc/wtis/opplugin/CommonSyncDingTalkOpPlugin.java

@@ -0,0 +1,64 @@
+package nckd.jimin.jyyy.hr.wtc.wtis.opplugin;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.args.AfterOperationArgs;
+import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.sdk.plugin.Plugin;
+import nckd.jimin.jyyy.hr.wtc.wtis.util.DingTalkSyncUtil;
+import nckd.jimin.jyyy.hr.wtc.wtis.util.SyncAttendanceHelper;
+
+/**
+ * @description: 假勤类单据通用调用钉钉接口插件,包含:出差,休假,补签
+ * @author Tyx
+ * @date  2025/6/3
+ */
+public class CommonSyncDingTalkOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
+
+    private static Log log = LogFactory.getLog(CommonSyncDingTalkOpPlugin.class);
+
+
+    @Override
+    public void endOperationTransaction(EndOperationTransactionArgs e) {
+        super.endOperationTransaction(e);
+        DynamicObject[] bills = e.getDataEntities();
+        for(DynamicObject bill : bills) {
+            //SyncAttendanceHelper.execute(bill, "");
+            //1:加班,2:出差,3:请假,4:补签
+            String entityNumber = bill.getDataEntityType().getName();
+            //根据entityNumber去commonparams里面找
+            String bizType = DingTalkSyncUtil.getParamKey(entityNumber);
+            log.info("{}准备执行同步钉钉,单据编号:{}, bizType: {}", entityNumber, bill.getString("billno"), bizType);
+            //出差跟请假 这个参数没用 补签情况下,如果是变更就传true,
+            if(entityNumber.equals("wtpm_supsignselfchange") || entityNumber.equals("wtpm_supsignpcchange")) {
+                SyncAttendanceHelper.execute(bill, entityNumber, bizType, true);
+            }
+            else {
+                SyncAttendanceHelper.execute(bill, entityNumber, bizType, false);
+            }
+        }
+    }
+
+    @Override
+    public void afterExecuteOperationTransaction(AfterOperationArgs e) {
+        super.afterExecuteOperationTransaction(e);
+//        DynamicObject[] bills = e.getDataEntities();
+//        for(DynamicObject bill : bills) {
+//            //SyncAttendanceHelper.execute(bill, "");
+//            //1:加班,2:出差,3:请假,4:补签
+//            String entityNumber = bill.getDataEntityType().getName();
+//            //根据entityNumber去commonparams里面找
+//            String bizType = DingTalkSyncUtil.getParamKey(entityNumber);
+//            log.info("{}准备执行同步钉钉,单据编号:{}, bizType: {}", entityNumber, bill.getString("billno"), bizType);
+//            //出差跟请假 这个参数没用 补签情况下,如果是变更就传true,
+//            if(entityNumber.equals("wtpm_supsignpcchange") || entityNumber.equals("wtpm_supsignpcchange")) {
+//                SyncAttendanceHelper.execute(bill, entityNumber, bizType, true);
+//            }
+//            else {
+//                SyncAttendanceHelper.execute(bill, entityNumber, bizType, false);
+//            }
+//        }
+    }
+}

+ 1 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/wtc/wtis/task/SyncPunchCardTask.java

@@ -102,6 +102,7 @@ public class SyncPunchCardTask extends AbstractTask implements Plugin {
             JSONObject response = DingTalkSyncUtil.doPostByHttpClient(url, param);
             logger.info("分页调用钉钉接口返回信息,第{}页:{}", String.valueOf(i), response);
             if(response.getInteger("errcode") == 0 ) {
+                DingTalkSyncUtil.createLog(DingTalkSyncUtil.v_success, param.toJSONString(), response.toJSONString(), DingTalkSyncUtil.SyncPunch);
                 SyncPunchCardHelper.savePunchCardData(response.getJSONArray("recordresult"), openMap);
             }
             else {

+ 71 - 1
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/wtc/wtis/util/DingTalkSyncUtil.java

@@ -17,6 +17,7 @@ import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
+import java.util.stream.Collectors;
 
 /**
  * 2025-05-22 Tyx
@@ -38,6 +39,11 @@ public class DingTalkSyncUtil {
     public static String v_partsuccess = "B";
     public static String v_error = "C";
     public static String SyncPunch = "打卡数据同步";
+    public static String SyncTrip = "出差数据同步";
+    public static String SyncVa = "休假数据同步";
+    public static String SyncFill = "补签数据同步";
+    public static String SyncShift = "班次数据同步";
+    public final static Map<String, String> typeMap = new HashMap<String, String>();
 
 
     /**
@@ -52,6 +58,18 @@ public class DingTalkSyncUtil {
         return bill.getString("nckd_entryentity.nckd_value");
     }
 
+    /**
+     * 获取参数名
+     * @param value
+     * @return
+     */
+    public static String getParamKey(String value) {
+        QFilter filter = new QFilter("number", QCP.equals, "DingTalk");
+        filter.and("nckd_entryentity.nckd_value",QCP.like, "%" + value + "%");
+        DynamicObject bill = QueryServiceHelper.queryOne("nckd_commonparams", "nckd_entryentity.nckd_key", new QFilter[]{filter});
+        return bill.getString("nckd_entryentity.nckd_key");
+    }
+
     /**
      * POST请求
      * @param url
@@ -116,11 +134,24 @@ public class DingTalkSyncUtil {
      */
     public static DynamicObjectCollection getMappingInfo() {
         QFilter filter = new QFilter("imtype.id", QCP.equals, 2L);
+        filter.and("user.isforbidden",QCP.equals,false);
         String selectFields = "user.number,openid";
         return QueryServiceHelper.query(mapping_entity, selectFields, new QFilter[]{filter});
     }
 
-
+    /**
+     * 用户集成数据转换成Map,key = 人员工号, value = openId
+     * @return
+     */
+    public static Map<String, String> convertMappingInfo() {
+        DynamicObjectCollection mappingCols = getMappingInfo();
+        Map<String, String> openMap = mappingCols.stream().collect(Collectors.toMap((dyx) -> {
+            return dyx.getString("user.number");
+        }, (dyx) -> {
+            return dyx.getString("openid");
+        }));
+        return openMap;
+    }
 
     /**
      * 创建同步日志
@@ -154,5 +185,44 @@ public class DingTalkSyncUtil {
         logger.info("-------- 保存日志 --------");
     }
 
+    /**
+     * 创建同步日志
+     * @param status 同步状态 A-成功 B-部分成功 C-失败
+     * @param request   请求报文
+     * @param response  返回报文
+     * @param syncType  同步类型
+     */
+    public static void createLog (String status, String billNo, String request, String response, String syncType) {
+        DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject(KEY_ENTITY_LOG);
+        String uuid = UUID.randomUUID().toString().replace("-", "");
+        dynamicObject.set("enable", "1");
+        dynamicObject.set("status", "C");
+        dynamicObject.set("number", uuid.substring(0,29));
+        dynamicObject.set("nckd_billno", billNo);
+        dynamicObject.set("nckd_status", status);
+        dynamicObject.set("nckd_synctype", syncType);
+        if (request.length() < 200) {
+            dynamicObject.set("nckd_request", request);
+        } else {
+            dynamicObject.set("nckd_request", request.substring(0, 200) + "...");
+        }
+        dynamicObject.set("nckd_request_tag", request);
+
+        if (response.length() < 200) {
+            dynamicObject.set("nckd_response", response);
+        } else {
+            dynamicObject.set("nckd_response", response.substring(0, 200) + "...");
+        }
+        dynamicObject.set("nckd_response_tag", response);
+        SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+        logger.info("-------- 保存日志 --------");
+    }
+
+    static {
+        typeMap.put("2",SyncTrip);
+        typeMap.put("3",SyncVa);
+        typeMap.put("4",SyncFill);
+    }
+
 
 }

+ 396 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/wtc/wtis/util/SyncAttendanceHelper.java

@@ -0,0 +1,396 @@
+package nckd.jimin.jyyy.hr.wtc.wtis.util;
+
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.wtc.wtbs.common.util.WTCDateUtils;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * description: 同步钉钉通用帮助类、包含休假、出差、补签
+ * 分三种情况:新增,变更,撤销
+ * 1、正常通知审批通过,单据审批通过后调用钉钉审批通过接口
+ * 2、星瀚单据变更后根据ApproveId调用钉钉接口更新,补卡没有变更
+ * 3、星瀚单据变更失效后,调用钉钉审批撤销接口,请假单判断是否失效标识:isnotleave,出差单判断是否失效标识:isnottrip,
+ * @author Tyx
+ * @date  2025/5/29
+ */
+public class SyncAttendanceHelper {
+
+    private static Log log = LogFactory.getLog(SyncAttendanceHelper.class);
+    private static String baseUrl = "";
+    private static String finishParamKey = "getFinishUrl";
+    private static String cancelParamKey = "getCancelUrl";
+    private static String fillParamKey = "getFillUrl";
+    private static String shiftParamKey = "getShiftUrl";
+    private static String billNo = "";
+    private static String accessToken = "";
+
+    /**
+     * 统一入口
+     * @param bill         单据Dyo
+     * @param entityNumber 单据标识
+     * @param bizType      类型 1:加班,2:出差,3:请假,4:补卡
+     * @param isChange     是否变更
+     */
+    public static void execute (DynamicObject bill, String entityNumber, String bizType, boolean isChange) {
+        billNo = bill.getString("billno");
+        //用户映射Map key = 人员工号, value = openId
+        Map<String, String> openMap = DingTalkSyncUtil.convertMappingInfo();
+        //获取accessToken
+        try {
+            //重新查一遍单据 避免部分属性没有
+            bill = BusinessDataServiceHelper.loadSingle(bill.getPkValue(), entityNumber);
+            //获取AccessToken
+            accessToken = DingTalkSyncUtil.getAccessToken();
+            log.info("钉钉单据同步实际调用accessToken : {}", accessToken);
+            //构建参数
+            JSONObject param = buildContent(bill, entityNumber, bizType, isChange, openMap);
+            log.info("钉钉单据同步实际调用入参报文 : {}", param.toJSONString());
+            //获取调用url
+            String url = getUrl(baseUrl, accessToken);
+            log.info("钉钉单据同步实际调用url : {}", url);
+            //实际调用
+            JSONObject reponseObject = doPost(url, param);
+            log.info("钉钉单据同步实际返回信息 : {}", reponseObject.toJSONString());
+            //保存日志
+            DingTalkSyncUtil.createLog("A", billNo, param.toJSONString(),reponseObject.toJSONString(),DingTalkSyncUtil.typeMap.get(bizType));
+        } catch (Exception e) {
+            DingTalkSyncUtil.createLog("C", billNo, bill.getString("billno") + "调用失败", e.getMessage(), DingTalkSyncUtil.typeMap.get(bizType));
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 实际调用
+     * @param url
+     * @param bodyData
+     * @return
+     * @throws IOException
+     */
+    public static JSONObject doPost (String url, JSONObject bodyData) throws IOException {
+        return DingTalkSyncUtil.doPostByHttpClient(url, bodyData);
+    }
+
+    /**
+     * 构建入参
+     * @param bill         单据Dyo
+     * @param entityNumber 单据标识
+     * @param bizType      类型 1:加班,2:出差,3:请假
+     * @param isChange     是否变更
+     * @return
+     */
+    public static JSONObject buildContent(DynamicObject bill, String entityNumber, String bizType, boolean isChange, Map openMap) throws IOException {
+        JSONObject param = new JSONObject();
+        switch(bizType) {
+            case "2" :
+                buildTripContent(param, bill, entityNumber, isChange, openMap);
+                break;
+            case "3" :
+                buildVAContent(param, bill, entityNumber, isChange, openMap);
+                break;
+            case "4" :
+                buildFillContent(param, bill, entityNumber, isChange, openMap);
+                break;
+        }
+        return param;
+    }
+
+    /**
+     * 构建出差入参
+     * @param param
+     * @param bill
+     * @param entityNumber
+     * @param isChange
+     * @param openMap
+     */
+    private static void buildTripContent(JSONObject param, DynamicObject bill, String entityNumber, boolean isChange, Map openMap) {
+        log.info("构建出差入参报文 start ");
+        boolean isCancel = bill.getBoolean("isnottrip");
+        getBaseUrl(isCancel, false);
+        //判断是否出差失效
+        if(isCancel) {
+            param.put("approve_id", bill.getLong("originalid"));
+            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
+        }
+        else {
+            param.put("biz_type", 2);
+            param.put("jump_url", "https://open.dingtalk.com/");
+            param.put("tag_name", "出差");
+            param.put("calculate_model", "1");
+            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
+            DynamicObject entry = bill.getDynamicObjectCollection("entryentity").get(0);
+            //sub_type 请假类型
+            param.put("sub_type", entry.getString("busitriptype.name"));
+            //approve_id 直接取原单ID
+            param.put("approve_id", bill.getLong("originalid"));
+            //分录单位 0-上半天 1-下半天 2-全天 3-时分
+            String startMethod = entry.getString("startmethod");
+            String endMethod = entry.getString("endmethod");
+            //duration_unit 单位 day-全天 halfDay-半天 hour-时分
+            String unitStr = "";
+            switch (startMethod) {
+                case "0":
+                case "1":
+                    unitStr = "halfDay";
+                    break;
+                case "2":
+                    unitStr = "day";
+                    break;
+                case "3":
+                    unitStr = "hour";
+                    break;
+            }
+            param.put("duration_unit", unitStr);
+            //根据单位处理时长
+            param.put("from_time", dealHalfDaySdf(entry.getDate("startdate"), startMethod));
+            param.put("to_time", dealHalfDaySdf(entry.getDate("enddate"), endMethod));
+        }
+    }
+
+    /**
+     * 构建休假入参
+     * @param param
+     * @param bill
+     * @param entityNumber
+     * @param isChange
+     * @param openMap
+     */
+    private static void buildVAContent(JSONObject param, DynamicObject bill, String entityNumber, boolean isChange, Map openMap) {
+        log.info("构建休假入参报文 start ");
+        boolean isCancel = bill.getBoolean("isnotleave");
+        getBaseUrl(isCancel, false);
+        //休假失效的情况:
+        if(isCancel) {
+            param.put("approve_id", bill.getLong("originalid"));
+            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
+        }
+        //非失效的情况:
+        else {
+            param.put("biz_type", 3);
+            param.put("jump_url", "https://open.dingtalk.com/");
+            param.put("tag_name", "请假");
+            param.put("calculate_model", "1");
+            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
+            DynamicObject entry = bill.getDynamicObjectCollection("entryentity").get(0);
+            //sub_type 请假类型
+            param.put("sub_type", entry.getString("entryvacationtype.name"));
+            //approve_id 直接取原单ID
+            param.put("approve_id", bill.getLong("originalid"));
+            //分录单位 0-上半天 1-下半天 2-全天 3-时分
+            String startMethod = entry.getString("entrystartmethod");
+            String endMethod = entry.getString("entryendmethod");
+            //duration_unit 单位 day-全天 halfDay-半天 hour-时分
+            String unitStr = "";
+            switch (startMethod) {
+                case "0":
+                case "1":
+                    unitStr = "halfDay";
+                    break;
+                case "2":
+                    unitStr = "day";
+                    break;
+                case "3":
+                    unitStr = "hour";
+                    break;
+            }
+            param.put("duration_unit", unitStr);
+            //根据单位处理时长
+            param.put("from_time", dealHalfDaySdf(entry.getDate("entrystartdate"), startMethod));
+            param.put("to_time", dealHalfDaySdf(entry.getDate("entryenddate"), endMethod));
+        }
+    }
+
+    /**
+     * 构建补卡入参
+     * @param param
+     * @param bill
+     * @param entityNumber
+     * @param isChange
+     * @param openMap
+     */
+    private static void buildFillContent(JSONObject param, DynamicObject bill, String entityNumber, boolean isChange, Map openMap) throws IOException {
+        log.info("构建补卡入参报文 start ");
+        getBaseUrl(isChange, true);
+        //补卡没有变更,isChange=true直接判断为失效
+        if(isChange) {
+            param.put("approve_id", bill.getLong("originalid"));
+            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
+        }
+        else {
+            param.put("jump_url", "https://open.dingtalk.com/");
+            param.put("tag_name", "补卡");
+            param.put("approve_id", bill.getLong("originalid"));
+            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
+            DynamicObject entry = bill.getDynamicObjectCollection("entryentity").get(0);
+            //work_date
+            Date signDate = entry.getDate("signdate");
+            param.put("work_date", WTCDateUtils.date2Str(signDate, "yyyy-MM-dd"));
+            long time = entry.getLong("suppleworktime");
+            LocalTime localTime = WTCDateUtils.secondToTime(time);
+            LocalDateTime localDateTime = LocalDateTime.of(WTCDateUtils.toLocalDate(signDate), localTime);
+            String dateTimeStr = localDateTimeToStr(localDateTime, "yyyy-MM-dd HH:mm:ss");
+            param.put("punch_check_time",dateTimeStr);
+            param.put("user_check_time",dateTimeStr);
+            //punch_id 钉钉排班ID 另外调接口获取
+            param.put("punch_id",getDingTalkShiftId(bill, openMap));
+        }
+    }
+
+
+    /**
+     * 返回Url
+     * @param isCancel
+     * @param isFill
+     */
+   public static void getBaseUrl (boolean isCancel, boolean isFill) {
+        if(isCancel) {
+            baseUrl = DingTalkSyncUtil.getParamValue(cancelParamKey);
+        }
+        else if(isFill) {
+            baseUrl = DingTalkSyncUtil.getParamValue(fillParamKey);
+        }
+        else {
+            baseUrl = DingTalkSyncUtil.getParamValue(finishParamKey);
+        }
+   }
+
+    /**
+     * 获取钉钉班次ID
+     * {
+     * 	"errcode": 0,
+     * 	"errmsg": "ok",
+     * 	"result": [
+     *                {
+     * 			"check_type": "OnDuty",
+     * 			"group_id": 1250482934,
+     * 			"id": 814088651114,
+     * 			"is_rest": "N",
+     * 			"overtime_setting_id": 696580239,
+     * 			"plan_check_time": "2025-06-03 08:30:00",
+     * 			"shift_id": 1386234138,
+     * 			"shift_version": 1317657354,
+     * 			"userid": "13697915049",
+     * 			"work_date": "2025-06-03 00:00:00"
+     *        },
+     *        {
+     * 			"check_type": "OffDuty",
+     * 			"group_id": 1250482934,
+     * 			"id": 814088651115,
+     * 			"is_rest": "N",
+     * 			"overtime_setting_id": 696580239,
+     * 			"plan_check_time": "2025-06-03 17:30:00",
+     * 			"shift_id": 1386234138,
+     * 			"shift_version": 1317657354,
+     * 			"userid": "13697915049",
+     * 			"work_date": "2025-06-03 00:00:00"
+     *        }
+     * 	],
+     * 	"success": true,
+     * 	"request_id": "16kk36k9q33hf"
+     * }
+     * @return
+     */
+    public static Long getDingTalkShiftId (DynamicObject bill, Map openMap) throws IOException {
+        DynamicObject entry = bill.getDynamicObjectCollection("entryentity").get(0);
+        Date shiftDate = entry.getDate("signdate");
+        Long shiftDateUnix = dateToUnix(shiftDate);
+        JSONObject ob = new JSONObject(true);
+        ob.put("from_date_time", shiftDateUnix);
+        ob.put("to_date_time", shiftDateUnix);
+        ob.put("op_user_id", "000082");
+        ob.put("userids", openMap.get(bill.getString("attfile.personnum")));
+        String url = DingTalkSyncUtil.getParamValue(shiftParamKey);
+        url = getUrl(url, accessToken);
+        JSONObject response = doPost(url, ob);
+        log.info("调用排班返回:{}", response);
+        DingTalkSyncUtil.createLog("A", bill.getString("billno"), ob.toJSONString(), response.toJSONString(), DingTalkSyncUtil.SyncShift);
+        if(ObjectUtils.isEmpty(response)) {
+            return null;
+        }
+        JSONArray shiftArr = response.getJSONArray("result");
+        if(ObjectUtils.isEmpty(shiftArr)) {
+            return null;
+        }
+        //根据进出卡标识获取对应标识
+        String tag = entry.getString("accesstag");
+        String checkType = "on".equals(tag) ? "OnDuty" : "OffDuty";
+
+        for(int i = 0 ; i < shiftArr.size(); i++) {
+            JSONObject shift = shiftArr.getJSONObject(i);
+            if(!checkType.equals(shift.getString("check_type")))
+                continue;
+            return shift.getLong("id");
+        }
+
+        return null;
+    }
+
+
+    public static String getUrl (String url, String accessToken) {
+        return url + "?access_token=" + accessToken;
+    }
+
+    /**
+     * 根据单位处理时间格式
+     * method == 0 || method == 1时:返回yyyy-MM-dd AM 或者 yyyy-MM-dd PM
+     * method == 2 返回yyyy-MM-dd
+     * method == 3 返回yyyy-MM-dd HH:mm:ss
+     * @param date
+     * @param method
+     * @return
+     */
+    public static String dealHalfDaySdf (Date date, String method) {
+        SimpleDateFormat sdf;
+        switch(method) {
+            case "0":
+                sdf = new SimpleDateFormat("yyyy-MM-dd");
+                return sdf.format(date) + " AM";
+            case "1":
+                sdf = new SimpleDateFormat("yyyy-MM-dd");
+                return sdf.format(date) + " PM";
+            case "2":
+                sdf = new SimpleDateFormat("yyyy-MM-dd");
+                return sdf.format(date);
+            case "3":
+                sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                return sdf.format(date);
+            default :
+                return null;
+        }
+    }
+
+    /**
+     * localDateTime转String
+     * @param localDateTime
+     * @param pattern
+     * @return
+     */
+    public static String localDateTimeToStr (LocalDateTime localDateTime, String pattern) {
+        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern(pattern);
+        return localDateTime.format(dateFormat);
+    }
+
+    /**
+     * date转Unix
+     * @param date
+     * @return
+     */
+    public static Long dateToUnix (Date date) {
+        return date.getTime();
+    }
+
+
+}

+ 3 - 2
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/wtc/wtis/util/SyncPunchCardHelper.java

@@ -45,7 +45,7 @@ public class SyncPunchCardHelper {
         try {
             long l1 = System.currentTimeMillis();
             PunchCardSyncSupport.execute(() -> {
-                syncCardRecord(batchNumber);
+                syncCardRecord(resultArr,batchNumber);
             });
             long l2 = System.currentTimeMillis();
             logger.info("SyncPunchCardHelper_sync_cardData_{}", l2 - l1);
@@ -101,9 +101,10 @@ public class SyncPunchCardHelper {
 
     /**
      * 同步打卡记录
+     * @JSONArray resultArr
      * @param batchNumber
      */
-    private static void syncCardRecord(String batchNumber) {
+    private static void syncCardRecord(JSONArray resultArr, String batchNumber) {
         try {
             long l1 = System.currentTimeMillis();
             DynamicObject[] date = PunchCardDataHelper.getDataByBatchNumber(batchNumber);