Ver código fonte

虹桥凯越v1.0 烟台凯越修改

wzy 9 meses atrás
pai
commit
f24247ac56

+ 155 - 0
mjava-kaiyue/src/main/java/com/malk/kaiyue/controller/KYHQController.java

@@ -0,0 +1,155 @@
+package com.malk.kaiyue.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.malk.controller.DDCallbackController;
+import com.malk.kaiyue.service.KYHQService;
+import com.malk.server.common.McR;
+import com.malk.server.dingtalk.DDConf;
+import com.malk.server.dingtalk.crypto.DingCallbackCrypto;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@RestController
+@RequestMapping("/hq")
+@Component
+public class KYHQController extends DDCallbackController {
+    @Autowired
+    private KYHQService kyhqService;
+
+    @Autowired
+    private DDConf ddConf;
+
+    @Value("${dingtalk_hq.token}")
+    private String token;
+
+    @Value("${dingtalk_hq.aesKey}")
+    private String aesKey;
+
+    @Value("${dingtalk_hq.appKey}")
+    private String appKey;
+
+    @PostMapping("/test")
+    McR test(){
+        log.info("11111");
+        return McR.success();
+    }
+
+    //获取在职员工userId列表
+    @GetMapping("/getEmployeeUserId")
+    McR getEmployeeUserId() {
+
+        List<String> userIdList = kyhqService.getEmployeeUserId();
+
+        return McR.success(userIdList);
+    }
+
+    //获取员工花名册信息
+    @PostMapping("/getEmployeeRosterInfo")
+    McR getEmployeeRosterInfo(@RequestBody Map<String, Object> map) {
+        List<Map> result = kyhqService.getEmployeeRosterInfo(map);
+
+        return McR.success(result);
+    }
+
+    //计算并设置员工年假数
+    @PostMapping("/getEmployeeAnnualLeaveNum")
+    McR getEmployeeAnnualLeaveNum(@RequestBody Map<String, Object> map) {
+        kyhqService.getEmployeeAnnualLeaveNum(map);
+        return McR.success();
+    }
+
+    //获取员工真实假期余额
+    @PostMapping("/getUserLeaveInfo")
+    McR getUserLeaveInfo(@RequestBody Map map) {
+        String userId = map.get("userId").toString();
+        Map result = kyhqService.getUserLeaveInfo(userId);
+        return McR.success(result);
+    }
+
+
+    //每年1月1日 00:00定时更新员工旧职级
+    /*@Scheduled(cron = "0 0 0 1 1 ? ")
+    @GetMapping("/cronUpdateEmployeeOldPositionLevel")
+    McR cronUpdateEmployeeOldPositionLevel(){
+        System.out.println("定时更新员工旧职级开始执行"+new Date());
+        return kycdService.updateEmployeeOldPositionLevel();
+    }*/
+
+    //每天 00:00定时更新员工年假数
+    @Scheduled(cron = "0 0 0 * * ? ")
+    @GetMapping("/cronUpdateEmployeeAnnualLeaveNum")
+    McR cronUpdateEmployeeAnnualLeaveNum(){
+        System.out.println("定时更新员工年假数开始执行"+new Date());
+        return kyhqService.updateEmployeeAnnualLeaveNum();
+    }
+
+    //保存十分钟内已处理的回调事件
+    private Map<String, Long> eventList = new HashMap<>();
+
+    @SneakyThrows
+    public synchronized Map<String, String> invokeCallback(@RequestParam(value = "signature", required = false) String signature,
+                                              @RequestParam(value = "timestamp", required = false) String timestamp,
+                                              @RequestParam(value = "nonce", required = false) String nonce,
+                                              @RequestBody(required = false) JSONObject json) {
+        DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(token, aesKey, appKey);
+        // 处理回调消息,得到回调事件decryptMsg...
+        final String decryptMsg = callbackCrypto.getDecryptMsg(signature, timestamp, nonce, json.getString("encrypt"));
+        JSONObject eventJson = JSON.parseObject(decryptMsg);
+        Map success = callbackCrypto.getEncryptedMap(DDConf.CALLBACK_RESPONSE, System.currentTimeMillis(), DingCallbackCrypto.Utils.getRandomStr(8));
+
+        // 业务处理代码...
+        String eventType = eventJson.getString("EventType");
+        if (DDConf.CALLBACK_CHECK.equals(eventType)) {
+            log.info("----- [DD]验证注册 -----");
+            return success;
+        }
+        // [回调任务执行逻辑: 异步] 钉钉超时3s未返回会被记录为失败, 可通过失败接口获取记录
+        if (Arrays.asList(DDConf.HRM_USER_RECORD_CHANGE).contains(eventType)) {
+            log.info("[DD]人事档案变动回调, eventType:{}, eventJson:{}",eventType, eventJson);
+            //获取员工userId
+            String userId = "";
+            if (Objects.nonNull(eventJson.get("staffId"))){
+                //人事档案返回的userId
+                userId = eventJson.get("staffId").toString();
+            }else {
+                log.error("[DD]人事档案变动回调, 未获取到userId");
+                return success;
+            }
+
+            log.info("员工userId:"+userId);
+            Map<String, Object> map = new HashMap();
+            map.put("userid_list", userId);
+            //更新员工年假余额
+            log.info("----- [DD]更新员工年假余额 -----");
+            kyhqService.getEmployeeAnnualLeaveNum(map);
+            return success;
+        }
+        log.info("----- [DD]已注册, 未处理的其它回调 -----, eventType:{}, eventJson:{}",eventType, eventJson);
+        return success;
+    }
+
+    /**
+     * 检查该回调事件在十分钟内是否处理过,应对钉钉瞬间重复回调
+     *
+     * @param decryptMsg 回调事件
+     * @return 是否处理过
+     */
+    private boolean isCallbackProcessed(String decryptMsg) {
+        // 清理超过十分钟的回调事件
+        long currentTime = System.currentTimeMillis();
+        long expirationTime = currentTime - TimeUnit.MINUTES.toMillis(10);
+        eventList.entrySet().removeIf(entry -> entry.getValue() < expirationTime);
+
+        return eventList.containsKey(decryptMsg);
+    }
+}

+ 1 - 1
mjava-kaiyue/src/main/java/com/malk/kaiyue/controller/KYYTController.java

@@ -75,7 +75,7 @@ public class KYYTController extends DDCallbackController {
     }
 
     //每年1月1日 01:00定时更新员工年假数
