Sfoglia il codice sorgente

月度人员入离职统计跑批更新

fyz 8 mesi fa
parent
commit
e6c4db4e6b

+ 1 - 1
mjava-lanyun/src/main/java/com/malk/lanyun/schedule/NCTask.java

@@ -37,7 +37,7 @@ public class NCTask {
         ncService.getNcCost();
     }
     /**
-     * 每月1日凌晨5点30跑批NC部门费用达成明细
+     * 每月1日凌晨5点30跑批NC-部门费用达成明细
      */
     @Scheduled(cron = "0 30 5 1 * ?")
     void departmentCost() {

+ 9 - 2
mjava-lanyun/src/main/java/com/malk/lanyun/schedule/ScheduleTask.java

@@ -54,9 +54,9 @@ public class ScheduleTask {
     }
 
     /**
-     * 各项目经营分析每月1日上午4点自动插入数据库
+     * 各项目经营分析每月1日上午3点30自动插入数据库
      */
-    @Scheduled(cron = "0 0 4 1 * ?")
+    @Scheduled(cron = "0 30 3 1 * ?")
     void insertAnalysisList(){
         timerService.insertAnalysisList();
     }
@@ -111,4 +111,11 @@ public class ScheduleTask {
         log.info("项目点档案本周出库情况执行时间 :" + new Date());
     }
 
+    /**
+     * 各项目经营分析每日上午2点30更新月度入离职
+     */
+    @Scheduled(cron = "0 30 2 * * ?")
+    void monthPersonnelTask(){
+        timerService.monthPersonnelMatters();
+    }
 }

+ 5 - 0
mjava-lanyun/src/main/java/com/malk/lanyun/service/TimerService.java

@@ -76,4 +76,9 @@ public interface TimerService {
      * 定时更新本周未出库项目数
      */
     void projectUnshipped();
+
+    /**
+     * 每日定时跑批本周到当天的入职离职人数,和当天在职人数
+     */
+    void monthPersonnelMatters();
 }

+ 111 - 1
mjava-lanyun/src/main/java/com/malk/lanyun/service/impl/TimerServiceImpl.java

@@ -1,7 +1,7 @@
 package com.malk.lanyun.service.impl;
 
+import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
@@ -13,6 +13,8 @@ import com.malk.server.aliwork.YDConf;
 import com.malk.server.aliwork.YDParam;
 import com.malk.service.aliwork.YDClient;
 import com.malk.service.aliwork.YDService;
+import com.malk.service.dingtalk.DDClient;
+import com.malk.service.dingtalk.DDClient_Personnel;
 import com.malk.utils.UtilDateTime;
 import com.malk.utils.UtilExcel;
 import com.malk.utils.UtilMap;
@@ -31,6 +33,7 @@ import java.sql.PreparedStatement;
 import java.text.DecimalFormat;
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.logging.Level;
 import java.util.stream.Collectors;
 
 import static java.lang.Integer.parseInt;
@@ -47,6 +50,12 @@ public class TimerServiceImpl implements TimerService {
     private String password;
     @Value("${spring.datasource.driver-class-name}")
     private String driver;
+    @Value("${dingtalk.appKey}")
+    private String dingtalkAppKey;
+    @Value("${dingtalk.appSecret}")
+    private String dingtalkAppSecret;
+    @Value("${dingtalk.agentId}")
+    private Long agentId;
     @Autowired
     private JdbcTemplate jdbcTemplate;
     @Autowired
@@ -55,6 +64,12 @@ public class TimerServiceImpl implements TimerService {
     @Autowired
     private YDClient ydClient;
 
+    @Autowired
+    private DDClient ddClient;
+
+    @Autowired
+    private DDClient_Personnel ddClientPersonnel;
+
     /**
      * 月度定额更新
      */
@@ -846,4 +861,99 @@ public class TimerServiceImpl implements TimerService {
                     .build(), YDConf.FORM_OPERATION.update);
         });
     }