-    @Scheduled(cron = "0 0 1 1 1 ? ")
+//    @Scheduled(cron = "0 0 1 1 1 ? ")
     @GetMapping("/cronUpdateEmployeeAnnualLeaveNum")
     McR cronUpdateEmployeeAnnualLeaveNum(){
         System.out.println("定时更新员工年假数开始执行"+new Date());

+ 5 - 2
mjava-kaiyue/src/main/java/com/malk/kaiyue/entity/AdvancedLeave.java

@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 
-@TableName(value = "kaiyue_cd")
+@TableName(value = "kaiyue")
 @Data
 public class AdvancedLeave {
     @TableId(value = "id", type = IdType.AUTO)
@@ -14,11 +14,14 @@ public class AdvancedLeave {
     private String userId;
 
     //预支假期余额
-    private int leaveNum;
+    private String leaveNum;
 
     //预支假期年份
     private String year;
 
+    //城市
+    private String city;
+
     //有效位 1:正常  0:删除
     private String validFlag;
 

+ 38 - 0
mjava-kaiyue/src/main/java/com/malk/kaiyue/service/KYHQService.java

@@ -0,0 +1,38 @@
+package com.malk.kaiyue.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.malk.kaiyue.entity.AdvancedLeave;
+import com.malk.server.common.McR;
+import org.springframework.scheduling.annotation.Async;
+
+import java.util.List;
+import java.util.Map;
+
+
+public interface KYHQService extends IService<AdvancedLeave> {
+    List<String> getEmployeeUserId();
+
+    List<Map> getEmployeeRosterInfo(Map<String, Object> map);
+
+    Map getEmployeeAnnualLeaveNum(Map<String, Object> map);
+
+    /**
+     * 每年1月1日 00:00定时更新员工旧职级
+     * @return
+     */
+//    McR updateEmployeeOldPositionLevel();
+
+    /**
+     * 每年1月1日 01:00定时更新员工年假数
+     * @return
+     */
+    McR updateEmployeeAnnualLeaveNum();
+
+    /**
+     * 获取用户真实假期余额
+     * @param userId
+     * @return
+     */
+    Map getUserLeaveInfo(String userId);
+
+}

+ 2 - 2
mjava-kaiyue/src/main/java/com/malk/kaiyue/service/impl/KYCDServiceImpl.java

@@ -282,10 +282,10 @@ public class KYCDServiceImpl extends ServiceImpl<AdvancedLeaveMapper, AdvancedLe
                         .eq(AdvancedLeave::getValidFlag,"1");
                 AdvancedLeave advancedLeave = advancedLeaveMapper.selectOne(advancedLeaveLambdaQueryWrapper);
                 if (Objects.nonNull(advancedLeave)){
-                    advanceLeave = advancedLeave.getLeaveNum();
+                    advanceLeave = Double.valueOf(advancedLeave.getLeaveNum());
                 }else {
                     advanceLeave = NumberUtil.sub(yearLeave - useLeaveNum).doubleValue() >= 8 ? 8 : NumberUtil.sub(yearLeave - useLeaveNum).intValue();
-                    advancedLeave.setLeaveNum(Integer.parseInt(advanceLeave+""));
+                    advancedLeave.setLeaveNum(advanceLeave + "");
                     advancedLeave.setUserId(userId);
                     advancedLeave.setYear(String.valueOf(DateUtil.year(new Date())));
                     advancedLeaveMapper.insert(advancedLeave);

+ 600 - 0
mjava-kaiyue/src/main/java/com/malk/kaiyue/service/impl/KYHQServiceImpl.java

@@ -0,0 +1,600 @@
+package com.malk.kaiyue.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.NumberUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.malk.kaiyue.entity.AdvancedLeave;
+import com.malk.kaiyue.mapper.AdvancedLeaveMapper;
+import com.malk.kaiyue.service.KYCDService;
+import com.malk.kaiyue.service.KYHQService;
+import com.malk.server.common.McR;
+import com.malk.server.dingtalk.DDConf;
+import com.malk.server.dingtalk.DDR;
+import com.malk.server.dingtalk.DDR_New;
+import com.malk.service.dingtalk.DDClient;
+import com.malk.utils.UtilHttp;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class KYHQServiceImpl extends ServiceImpl<AdvancedLeaveMapper, AdvancedLeave> implements KYHQService {
+    @Autowired
+    private DDClient ddClient;
+
+    @Autowired
+    private DDConf ddConf;
+
+    @Autowired
+    private AdvancedLeaveMapper advancedLeaveMapper;
+
+    @Value("${dingtalk_hq.appKey}")
+    private String appKey;
+
+    @Value("${dingtalk_hq.appSecret}")
+    private String appSecret;
+
+    @Value("${dingtalk_hq.agentId}")
+    private String agentId;
+
+    @Value("${dingtalk_hq.operator}")
+    private String opUserId;
+
+    //虹桥凯悦-年假测试
+    private static final String LEAVE_CODE = "5c8a4c95-09e1-4f2d-9712-5676e2e5a4fd";
+    //体验社-成都年假测试
+//    private static final String LEAVE_CODE = "609a84ed-54d4-4ecd-a44f-4c55b04c37ea";
+
+    @Override
+    public List<String> getEmployeeUserId() {
+        Map<String,Object> map = new HashMap<>();
+        //在职员工状态筛选,可以查询多个状态。不同状态之间使用英文逗号分隔。
+        //2:试用期  3:正式  5:待离职  -1:无状态
+        map.put("status_list","3");
+        //分页游标,从0开始。根据返回结果里的next_cursor是否为空来判断是否还有下一页,且再次调用时offset设置成next_cursor的值。
+        map.put("offset",0);
+        //分页大小,最大50。
+        map.put("size",50);
+
+        //获取员工userId集合
+        List<String> userIdList = new ArrayList<>();
+        getUserIdList(map,userIdList);
+
+        return userIdList;
+    }
+
+    public List<String> getUserIdList(Map map,List<String> userIdList){
+        //获取accessToken
+        String access_token = ddClient.getAccessToken(appKey,appSecret);
+        //调用钉钉接口获取在职员工userId集合
+        DDR ddr = (DDR) UtilHttp.doPost("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/queryonjob", null, DDConf.initTokenParams(access_token), map, DDR.class);
+        Map result = (Map) ddr.getResult();
+        //将返回结果里的data_list合并到userIdList
+        userIdList.addAll((List<String>) result.get("data_list"));
+        //判断是否还有下一页
+        if (Objects.nonNull(result.get("next_cursor"))){
+            map.put("offset",result.get("next_cursor"));
+            //递归将集合合并到userIdList
+            getUserIdList(map,userIdList);
+        }
+
+        return userIdList;
+    }
+
+    @Override
+    public List<Map> getEmployeeRosterInfo(Map<String, Object> map) {
+        //获取accessToken
+        String access_token = ddClient.getAccessToken(appKey,appSecret);
+        //获取员工花名册字段信息
+        if (Objects.nonNull(map)){
+            DDR ddr = (DDR) UtilHttp.doPost("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/v2/list", null, DDConf.initTokenParams(access_token), map, DDR.class);
+            return (List<Map>)ddr.getResult();
+        }
+
+        return null;
+    }
+
+    //保存5s内已处理的更新假期余额事件
+    private Map<String, Long> bodyList = new ConcurrentHashMap<>();
+
+    @Async
+    public Map getEmployeeAnnualLeaveNum(Map<String, Object> map) {
+        //获取accessToken
+        String access_token = ddClient.getAccessToken(appKey,appSecret);
+
+        //查询接口body添加参数
+        //field_filter_list:要查询字段(花名册字段信息参考:https://open.dingtalk.com/document/orgapp/roster-custom-field-business-code)
+        //agentid:企业内部应用AgentId
+        map.put("field_filter_list","sys00-name,sys01-positionLevel,sys00-confirmJoinTime,371db808-4043-4f03-9c27-8fc8a08e741d,b00c205d-c509-4f01-8706-5279d10532f8,sys05-nowContractStartTime,sys05-contractRenewCount");
+        map.put("agentid",agentId);
+
+        List<Map> errorList = new ArrayList<>();
+        List<Map> successList = new ArrayList<>();
+        //获取员工花名册字段信息
+        if (Objects.nonNull(map)){
+            DDR ddr = (DDR) UtilHttp.doPost("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/v2/list", null, DDConf.initTokenParams(access_token), map, DDR.class);
+            List<Map> employeeData = (List<Map>) ddr.getResult();
+
+            //遍历员工信息
+            for (Map data : employeeData) {
+                String userId = data.get("userid").toString();
+                try{
+                    //入职日期
+                    String confirmJoinTime = "";
+                    //职级
+                    String positionLevel = "";
+                    //姓名
+                    String name = "";
+                    //原职级
+                    String oldPositionLevel = "";
+                    //升职日期
+                    String promotionTime = "";
+                    //现合同开始日期
+                    String contractStartTime = "";
+                    //合同续签次数
+                    int contractRenewCount = 0;
+
+                    List<Map> fieldDataList = (List<Map>) data.get("field_data_list");
+                    for (Map fieldData : fieldDataList) {
+                        String fieldCode = fieldData.get("field_code").toString();
+                        List<Map> fieldValueList = (List<Map>) fieldData.get("field_value_list");
+
+                        if (Objects.nonNull(fieldValueList.get(0).get("value"))){
+                            String value = fieldValueList.get(0).get("value").toString();
+                            switch (fieldCode){
+                                case "sys00-confirmJoinTime": confirmJoinTime = value;break;
+                                case "sys01-positionLevel": positionLevel = value;break;
+                                case "sys00-name": name = value;break;
+                                case "371db808-4043-4f03-9c27-8fc8a08e741d": oldPositionLevel = value;break;//原职级
+                                case "b00c205d-c509-4f01-8706-5279d10532f8": promotionTime = value;break;//升职日期
+                                case "sys05-nowContractStartTime": contractStartTime = value;break;
+                                case "sys05-contractRenewCount": contractRenewCount = Integer.valueOf(value);break;
+                                default:break;
+                            }
+                        }
+                    }
+                    //若没有原职级 则默认原职级是现职级
+                    if ("".equals(oldPositionLevel)){
+                        oldPositionLevel = positionLevel;
+                    }
+                    //若没有升职日期 则默认升职日期是入职日期
+                    if ("".equals(promotionTime)){
+                        promotionTime = confirmJoinTime;
+                    }
+                    //若没有现合同开始日期 则默认现合同开始日期是入职日期
+                    if ("".equals(contractStartTime)){
+                        contractStartTime = confirmJoinTime;
+                    }
+
+                    if ("".equals(confirmJoinTime) || "".equals(positionLevel) || "".equals(name)){
+                        Map errorMap = new HashMap();
+                        errorMap.put(userId,"参数缺失!");
+                        errorList.add(errorMap);
+//                        log.info("更新员工userid:{} 参数缺失!", userId);
+                        continue;
+                    }
+
+                    //假期有效开始日期为当年1月1日
+                    DateTime beginDate = DateUtil.beginOfYear(new Date());
+                    //假期有效截至日期为当年12月31日
+                    DateTime endDate = DateUtil.endOfYear(new Date());
+                    //今天
+                    DateTime today = DateUtil.date();
+                    //当年天数
+                    int yearDays = DateUtil.dayOfYear(endDate);
+                    //年假
+                    double yearLeave = 0.0;
+                    //预支年假
+                    double advanceLeave = 3.0;
+
+                    //获取原职级年假基数
+                    int oldPositionLevelBaseNum = getAnnualLeaveBaseNum(oldPositionLevel);
+                    //获取现职级年假基数
+                    int positionLevelBaseNum = getAnnualLeaveBaseNum(positionLevel);
+
+                    int year = DateUtil.year(new Date());
+
+                    //判断员工是否当年新入职
+                    if (DateUtil.year(DateUtil.parse(confirmJoinTime)) == DateUtil.year(new Date())){
+                        beginDate = DateUtil.parse(confirmJoinTime);
+                    }
+
+                    boolean flag1 = false;
+                    boolean flag2 = false;
+                    //判断升职日期是否是今年
+                    if (DateUtil.year(DateUtil.parse(promotionTime)) == year){
+                        flag1 = true;
+                    }
+                    //判断合同开始日期是否在今年
+                    if (DateUtil.year(DateUtil.parse(contractStartTime)) == year){
+                        flag2 = true;
+                    }
+                    //上期合同续签数附加年假数
+                    int lastAdditiveYearLeave = Math.min(Math.max(contractRenewCount - 1, 0), 2) * 2;
+                    //合同续签数附加年假数
+                    int additiveYearLeave = Math.min(contractRenewCount, 2) * 2;
+                    if (!flag1 && !flag2){
+                        long day1 = DateUtil.betweenDay(beginDate, today, true);
+                        yearLeave = (double) (day1 * (positionLevelBaseNum + additiveYearLeave)) / yearDays ;
+                        log.info("day1:{},yearLeave:{}", day1, yearLeave);
+                    }
+                    if (flag1 && !flag2){
+                        //假期有效开始日期-升职日-今天
+                        //获取假期有效开始日期到升职日的天数
+                        long day1 = DateUtil.betweenDay(beginDate, DateUtil.parse(promotionTime), true);
+                        //获取升职日到今天的天数
+                        long day2 = DateUtil.betweenDay(DateUtil.parse(promotionTime), today, true) + 1;
+                        //分两段计算:假期有效开始日期-现升职日,现职日到今天
+                        yearLeave = (double) (day1 * (oldPositionLevelBaseNum + additiveYearLeave) + day2 * (positionLevelBaseNum + additiveYearLeave)) / yearDays ;
+                        log.info("day1:{},day2:{},yearLeave:{}", day1, day2, yearLeave);
+                    }
+                    if (!flag1 && flag2){
+                        //假期有效开始日期-现合同开始日-今天
+                        //获取假期有效开始日期到现合同开始日的天数
+                        long day1 = DateUtil.betweenDay(beginDate, DateUtil.parse(contractStartTime), true);
+                        //获取现合同开始日到今天的天数
+                        long day2 = DateUtil.betweenDay(DateUtil.parse(contractStartTime), today, true) + 1;
+                        //分三段计算:假期有效开始日期-现合同开始日期,现合同开始日期到升职日,升职日到今天
+                        yearLeave = (double) (day1 * (positionLevelBaseNum + lastAdditiveYearLeave) + day2 * (positionLevelBaseNum + additiveYearLeave)) / yearDays;
+                        log.info("day1:{},day2:{},yearLeave:{}", day1, day2, yearLeave);
+                    }
+                    if (flag1 && flag2){
+                        //判断升职日期与现合同开始日期先后
+                        //升职日期在现合同开始日期之前
+                        if (DateUtil.compare(DateUtil.parse(contractStartTime), DateUtil.parse(promotionTime)) >= 0){
+                            //获取假期有效开始日期到升职日的天数
+                            long day1 = DateUtil.betweenDay(beginDate, DateUtil.parse(promotionTime), true);
+                            //获升职值日到现合同开始日期的天数
+                            long day2 = DateUtil.betweenDay(DateUtil.parse(promotionTime), DateUtil.parse(contractStartTime), true);
+                            //获取现合同开始日期到今天的天数
+                            long day3 = DateUtil.betweenDay(DateUtil.parse(contractStartTime), today, true) + 1;
+                            //分三段计算:假期有效开始日期-现升职日,现职日到现现合同开始日期,现合同开始日期到今天
+                            yearLeave = (double) (day1 * (oldPositionLevelBaseNum + lastAdditiveYearLeave) + day2 * (positionLevelBaseNum + lastAdditiveYearLeave) + day3 * (positionLevelBaseNum + additiveYearLeave)) / yearDays ;
+                            log.info("day1:{},day2:{},day3:{},yearLeave:{}", day1, day2, day3, yearLeave);
+                        }else {
+                            //升职日在现合同开始日期之后
+                            //获取假期有效开始日期到现合同开始日的天数
+                            long day1 = DateUtil.betweenDay(beginDate, DateUtil.parse(contractStartTime), true);
+                            //获取现合同开始日到升职日的天数
+                            long day2 = DateUtil.betweenDay(DateUtil.parse(contractStartTime), DateUtil.parse(promotionTime), true);
+                            //获取升职日到今天的天数
+                            long day3 = DateUtil.betweenDay(DateUtil.parse(promotionTime), today, true) + 1;
+                            //分三段计算:假期有效开始日期-现合同开始日期,现合同开始日期到升职日,升职日到今天
+                            yearLeave = (double) (day1 * (oldPositionLevelBaseNum + lastAdditiveYearLeave) + day2 * (oldPositionLevelBaseNum + additiveYearLeave) + day3 * (positionLevelBaseNum + additiveYearLeave)) / yearDays;
+                            log.info("day1:{},day2:{},day3:{},yearLeave:{}", day1, day2, day3, yearLeave);}
+                    }
+
+                    yearLeave = Math.floor(yearLeave * 100.0) / 100.0;
+
+                    //查询出用户消费年假记录 当返回leaveRecords中calType为null或不返回该字段则为请假消耗 将计算出的年假数减去请假消耗的数量
+                /*Map body = new HashMap();
+                body.put("opUserId",opUserId);
+                body.put("leaveCode",LEAVE_CODE);
+                body.put("userIds",new String[]{userId});
+                body.put("pageNumber",0);
+                body.put("pageSize",50);
+                DDR_New useDdr = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v1.0/attendance/vacations/records/query", DDConf.initTokenHeader(access_token), null, body, DDR_New.class);
+                Map useResult = (Map) useDdr.getResult();
+                List<Map> useList = (List<Map>) useResult.get("leaveRecords");
+                Double useLeaveNum = 0d;
+                if (Objects.nonNull(useList) && !useList.isEmpty()){
+                    for (Map use : useList) {
+                        DateTime gmtCreate = DateUtil.date((long) use.get("gmtCreate"));
+                        //获取今年请假记录
+                        if (DateUtil.year(gmtCreate) == DateUtil.year(new Date())){
+                            //判断是否为正常请假而不是接口测试或期初假期发放
+                            if (!"接口测试修改".equals(use.get("leaveReason").toString()) && !"期初假期发放".equals(use.get("leaveReason").toString())){
+                                //若是请假消耗或管理员手动减少
+                                if (!use.containsKey("calType") || Objects.isNull(use.get("calType")) || "delete".equals(use.get("calType").toString())){
+                                    useLeaveNum += (int) use.get("recordNumPerDay") / 100;
+                                }
+                                //注:若是管理员手动增加 则假期余额会多出一个BCXsunNm记录增加的假期天数  最终会在设置的假期余额的基础上加上这些天数
+                                //故此处手动新增的假期余额不做处理
+                            }
+                        }
+                    }
+                }*/
+
+                    //年假余额
+                    BigDecimal annualLeaveNum = new BigDecimal(0.00);
+
+                    //查询员工年假余额
+                    long currentTime2 = System.currentTimeMillis();
+
+                    List<Map> leaveQuotasList = new ArrayList<>();
+                    getLeaveNum("5c8a4c95-09e1-4f2d-9712-5676e2e5a4fd",userId,0,50,leaveQuotasList);
+
+                    if (Objects.nonNull(leaveQuotasList)){
+                        for (Map leaveQuotas : leaveQuotasList) {
+                            if ((long) leaveQuotas.get("start_time") <= currentTime2 && currentTime2 <= (long) leaveQuotas.get("end_time")){
+                                if (Objects.isNull(leaveQuotas.get("quota_num_per_hour")) && Objects.nonNull(leaveQuotas.get("quota_num_per_day"))){
+                                    annualLeaveNum = annualLeaveNum.add(new BigDecimal(String.valueOf(leaveQuotas.get("quota_num_per_day"))).divide(new BigDecimal(100)));
+                                }
+                                if (Objects.nonNull(leaveQuotas.get("used_num_per_day"))){
+                                    annualLeaveNum = annualLeaveNum.subtract(new BigDecimal(String.valueOf(leaveQuotas.get("used_num_per_day"))).divide(new BigDecimal(100)));
+                                }
+                            }
+                        }
+                    }
+
+                    LambdaQueryWrapper<AdvancedLeave> advancedLeaveLambdaQueryWrapper = new LambdaQueryWrapper<>();;
+                    advancedLeaveLambdaQueryWrapper.eq(AdvancedLeave::getUserId,userId)
+                            .eq(AdvancedLeave::getYear,year)
+                            .eq(AdvancedLeave::getValidFlag,"1")
+                            .eq(AdvancedLeave::getCity,"hq");
+                    AdvancedLeave advancedLeave = advancedLeaveMapper.selectOne(advancedLeaveLambdaQueryWrapper);
+                    if (Objects.nonNull(advancedLeave)){
+                        advanceLeave = Double.valueOf(advancedLeave.getLeaveNum());
+                    }
+                    else {
+                        advancedLeave = new AdvancedLeave();
+                        //年假余额大于3时 默认3
+                        if (annualLeaveNum.compareTo(new BigDecimal(3)) > 0){
+                            annualLeaveNum = new BigDecimal(3);
+                        }
+                        advanceLeave = Double.parseDouble(annualLeaveNum.subtract(annualLeaveNum.remainder(new BigDecimal(0.5))).toString());
+                        advancedLeave.setLeaveNum(advanceLeave + "");
+                        advancedLeave.setUserId(userId);
+                        advancedLeave.setYear(String.valueOf(DateUtil.year(new Date())));
+                        advancedLeave.setCity("hq");
+                        advancedLeaveMapper.insert(advancedLeave);
+                    }
+
+                    //年假总数
+                    int realYearLeave =  NumberUtil.sub(yearLeave, -advanceLeave) < 0 ? 0 : (int) (NumberUtil.sub(yearLeave, -advanceLeave) * 100);
+
+                    //获取员工原年假余额 取较大值
+                /*Map balanceMap = new HashMap();
+                balanceMap.put("leave_code",LEAVE_CODE);
+                balanceMap.put("op_userid",opUserId);
+                balanceMap.put("userids",userId);
+                balanceMap.put("offset",0);
+                balanceMap.put("size",10);
+                DDR balanceDdr = (DDR) UtilHttp.doPost("https://oapi.dingtalk.com/topapi/attendance/vacation/quota/list", null, DDConf.initTokenParams(access_token), balanceMap, DDR.class);
+                Map balanceResult = (Map) balanceDdr.getResult();
+                List<Map> leaveQuotas = (List<Map>) balanceResult.get("leave_quotas");
+                if (Objects.nonNull(leaveQuotas) && !leaveQuotas.isEmpty()){
+                    for (Map leaveQuota : leaveQuotas) {
+                        if (year == (int) leaveQuota.get("quota_cycle")){
+                            if (leaveQuota.get("quota_num_per_day") != null){
+                                realYearLeave += (int) leaveQuota.get("quota_num_per_day");
+                            }
+                            if (leaveQuota.get("used_num_per_day") != null){
+                                realYearLeave -= (int) leaveQuota.get("used_num_per_day");
+                            }
+
+                        }
+                    }
+                }*/
+
+                    //更新假期余额接口的body
+//                DateTime deadline = DateUtil.parse((DateUtil.year(new Date()) + 1) + "-03-31");
+                    Map<String,Object> updateBody = new HashMap<>();
+                    Map<String,Object> leave_quotas = new HashMap<>();
+                    //额度有效期开始时间,毫秒级时间戳
+                    leave_quotas.put("start_time",beginDate.getTime());
+                    //额度有效期结束时间,毫秒级时间戳。
+                    leave_quotas.put("end_time",endDate.getTime());
+                    //操作原因
+                    leave_quotas.put("reason","接口测试修改");
+                    //以天计算的额度总数 假期类型按天计算时,该值不为空且按百分之一天折算。 例如:1000=10天。
+                    leave_quotas.put("quota_num_per_day",realYearLeave);
+                    //以小时计算的额度总数 假期类型按小时,计算该值不为空且按百分之一小时折算。例如:1000=10小时。
+                    leave_quotas.put("quota_num_per_hour",0);
+                    //额度所对应的周期,格式必须是"yyyy",例如"2021"
+                    leave_quotas.put("quota_cycle",DateUtil.year(new Date())+"");
+                    //自定义添加的假期类型:年假开发测试的leave_code
+                    leave_quotas.put("leave_code",LEAVE_CODE);
+                    //要更新的员工的userId
+                    leave_quotas.put("userid",userId);
+
+                    updateBody.put("leave_quotas",leave_quotas);
+                    //当前企业内拥有OA审批应用权限的管理员的userId
+                    updateBody.put("op_userid",opUserId);
+
+                    String bodyStr = realYearLeave + userId;
+
+                    // 检查更新事件是否已经处理过,如果是,则忽略该更新
+                    if (isUpdateLeave(bodyStr)) {
+                        log.info("更新事件已处理,忽略该回调...");
+                        continue;
+                    }
+
+                    // 将更新和当前时间戳添加到已处理集合中
+                    long currentTime = System.currentTimeMillis();
+                    bodyList.put(bodyStr, currentTime);
+
+                    Map successMap = new HashMap();
+                    successMap.put(userId,"姓名:"+name+",职级:" + positionLevel+",原职级:" + oldPositionLevel+",入职日期:" + confirmJoinTime + ",升职日期:" + promotionTime + ",年假数:"+ realYearLeave + ",开始日期:" + beginDate+",截止日期:" + endDate);
+                    successList.add(successMap);
+                    //更新假期余额
+                    UtilHttp.doPost("https://oapi.dingtalk.com/topapi/attendance/vacation/quota/update", null, DDConf.initTokenParams(access_token), updateBody, DDR.class);
+
+                }catch (Exception e){
+                    Map errorMap = new HashMap();
+                    errorMap.put(userId,e.getMessage());
+                    errorList.add(errorMap);
+                }
+            }
+        }
+        log.info("更新失败列表:{}",errorList);
+        log.info("更新成功列表:{}",successList);
+        Map result = new HashMap();
+        result.put("errorList",errorList);
+        result.put("successList",successList);
+
+        return result;
+    }
+
+
+    @Override
+    public McR updateEmployeeAnnualLeaveNum() {
+        Map map = new HashMap();
+        map.put("userid_list","344749020127590108,253434204020308091");
+
+        return McR.success(getEmployeeAnnualLeaveNum(map));
+ /*       //获取员工userId集合
+        List<String> userIdList = getEmployeeUserId();
+        //遍历集合给所有员工更新年假余额
+        List<Map> result = new ArrayList<>();
+        if (Objects.nonNull(userIdList) && !userIdList.isEmpty()){
+            Map map = new HashMap();
+            //将userIdList拆分成50长度的多个数组
+            List<List<String>> userIdListList = CollectionUtil.splitList(userIdList,50);
+            for (List<String> userIdList1 : userIdListList) {
+                map.put("userid_list",String.join(",",userIdList1));
+                result.add(getEmployeeAnnualLeaveNum(map));
+            }
+            log.info("result:{}",result);
+
+            for (String userId : userIdList) {
+                Map map = new HashMap();
+                map.put("userid_list",userId);
+                getEmployeeAnnualLeaveNum(map);
+            }
+//        return McR.success(result);
+        }*/
+    }
+
+    @Override
+    public Map getUserLeaveInfo(String userId) {
+        /*LambdaQueryWrapper<AdvancedLeave> advancedLeaveLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        advancedLeaveLambdaQueryWrapper.eq(AdvancedLeave::getUserId,userId)
+                .eq(AdvancedLeave::getYear,String.valueOf(DateUtil.year(new Date())))
+                .eq(AdvancedLeave::getCity,"hq")
+                .eq(AdvancedLeave::getValidFlag,"1");
+        AdvancedLeave advancedLeave = advancedLeaveMapper.selectOne(advancedLeaveLambdaQueryWrapper);*/
+        long currentTime = System.currentTimeMillis();
+
+        Map leaveMap = new HashMap();
+
+        //年假余额
+        BigDecimal annualLeaveNum = new BigDecimal(0.00);
+
+        //查询员工年假余额
+        List<Map> leaveQuotasList = new ArrayList<>();
+        getLeaveNum("5c8a4c95-09e1-4f2d-9712-5676e2e5a4fd",userId,0,50,leaveQuotasList);
+
+        if (Objects.nonNull(leaveQuotasList)){
+            for (Map leaveQuotas : leaveQuotasList) {
+                if ((long) leaveQuotas.get("start_time") <= currentTime && currentTime <= (long) leaveQuotas.get("end_time")){
+                    if (Objects.isNull(leaveQuotas.get("quota_num_per_hour")) && Objects.nonNull(leaveQuotas.get("quota_num_per_day"))){
+                        annualLeaveNum = annualLeaveNum.add(new BigDecimal(String.valueOf(leaveQuotas.get("quota_num_per_day"))).divide(new BigDecimal(100)));
+                   }
+                    if (Objects.nonNull(leaveQuotas.get("used_num_per_day"))){
+                        annualLeaveNum = annualLeaveNum.subtract(new BigDecimal(String.valueOf(leaveQuotas.get("used_num_per_day"))).divide(new BigDecimal(100)));
+                   }
+                }
+            }
+        }
+
+        if (annualLeaveNum.compareTo(new BigDecimal(3)) >= 0){
+            annualLeaveNum = annualLeaveNum.subtract(new BigDecimal(3));
+            leaveMap.put("可预支年假",3);
+            leaveMap.put("实际年假余额",annualLeaveNum);
+            leaveMap.put("已预支年假",0);
+        }else {
+            BigDecimal relYearLeave = annualLeaveNum.remainder(new BigDecimal(0.5));
+            leaveMap.put("可预支年假",annualLeaveNum.subtract(relYearLeave));
+            leaveMap.put("实际年假余额",annualLeaveNum.subtract(new BigDecimal(3)));
+            leaveMap.put("已预支年假",new BigDecimal(3).subtract(annualLeaveNum).add(relYearLeave));
+        }
+
+        //获取员工补休假余额
+        List<Map> leaveQuotasList2 = new ArrayList<>();
+        getLeaveNum("b57e2a2a-b553-4955-b2a2-21c9f42f5420",userId,0,10,leaveQuotasList2);
+
+        //调休假余额
+        BigDecimal compensatoryLeaveNum = new BigDecimal(0.00);
+        if (Objects.nonNull(leaveQuotasList2)){
+            for (Map leaveQuotas : leaveQuotasList2) {
+                if ((long) leaveQuotas.get("start_time") <= currentTime && currentTime <= (long) leaveQuotas.get("end_time")){
+                    if (Objects.isNull(leaveQuotas.get("quota_num_per_day")) && Objects.nonNull(leaveQuotas.get("quota_num_per_hour"))){
+                        compensatoryLeaveNum = compensatoryLeaveNum.add(new BigDecimal(String.valueOf(leaveQuotas.get("quota_num_per_hour"))).divide(new BigDecimal(100)));
+                    }
+                    if (Objects.nonNull(leaveQuotas.get("used_num_per_hour"))){
+                        compensatoryLeaveNum = compensatoryLeaveNum.subtract(new BigDecimal(String.valueOf(leaveQuotas.get("used_num_per_hour"))).divide(new BigDecimal(100)));
+                    }
+                }
+            }
+        }
+        if (compensatoryLeaveNum.compareTo(new BigDecimal(64)) >= 0){
+            leaveMap.put("可预支调休",64);
+            leaveMap.put("实际加班小时数",compensatoryLeaveNum.subtract(new BigDecimal(64)));
+            leaveMap.put("已预支调休",0);
+        }else {
+            leaveMap.put("可预支调休",compensatoryLeaveNum);
+            leaveMap.put("实际加班小时数",0);
+            leaveMap.put("已预支调休",new BigDecimal(64).subtract(compensatoryLeaveNum));
+        }
+
+        return leaveMap;
+    }
+
+    private List<Map> getLeaveNum(String leave_code,String userId,int offset,int size,List<Map> leaveQuotasList) {
+        String access_token = ddClient.getAccessToken(appKey,appSecret);
+
+        Map body = new HashMap();
+        body.put("leave_code",leave_code);
+        body.put("op_userid",opUserId);
+        body.put("userids",userId);
+        body.put("offset",offset);
+        body.put("size",size);
+
+        DDR_New ddrNew = (DDR_New) UtilHttp.doPost("https://oapi.dingtalk.com/topapi/attendance/vacation/quota/list", null, DDConf.initTokenParams(access_token), body, DDR_New.class);
+        Map result = (Map) ddrNew.getResult();
+
+        if (Objects.nonNull(result.get("leave_quotas"))){
+            leaveQuotasList.addAll((List<Map>) result.get("leave_quotas"));
+        }
+
+        if ((boolean) result.get("has_more")){
+            getLeaveNum(leave_code,userId,offset+size,size,leaveQuotasList);
+        }
+        return leaveQuotasList;
+    }
+
+    private int getAnnualLeaveBaseNum(String positionLevel) {
+        int annualLeave = 0;
+        if (positionLevel.equals("LC")){
+            annualLeave = 20;
+        }else if (positionLevel.equals("DH") || positionLevel.equals("MGR")){
+            annualLeave = 12;
+        }else if (positionLevel.equals("TL-AM") || positionLevel.equals("TL")){
+            annualLeave = 10;
+        }else if (positionLevel.equals("GSS")){
+            annualLeave = 8;
+        }else if (positionLevel.equals("T")){
+            annualLeave = 0;
+        }
+        return annualLeave;
+    }
+
+    /**
+     * 检查该更新事件在5s内是否处理过,应对钉钉瞬间重复回调
+     *
+     * @param msg 回调事件
+     * @return 是否处理过
+     */
+    private boolean isUpdateLeave(String msg) {
+        // 清理超过5s的回调事件
+        long currentTime = System.currentTimeMillis();
+        long expirationTime = currentTime - TimeUnit.SECONDS.toMillis(60);
+        bodyList.entrySet().removeIf(entry -> entry.getValue() < expirationTime);
+
+        return bodyList.containsKey(msg);
+    }
+
+}

+ 186 - 31
mjava-kaiyue/src/main/java/com/malk/kaiyue/service/impl/KYYTServiceImpl.java

@@ -2,6 +2,9 @@ package com.malk.kaiyue.service.impl;
 
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.malk.kaiyue.entity.AdvancedLeave;
+import com.malk.kaiyue.mapper.AdvancedLeaveMapper;
 import com.malk.kaiyue.service.KYYTService;
 import com.malk.server.common.McR;
 import com.malk.server.dingtalk.DDConf;
@@ -29,6 +32,9 @@ public class KYYTServiceImpl implements KYYTService {
     @Autowired
     private DDConf ddConf;
 
+    @Autowired
+    private AdvancedLeaveMapper advancedLeaveMapper;
+
     @Value("${dingtalk_yt.appKey}")
     private String appKey;
 
@@ -42,8 +48,10 @@ public class KYYTServiceImpl implements KYYTService {
     private String opUserId;
 
 
-    //成都凯悦-年假(测试)
-    private static final String LEAVE_CODE = "28abb7bf-e1b6-4387-9e2a-e1b2ae983e7a";
+    //烟台凯悦-年假(测试)
+//    private static final String LEAVE_CODE = "28abb7bf-e1b6-4387-9e2a-e1b2ae983e7a";
+    //年假
+    private static final String LEAVE_CODE = "3dcd4b8c-1b88-4206-9b52-cc9e2fde2e38";
     //体验社-年假测试wzy
 //    private static final String LEAVE_CODE = "f9240c02-8fe7-4535-af2c-ca6740e1c654";
 
@@ -127,10 +135,17 @@ public class KYYTServiceImpl implements KYYTService {
                 String positionLevel = "";
                 //原职级
                 String oldPositionLevel = "";
+                //升职日期
+                String promotionTime = "";
                 //姓名
                 String name = "";
                 //合同续签次数
                 int contractRenewCount = 0;
+                //现合同开始日期
+                String contractStartTime = "";
+                //员工类型
+                String employeeType = "";
+
                 List<Map> fieldDataList = (List<Map>) data.get("field_data_list");
                 for (Map fieldData : fieldDataList) {
                     String fieldCode = fieldData.get("field_code").toString();
@@ -144,18 +159,32 @@ public class KYYTServiceImpl implements KYYTService {
                             case "2a8b3a12-5102-4e60-b5d2-5f3bca0f0b7c": oldPositionLevel = value;break;
                             case "sys00-name": name = value;break;
                             case "sys05-contractRenewCount": contractRenewCount = Integer.valueOf(value);break;
+                            case "sys01-employeeType": employeeType = value;break;
+                            case "sys05-nowContractStartTime": contractStartTime = value;break;
+                            case "sys01-promotionTime": promotionTime = value;break;
                             default:break;
                         }
-                    }else {
-                        log.info("更新员工年假余额:参数缺啦!");
-                        return McR.errorParam("参数缺啦!");
                     }
 
                 }
+
                 //若没有原职级 则默认原职级是现职级
                 if ("".equals(oldPositionLevel)){
                     oldPositionLevel = positionLevel;
                 }
+                //若没有升职日期 则默认升职日期是入职日期
+                if ("".equals(promotionTime)){
+                    promotionTime = confirmJoinTime;
+                }
+                //若没有现合同开始日期 则默认现合同开始日期是入职日期
+                if ("".equals(contractStartTime)){
+                    contractStartTime = confirmJoinTime;
+                }
+
+                if ("".equals(confirmJoinTime) || "".equals(positionLevel) || "".equals(name)){
+                    log.info("更新员工userid:{} 参数缺失!", userId);
+                    continue;
+                }
 
                 //假期有效开始日期为当年1月1日
                 DateTime beginDate = DateUtil.beginOfYear(new Date());
@@ -164,10 +193,20 @@ public class KYYTServiceImpl implements KYYTService {
                 //当年天数
                 int yearDays = DateUtil.dayOfYear(endDate);
 
+                //判断员工是否当年新入职
+                if (DateUtil.year(DateUtil.parse(confirmJoinTime)) == DateUtil.year(new Date())){
+                    beginDate = DateUtil.parse(confirmJoinTime);
+                }
+
                 //工龄(年) 计算规则:首次工作时间至当年一月一日 数值向下取整
-                int workAge =(int) (DateUtil.betweenYear(DateUtil.parse(joinWorkingTime), beginDate, true));
-                if (DateUtil.dayOfYear(DateUtil.parse(joinWorkingTime)) != 1){
-                    workAge --;
+                int workAge = 0;
+                if (joinWorkingTime.equals("")){
+                    workAge = 0;
+                }else {
+                    workAge =(int) (DateUtil.betweenYear(DateUtil.parse(joinWorkingTime), beginDate, true));
+                    if (DateUtil.dayOfYear(DateUtil.parse(joinWorkingTime)) != 1 && workAge != 0){
+                        workAge --;
+                    }
                 }
 
                 System.out.println("截至今年1月1日,工龄为:"+workAge + "年");
@@ -176,34 +215,105 @@ public class KYYTServiceImpl implements KYYTService {
                 //预支年假
                 double advanceLeave = 8.0;
 
-                int yearLeaveBaseNum = getAnnualLeaveBaseNum(positionLevel,workAge,contractRenewCount);
-                int oldYearLeaveBaseNum = getAnnualLeaveBaseNum(oldPositionLevel,workAge,contractRenewCount);
-                long day1 = 0;
-                long day2 = 0;
+                Date today = new Date();
 
-                //判断员工是否当年新入职
-                if (DateUtil.year(DateUtil.parse(confirmJoinTime)) == DateUtil.year(new Date())){
-                    day1 = DateUtil.betweenDay(DateUtil.parse(confirmJoinTime),new Date(),true);
-                    day2 = DateUtil.betweenDay(new Date(),endDate,true);
+                int year = DateUtil.year(new Date());
+
+                if (employeeType.equals("退休返聘")){
+                    yearLeave = 5.0;
                 }else {
-                    day1 = DateUtil.betweenDay(beginDate,new Date(),true);
-                    day2 = DateUtil.betweenDay(new Date(),endDate,true);
-                }
-                yearLeave = (double) (day1 * oldYearLeaveBaseNum + day2 * yearLeaveBaseNum) / yearDays;
-                double yearLeaveDecimalPart = yearLeave - (int) yearLeave;
+                    int positionLevelBaseNum = getAnnualLeaveBaseNum(positionLevel,workAge,contractRenewCount);
+                    int oldPositionLevelBaseNum = getAnnualLeaveBaseNum(oldPositionLevel,workAge,contractRenewCount);
+
+                    boolean flag1 = false;
+                    boolean flag2 = false;
+                    //判断升职日期是否是今年
+                    if (DateUtil.year(DateUtil.parse(promotionTime)) == year){
+                        flag1 = true;
+                    }
+                    //判断合同开始日期是否在今年
+                    if (DateUtil.year(DateUtil.parse(contractStartTime)) == year){
+                        flag2 = true;
+                    }
+                    //上期合同续签数附加年假数
+                    int lastAdditiveYearLeave = Math.min(Math.max(contractRenewCount - 1, 0), 2) * 2;
+                    //合同续签数附加年假数
+                    int additiveYearLeave = Math.min(contractRenewCount, 2) * 2;
+                    if (!flag1 && !flag2){
+                        long day1 = DateUtil.betweenDay(beginDate, endDate, true);
+                        yearLeave = (double) (day1 * (positionLevelBaseNum + additiveYearLeave)) / yearDays ;
+                        log.info("day1:{},yearLeave:{}", day1, yearLeave);
+                    }
+                    if (flag1 && !flag2){
+                        //获取假期有效开始日期到升职日的天数
+                        long day1 = DateUtil.betweenDay(beginDate, DateUtil.parse(promotionTime), true);
+                        //获取升职日到假期截止时间的天数
+                        long day2 = DateUtil.betweenDay(DateUtil.parse(promotionTime), endDate, true) + 1;
+                        //分两段计算:假期有效开始日期-现升职日,现升职日到假期截止时间
+                        yearLeave = (double) (day1 * (oldPositionLevelBaseNum + additiveYearLeave) + day2 * (positionLevelBaseNum + additiveYearLeave)) / yearDays ;
+                        log.info("day1:{},day2:{},yearLeave:{}", day1, day2, yearLeave);
+                    }
+                    if (!flag1 && flag2){
+                        //获取假期有效开始日期到现合同开始日的天数
+                        long day1 = DateUtil.betweenDay(beginDate, DateUtil.parse(contractStartTime), true);
+                        //获取现合同开始日到今天的天数
+                        long day2 = DateUtil.betweenDay(DateUtil.parse(contractStartTime), endDate, true) + 1;
+                        //分两段计算:假期有效开始日期-现合同开始日期,现合同开始日期到假期截止时间
+                        yearLeave = (double) (day1 * (positionLevelBaseNum + lastAdditiveYearLeave) + day2 * (positionLevelBaseNum + additiveYearLeave)) / yearDays;
+                        log.info("day1:{},day2:{},yearLeave:{}", day1, day2, yearLeave);
+                    }
+                    if (flag1 && flag2){
+                        //判断升职日期与现合同开始日期先后
+                        //升职日期在现合同开始日期之前
+                        if (DateUtil.compare(DateUtil.parse(contractStartTime), DateUtil.parse(promotionTime)) >= 0){
+                            //获取假期有效开始日期到升职日的天数
+                            long day1 = DateUtil.betweenDay(beginDate, DateUtil.parse(promotionTime), true);
+                            //获升职值日到现合同开始日期的天数
+                            long day2 = DateUtil.betweenDay(DateUtil.parse(promotionTime), DateUtil.parse(contractStartTime), true);
+                            //获取现合同开始日期到假期截止时间的天数
+                            long day3 = DateUtil.betweenDay(DateUtil.parse(contractStartTime), endDate, true) + 1;
+                            //分三段计算:假期有效开始日期-现升职日,现职日到现现合同开始日期,现合同开始日期到假期截止时间
+                            yearLeave = (double) (day1 * (oldPositionLevelBaseNum + lastAdditiveYearLeave) + day2 * (positionLevelBaseNum + lastAdditiveYearLeave) + day3 * (positionLevelBaseNum + additiveYearLeave)) / yearDays ;
+                            log.info("day1:{},day2:{},day3:{},yearLeave:{}", day1, day2, day3, yearLeave);
+                        }else {
+                            //升职日在现合同开始日期之后
+                            //获取假期有效开始日期到现合同开始日的天数
+                            long day1 = DateUtil.betweenDay(beginDate, DateUtil.parse(contractStartTime), true);
+                            //获取现合同开始日到升职日的天数
+                            long day2 = DateUtil.betweenDay(DateUtil.parse(contractStartTime), DateUtil.parse(promotionTime), true);
+                            //获取升职日到假期截止时间的天数
+                            long day3 = DateUtil.betweenDay(DateUtil.parse(promotionTime), endDate, true) + 1;
+                            //分三段计算:假期有效开始日期-现合同开始日期,现合同开始日期到升职日,升职日到假期截止时间
+                            yearLeave = (double) (day1 * (oldPositionLevelBaseNum + lastAdditiveYearLeave) + day2 * (oldPositionLevelBaseNum + additiveYearLeave) + day3 * (positionLevelBaseNum + additiveYearLeave)) / yearDays;
+                            log.info("day1:{},day2:{},day3:{},yearLeave:{}", day1, day2, day3, yearLeave);}
+                    }
 
-                result.add("姓名:"+name+",职级:"+positionLevel+",工龄:"+workAge+"年,合同续签数"+contractRenewCount+",年假数:" + yearLeave + "天"+",截止日期:"+endDate);
+                    /*//判断员工是否当年新入职
+                    if (DateUtil.year(DateUtil.parse(confirmJoinTime)) == DateUtil.year(today)){
+                        day1 = DateUtil.betweenDay(DateUtil.parse(confirmJoinTime),new Date(),true);
+                        day2 = DateUtil.betweenDay(today,endDate,true);
+                    }else {
+                        day1 = DateUtil.betweenDay(beginDate,today,true);
+                        day2 = DateUtil.betweenDay(today,endDate,true);
+                    }
+                    yearLeave = (double) (day1 * oldYearLeaveBaseNum + day2 * yearLeaveBaseNum) / yearDays;
+                    */
 
-                if (yearLeaveDecimalPart < 0.25){
-                    yearLeave = (int) yearLeave;
-                }else if (yearLeaveDecimalPart < 0.75){
-                    yearLeave = (int) yearLeave + 0.5;
-                }else if (yearLeaveDecimalPart < 1){
-                    yearLeave = (int) yearLeave + 1;
+                    double yearLeaveDecimalPart = yearLeave - (int) yearLeave;
+
+                    result.add("姓名:"+name+",职级:"+positionLevel+",工龄:"+workAge+"年,合同续签数"+contractRenewCount+",年假数:" + yearLeave + "天"+",截止日期:"+endDate);
+
+                    if (yearLeaveDecimalPart < 0.25){
+                        yearLeave = (int) yearLeave;
+                    }else if (yearLeaveDecimalPart < 0.75){
+                        yearLeave = (int) yearLeave + 0.5;
+                    }else if (yearLeaveDecimalPart < 1){
+                        yearLeave = (int) yearLeave + 1;
+                    }
                 }
 
                 //查询出用户消费年假记录 当返回leaveRecords中calType为null或不返回该字段则为请假消耗 将计算出的年假数减去请假消耗的数量
-                Map body = new HashMap();
+                /*Map body = new HashMap();
                 body.put("opUserId",opUserId);
                 body.put("leaveCode",LEAVE_CODE);
                 body.put("userIds",new String[]{userId});
@@ -237,10 +347,55 @@ public class KYYTServiceImpl implements KYYTService {
                             }
                         }
                     }
+                }*/
+
+                //年假余额
+                BigDecimal annualLeaveNum = new BigDecimal(0.00);
+
+                //查询员工年假余额
+                long currentTime2 = System.currentTimeMillis();
+
+                List<Map> leaveQuotasList = new ArrayList<>();
+                getLeaveNum(LEAVE_CODE,userId,0,50,leaveQuotasList);
+
+                if (Objects.nonNull(leaveQuotasList)){
+                    for (Map leaveQuotas : leaveQuotasList) {
+                        if ((long) leaveQuotas.get("start_time") <= currentTime2 && currentTime2 <= (long) leaveQuotas.get("end_time")){
+                            if (Objects.isNull(leaveQuotas.get("quota_num_per_hour")) && Objects.nonNull(leaveQuotas.get("quota_num_per_day"))){
+                                annualLeaveNum = annualLeaveNum.add(new BigDecimal(String.valueOf(leaveQuotas.get("quota_num_per_day"))).divide(new BigDecimal(100)));
+                            }
+                            if (Objects.nonNull(leaveQuotas.get("used_num_per_day"))){
+                                annualLeaveNum = annualLeaveNum.subtract(new BigDecimal(String.valueOf(leaveQuotas.get("used_num_per_day"))).divide(new BigDecimal(100)));
+                            }
+                        }
+                    }
+                }
+
+                LambdaQueryWrapper<AdvancedLeave> advancedLeaveLambdaQueryWrapper = new LambdaQueryWrapper<>();;
+                advancedLeaveLambdaQueryWrapper.eq(AdvancedLeave::getUserId,userId)
+                        .eq(AdvancedLeave::getYear,year)
+                        .eq(AdvancedLeave::getValidFlag,"1")
+                        .eq(AdvancedLeave::getCity,"yt");
+                AdvancedLeave advancedLeave = advancedLeaveMapper.selectOne(advancedLeaveLambdaQueryWrapper);
+                if (Objects.nonNull(advancedLeave)){
+                    advanceLeave = Double.valueOf(advancedLeave.getLeaveNum());
+                }
+                else {
+                    advancedLeave = new AdvancedLeave();
+                    //年假余额大于8时 默认8
+                    if (annualLeaveNum.compareTo(new BigDecimal(8)) > 0){
+                        annualLeaveNum = new BigDecimal(8);
+                    }
+                    advanceLeave = Double.parseDouble(annualLeaveNum.subtract(annualLeaveNum.remainder(new BigDecimal(0.5))).toString());
+                    advancedLeave.setLeaveNum(advanceLeave + "");
+                    advancedLeave.setUserId(userId);
+                    advancedLeave.setYear(String.valueOf(DateUtil.year(new Date())));
+                    advancedLeave.setCity("yt");
+                    advancedLeaveMapper.insert(advancedLeave);
                 }
 
                 //实际年假数
-                double realYearLeave = (yearLeave - useLeaveNum + advanceLeave) < 0 ? 0 : (yearLeave - useLeaveNum + advanceLeave);
+                double realYearLeave = (yearLeave + advanceLeave) < 0 ? 0 : (yearLeave + advanceLeave);
 
                 //更新假期余额接口的body
                 Map<String,Object> updateBody = new HashMap<>();
@@ -250,7 +405,7 @@ public class KYYTServiceImpl implements KYYTService {
                 //额度有效期结束时间,毫秒级时间戳。
                 leave_quotas.put("end_time",endDate.getTime());
                 //操作原因
-                leave_quotas.put("reason","接口测试修改");
+                leave_quotas.put("reason","接口年假发放");
                 //以天计算的额度总数 假期类型按天计算时,该值不为空且按百分之一天折算。 例如:1000=10天。
                 leave_quotas.put("quota_num_per_day",(int) (realYearLeave * 100) );
                 //以小时计算的额度总数 假期类型按小时,计算该值不为空且按百分之一小时折算。例如:1000=10小时。

+ 20 - 3
mjava-kaiyue/src/main/resources/application-dev.yml

@@ -1,14 +1,18 @@
 # 环境配置
 server:
-  port: 9002
+  port: 9021
   servlet:
     context-path: /api/kaiyue
 
 # condition
 spel:
-  scheduling: false        # 定时任务是否执行
+  scheduling: false        # 定时任务是否执行w
   multiSource: false       # 是否多数据源配置
 
+devtools:
+  restart:
+    enabled: false
+
 spring:
   # database
   datasource:
@@ -56,6 +60,9 @@ file:
 logging:
   file:
     path: /Users/malk/server/_Tool/var/mjava/log
+  level:
+    com.malk.*: debug
+
 
 #高级假期
 #agentId: 3078222540
@@ -94,4 +101,14 @@ dingtalk_yt:
   corpId: ding43bb7be8e7bdc63224f2f5cc6abecb85
   aesKey: 7txhFmSyWIXIrEvwlNfcuMfOQe19K6hqCdIaXMHLO2K
   token: 24VR2Bnu
-  operator: "02421908021891243060"   # OA管理员账号 [0开头需要转一下字符串]
+  operator: "2534342040-496999405"   # OA管理员账号 [0开头需要转一下字符串]
+
+# dingtalk-hq
+dingtalk_hq:
+  agentId: 3213052281
+  appKey: dingtn8laxhycpzsq0tw
+  appSecret: NzXtpILrzOL6pTqVwQDAlZzdCbacm7rBPO-WCJh-qPxjw9RZfUIp9R9YcqU2hj73
+  corpId: ding0eac343305c5c09935c2f4657eb6378f
+  aesKey: fI8d5nU9EdAFRMv7MJDMJFrhpkPLrFUVYjpmPEhIfPD
+  token: cdclTjeXBL3MGhlxu9cD1VSQUTeCulxFLCYqkJuC4s
+  operator: "344749020127590108"   # OA管理员账号 [0开头需要转一下字符串]

+ 1 - 1
mjava-kaiyue/src/main/resources/application.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: prod
+    active: dev