+
+    @Override
+    public void monthPersonnelMatters() {
+
+        Date date = DateUtil.date();
+        Date beginOfMonth = DateUtil.beginOfMonth(date);
+        String[] dateList = new String[2];
+        dateList[0] = String.valueOf(beginOfMonth.getTime());
+        dateList[1] = String.valueOf(date.getTime());
+        System.out.println(dateList);
+        //查询宜搭已存在的当月数据并删除
+        List<Map> materialList = ydService.queryFormData_all(YDParam.builder()
+                .appType("APP_ERBDTFS82HOVBPL3NFH0")
+                .systemToken("RRB66F91T97H1WN89QZYC47PKLZO2ZQOUMOQLP")
+                .formUuid("FORM-4F7FB29798F442E783B1C77AF1E7D644FBI7")
+                .searchFieldJson(JSON.toJSONString(UtilMap.map("dateField_lzhqum4c", (Object) dateList)))
+                .build());
+        if (ObjectUtil.isNotNull(materialList)&&materialList.size()>0){
+            materialList.forEach(e->{
+                ydClient.operateData(YDParam.builder()
+                        .appType("APP_ERBDTFS82HOVBPL3NFH0")
+                        .systemToken("RRB66F91T97H1WN89QZYC47PKLZO2ZQOUMOQLP")
+                        .formInstanceId(e.get("formInstanceId").toString())
+                        .build(), YDConf.FORM_OPERATION.delete);
+            });
+        }
+
+        //获取所有离职员工id
+        String dingtalkToken = ddClient.getAccessToken(dingtalkAppKey, dingtalkAppSecret);
+        List<String> leaveIdList = ddClientPersonnel.getLeaveEmployeeIdList(dingtalkToken);
+        //获取当前在职人员id
+        String status = "2,3,5,-1";
+        List<String> employeeIds = ddClientPersonnel.getWorkingEmployeeIds(ddClient.getAccessToken(), status);
+        //第一个元素为本月入职人数
+        final int[] month = {0,0};
+        List<String> filterList = new ArrayList<>();
+        Set<String> inJobIdSet = new HashSet<>();
+        List<String> inJobIds = new ArrayList<>();
+        inJobIds.addAll(employeeIds);
+        inJobIds.addAll(leaveIdList);
+        //现合同起始日
+        filterList.add("sys05-nowContractStartTime");
+        //现合同到期日
+        filterList.add("sys05-nowContractEndTime");
+        //入职时间
+        filterList.add("sys00-confirmJoinTime");
+        //获取待入职人员id列表
+//        List<String> pendingIds = ddClientPersonnel.getPendingEmployeeIds(ddClient.getAccessToken());
+        //根据在职人员id和离职人员id列表获取花名册信息
+        List<Map> employeeInfos = ddClientPersonnel.getEmployeeInfos(ddClient.getAccessToken(), inJobIds, agentId, filterList);
+        System.out.println(employeeInfos);
+        employeeInfos.forEach(e->{
+            if (ObjectUtil.isNotNull(e.get("field_data_list"))){
+                List<Map> userData = (List<Map>) e.get("field_data_list");
+                userData.forEach(f->{
+                    if ("sys00-confirmJoinTime".equals(f.get("field_code"))){
+                        List<Map> dataValue = (List<Map>) f.get("field_value_list");
+                        if (ObjectUtil.isNotNull(dataValue.get(0).get("value"))){
+                            Date value = DateUtil.parse(dataValue.get(0).get("value").toString());
+//                            if (dataValue.get(0).get("value").toString().substring(0,7).equals("2024-09")){
+                                if (DateUtil.compare(value,beginOfMonth)>=0 && DateUtil.compare(date,value)>=0){
+//                                month[0]++;
+                                    inJobIdSet.add(e.get("userid").toString());
+//                                }
+                            }
+                        }
+                    }
+                });
+            }
+        });
+
+        //根据员工id获取员工信息,过滤当前时间到本月初的人员
+        if (ObjectUtil.isNotNull(leaveIdList)&&leaveIdList.size()>0){
+            List<Map> leaveInfos = ddClientPersonnel.getLeaveEmployeeInfos(dingtalkToken, leaveIdList);
+            if (ObjectUtil.isNotNull(leaveInfos) && leaveInfos.size()>0){
+                leaveInfos.forEach(e->{
+                    if (ObjectUtil.isNotNull(e.get("lastWorkDay"))){
+                        long lastWorkTime = Long.parseLong(e.get("lastWorkDay").toString());
+                        Date lastWorkDate = DateUtil.date(lastWorkTime);
+                        if (ObjectUtil.isNotEmpty(lastWorkDate) && DateUtil.compare(lastWorkDate,beginOfMonth)>=0 && DateUtil.compare(date,lastWorkDate)>=0){
+                            month[1] += 1;
+                        }
+                    }
+                });
+            }
+        }
+//
+        ydClient.operateData(YDParam.builder()
+                .appType("APP_ERBDTFS82HOVBPL3NFH0")
+                .systemToken("RRB66F91T97H1WN89QZYC47PKLZO2ZQOUMOQLP")
+                .formUuid("FORM-4F7FB29798F442E783B1C77AF1E7D644FBI7")
+                //年月,离职人数,入职人数,在职人数
+                .formDataJson(JSON.toJSONString(UtilMap.map("dateField_lzhqum4c, numberField_lzhqum4e, numberField_lzhqum4f, numberField_lzhqum4d",beginOfMonth.getTime(),month[1],inJobIdSet.size(),employeeIds.size())))
+                .build(), YDConf.FORM_OPERATION.create);
+    }
 }

+ 8 - 1
mjava/src/main/java/com/malk/server/dingtalk/DDR.java

@@ -1,5 +1,6 @@
 package com.malk.server.dingtalk;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.malk.server.common.McException;
 import com.malk.server.common.VenR;
 import lombok.Data;
@@ -54,6 +55,10 @@ public class DDR<T> extends VenR {
 
     private String corpid;
 
+    private List<String> userIdList;
+
+    private Long nextToken;
+
     /**
      * 发送工作通知
      */
@@ -75,7 +80,9 @@ public class DDR<T> extends VenR {
      */
     @Override
     public void assertSuccess() {
-        McException.assertException(!errcode.equals(SUC_CODE) && !errcode.equals(USER_CREATE), errcode, errmsg, "dingtalk");
+//        if (!hasMore && ObjectUtil.isNull(userIdList)){
+            McException.assertException(!errcode.equals(SUC_CODE) && !errcode.equals(USER_CREATE), errcode, errmsg, "dingtalk");
+//        }
     }
 
     /**

+ 30 - 0
mjava/src/main/java/com/malk/service/dingtalk/DDClient_Personnel.java

@@ -17,4 +17,34 @@ public interface DDClient_Personnel {
      * @param field_filter_list [非必填] 需要获取的花名册字段field_code值列表,多个字段之间使用逗号分隔,一次最多支持传100个值
      */
     List<Map> getEmployeeInfos(String access_token, List<String> userIds, Number agentId, List<String> field_filter_list);
+
+    /**
+     * 获取待入职员工列表
+     * @param access_token
+     * @return
+     */
+    List<String> getPendingEmployeeIds(String access_token);
+
+    /**
+     * 获取在职员工列表
+     * @param access_token
+     * @param statusList
+     * @return
+     */
+    List<String> getWorkingEmployeeIds(String access_token, String statusList);
+
+    /**
+     * 获取离职员工id列表
+     * @param access_token
+     * @return
+     */
+    List<String> getLeaveEmployeeIdList(String access_token);
+
+    /**
+     * 根据离职员工id列表批量获取离职员工信息
+     * @param access_token
+     * @param userIdList
+     * @return
+     */
+    List<Map> getLeaveEmployeeInfos(String access_token, List<String> userIdList);
 }

+ 3 - 1
mjava/src/main/java/com/malk/service/dingtalk/impl/DDImplClient_Contacts.java

@@ -160,7 +160,9 @@ public class DDImplClient_Contacts implements DDClient_Contacts {
             body.put("maxResults", 50);
         }
         DDR_New ddr_new = DDR_New.doGet("https://api.dingtalk.com/v1.0/contact/empLeaveRecords", DDConf.initTokenHeader(access_token), body);
-        dataList.addAll(ddr_new.getRecords());
+        if (ObjectUtil.isNotNull(ddr_new.getRecords())){
+            dataList.addAll(ddr_new.getRecords());
+        }
         if (StringUtils.isNotBlank(ddr_new.getNextToken())) {
             extInfo = UtilMap.putNotNull(extInfo, "nextToken", ddr_new.getNextToken());
             _getLeaveEmployeeRecords(access_token, startTime, extInfo, dataList);

+ 140 - 4
mjava/src/main/java/com/malk/service/dingtalk/impl/DDImplClient_Personnel.java

@@ -1,5 +1,10 @@
 package com.malk.service.dingtalk.impl;
 
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.malk.server.dingtalk.DDConf;
 import com.malk.server.dingtalk.DDR;
 import com.malk.service.dingtalk.DDClient_Personnel;
@@ -8,6 +13,7 @@ import com.malk.utils.UtilMap;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -15,6 +21,10 @@ import java.util.Map;
 @Slf4j
 public class DDImplClient_Personnel implements DDClient_Personnel {
 
+    private final int size = 50;
+
+    private final int offset = 0;
+
     /**
      * 获取花名册元数据
      */
@@ -30,10 +40,136 @@ public class DDImplClient_Personnel implements DDClient_Personnel {
      */
     @Override
     public List<Map> getEmployeeInfos(String access_token, List<String> userIds, Number agentId, List<String> field_filter_list) {
-        Map bodys = UtilMap.map("userid_list, agentid", String.join(",", userIds), agentId);
-        if (UtilList.isNotEmpty(field_filter_list)) {
-            bodys.put("field_filter_list", String.join(",", field_filter_list));
+
+        int start = 0;
+        boolean isNext = true;
+        List<Map> result = new ArrayList<>();
+        StringBuilder ids = new StringBuilder();
+        StringBuilder filters = new StringBuilder();
+        if (ObjectUtil.isNotNull(field_filter_list)){
+            filters.append(String.join(",",field_filter_list));
+        }
+        while (isNext){
+            ids.append(String.join(",", userIds.subList(start * size, Math.min((start + 1) * size, userIds.size()))));
+            Map bodys = UtilMap.map("userid_list, agentid", ids.toString(), agentId);
+            if (ObjectUtil.isNotEmpty(filters)){
+                bodys.put("field_filter_list",filters);
+            }
+            List<Map> dataList = (List<Map>) DDR.doPost("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/v2/list", null,  DDConf.initTokenParams(access_token), bodys).getResult();
+            if (ObjectUtil.isNotNull(dataList) && dataList.size()>0){
+                result.addAll(dataList);
+            }
+            if (userIds.size() <= ((start + 1)*size)){
+                isNext = false;
+            }else {
+                start++;
+            }
+            ids = new StringBuilder();
+
+        }
+        return result;
+    }
+
+    /**
+     * 获取待入职员工列表
+     *
+     * @apiNote https://open.dingtalk.com/document/orgapp/intelligent-personnel-obtain-employee-roster-information
+     */
+    @Override
+    public List<String> getPendingEmployeeIds(String access_token) {
+        boolean isNext = true;
+        List<String> dataList = new ArrayList<>();
+        Map bodys = UtilMap.map("offset, size", offset, size );
+        while (isNext){
+            DDR ddr = DDR.doPost("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/querypreentry", null, DDConf.initTokenParams(access_token), bodys);
+            if (ObjectUtil.isNotNull(ddr.getResult())){
+                JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(ddr.getResult()));
+                JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(jsonObject.get("data_list")));
+                if (ObjectUtil.isNull(jsonObject.get("next_cursor"))){
+                    isNext = false;
+                }else {
+                    int next = Integer.parseInt(jsonObject.get("next_cursor").toString());
+                    bodys.put("offset",next);
+                }
+                jsonArray.forEach(e->{
+                    dataList.add(e.toString());
+                });
+            }
         }
-        return (List<Map>) DDR.doPost("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/v2/list", null, DDConf.initTokenParams(access_token), bodys).getResult();
+        return dataList;
     }
+
+    /**
+     * 获取在职员工列表
+     *
+     * @apiNote https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/queryonjob
+     */
+    @Override
+    public List<String> getWorkingEmployeeIds(String access_token, String statusList) {
+        boolean isNext = true;
+        List<String> dataList = new ArrayList<>();
+        Map bodys = UtilMap.map("status_list, offset, size",statusList, offset, size );
+        while (isNext){
+            DDR ddr = DDR.doPost("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/queryonjob", null, DDConf.initTokenParams(access_token), bodys);
+            JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(ddr.getResult()));
+            JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(jsonObject.get("data_list")));
+            if (ObjectUtil.isNull(jsonObject.get("next_cursor"))){
+                isNext = false;
+            }else {
+                int next = Integer.parseInt(jsonObject.get("next_cursor").toString());
+                bodys.put("offset",next);
+            }
+            jsonArray.forEach(e->{
+                dataList.add(e.toString());
+            });
+        }
+        return dataList;
+    }
+
+    /**
+     * 获取离职员工id列表
+     *
+     * @apiNote https://api.dingtalk.com/v1.0/hrm/employees/dismissions?nextToken=?
+     */
+    @Override
+    public List<String> getLeaveEmployeeIdList(String access_token) {
+        log.info("正在调用钉钉后台智能人事离职人员ID接口");
+        Long nextToken = 0L;
+        boolean hasMore = true;
+        List<String> maps = new ArrayList<>();
+        Map header = DDConf.initTokenHeader(access_token);
+        while (hasMore){
+            DDR ddr = DDR.doGet("https://api.dingtalk.com/v1.0/hrm/employees/dismissions?nextToken=" + nextToken + "&maxResults=" + size, header, null);
+            List<String> userIdList = ddr.getUserIdList();
+            maps.addAll(userIdList);
+            hasMore = ddr.isHasMore();
+            if (hasMore){
+                nextToken = ddr.getNextToken();
+            }
+        }
+        return maps;
+    }
+
+    /**
+     * 根据员工id获取离职员工信息
+     *
+     * @apiNote https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/queryonjob
+     */
+    @Override
+    public List<Map> getLeaveEmployeeInfos(String access_token, List<String> userIdList) {
+        log.info("正在调用钉钉后台智能人事离职人员信息接口");
+        List<String> idList = new ArrayList<>();
+        List<Map> lastResult = new ArrayList<>();
+        Map header = DDConf.initTokenHeader(access_token);
+        for (int i = 0; i < userIdList.size(); i+=size) {
+            idList.addAll(userIdList.subList(i,Math.min(i+size,userIdList.size())));
+                List<Map> result = (List<Map>) DDR.doGet("https://api.dingtalk.com/v1.0/hrm/employees/dimissionInfos?userIdList=" + JSON.toJSONString(idList), header, null).getResult();
+                if (ObjectUtil.isNotNull(result) && result.size()>0){
+                    lastResult.addAll(result);
+                }
+            idList.clear();
+        }
+        return lastResult;
+    }
+
 }

+ 1 - 1
mjava/src/main/java/com/malk/utils/UtilHttp.java

@@ -130,7 +130,7 @@ public abstract class UtilHttp {
     public static VenR doGet(String url, Map header, Map param, Class rClass) {
         String rsp = doGet(url, header, param);
         VenR r = (VenR) JSON.parseObject(rsp, rClass);
-        r.assertSuccess();
+//        r.assertSuccess();
         return r;
     }