diff --git a/evo-admin/pom.xml b/evo-admin/pom.xml
index 170d4fc..dbd11ca 100644
--- a/evo-admin/pom.xml
+++ b/evo-admin/pom.xml
@@ -17,6 +17,26 @@
+
+ com.github.ulisesbocchio
+ jasypt-spring-boot-starter
+ 3.0.5
+
+
+
+
+
+ com.aliyun
+ dingtalk
+ 2.2.43
+
+
+
+ com.aliyun
+ alibaba-dingtalk-service-sdk
+ 2.0.0
+
+
com.baomidou
mybatis-plus-boot-starter
@@ -223,7 +243,6 @@
system
${project.basedir}/src/main/resources/lib/k3cloud-webapi-sdk-java11-v8.2.0.jar
-
com.google.code.gson
gson
@@ -241,6 +260,12 @@
org.springframework.boot
spring-boot-starter-thymeleaf
+
+ org.springframework.boot
+ spring-boot-test
+ 3.3.3
+ test
+
diff --git a/evo-admin/src/main/java/com/evo/EvoApplication.java b/evo-admin/src/main/java/com/evo/EvoApplication.java
index d9284f8..d39027e 100644
--- a/evo-admin/src/main/java/com/evo/EvoApplication.java
+++ b/evo-admin/src/main/java/com/evo/EvoApplication.java
@@ -4,6 +4,7 @@ import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
@@ -14,6 +15,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@EnableScheduling
@MapperScan({"com.evo.**.mapper.**.**"})
+@EnableAsync // 开启异步支持
public class EvoApplication
{
public static void main(String[] args)
diff --git a/evo-admin/src/main/java/com/evo/attendance/controller/RzAttendanceController.java b/evo-admin/src/main/java/com/evo/attendance/controller/RzAttendanceController.java
index 2ed910c..c4dde46 100644
--- a/evo-admin/src/main/java/com/evo/attendance/controller/RzAttendanceController.java
+++ b/evo-admin/src/main/java/com/evo/attendance/controller/RzAttendanceController.java
@@ -125,4 +125,17 @@ public class RzAttendanceController extends BaseController
{
return success(rzAttendanceDetailService.selectListByAttendanceId(id));
}
+
+ @Log(title = "考勤记录重新抓取", businessType = BusinessType.UPDATE)
+ @GetMapping(value = "/load/{id}")
+ public AjaxResult loadAttendance(@PathVariable("id") Long id){
+ return success(rzAttendanceService.loadAttendance(id));
+ }
+
+ @Log(title = "考勤记录全部重新抓取", businessType = BusinessType.UPDATE)
+ @PutMapping(value = "/load/all")
+ public AjaxResult loadAllAttendance(@RequestBody RzAttendance rzAttendance)
+ {
+ return rzAttendanceService.loadAllAttendance(rzAttendance);
+ }
}
diff --git a/evo-admin/src/main/java/com/evo/attendance/domain/RzAttendance.java b/evo-admin/src/main/java/com/evo/attendance/domain/RzAttendance.java
index 4644bbc..995421d 100644
--- a/evo-admin/src/main/java/com/evo/attendance/domain/RzAttendance.java
+++ b/evo-admin/src/main/java/com/evo/attendance/domain/RzAttendance.java
@@ -120,5 +120,9 @@ public class RzAttendance extends BaseEntity {
private Date startTime;
@TableField(exist = false)
private Date endTime;
+ /***
+ * 补卡扣款
+ */
+ private BigDecimal debiting;
}
diff --git a/evo-admin/src/main/java/com/evo/attendance/processor/PunchTheClockStrategyExchangeProcessor.java b/evo-admin/src/main/java/com/evo/attendance/processor/PunchTheClockStrategyExchangeProcessor.java
index 2112b4c..e2b55c6 100644
--- a/evo-admin/src/main/java/com/evo/attendance/processor/PunchTheClockStrategyExchangeProcessor.java
+++ b/evo-admin/src/main/java/com/evo/attendance/processor/PunchTheClockStrategyExchangeProcessor.java
@@ -76,7 +76,14 @@ public interface PunchTheClockStrategyExchangeProcessor {
return "";
}
- default String initMessage(Integer result, String meg){
+
+
+ default String initMessage(Integer result, String meg){
+ //需要返回的对象, 默认失败
+ return initStaticMessage(result, meg);
+ }
+
+ static String initStaticMessage(Integer result, String meg){
//需要返回的对象, 默认失败
return JSONObject.toJSONString(new RzAttendanceVo(result, meg, null));
}
diff --git a/evo-admin/src/main/java/com/evo/attendance/processor/impl/KQDeviceExchangeProcessor.java b/evo-admin/src/main/java/com/evo/attendance/processor/impl/KQDeviceExchangeProcessor.java
index cc48fe5..dc2d716 100644
--- a/evo-admin/src/main/java/com/evo/attendance/processor/impl/KQDeviceExchangeProcessor.java
+++ b/evo-admin/src/main/java/com/evo/attendance/processor/impl/KQDeviceExchangeProcessor.java
@@ -150,7 +150,6 @@ public class KQDeviceExchangeProcessor implements PunchTheClockStrategyExchangeP
return initMessage(1, "当天已经打过加班卡");
}
-
//员工考勤记录
RzAttendance attendance = rzAttendanceMapper.queryNowDayAttendanceByStatisticalIdAndDate(Long.valueOf(userId),date);
//如果当前打卡是下班卡, 并且当前员工考勤上班时间为空, 则判定为隔天打下班卡
diff --git a/evo-admin/src/main/java/com/evo/attendance/service/IRzAttendanceService.java b/evo-admin/src/main/java/com/evo/attendance/service/IRzAttendanceService.java
index 34938e6..8ffc453 100644
--- a/evo-admin/src/main/java/com/evo/attendance/service/IRzAttendanceService.java
+++ b/evo-admin/src/main/java/com/evo/attendance/service/IRzAttendanceService.java
@@ -1,9 +1,12 @@
package com.evo.attendance.service;
+import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.IService;
+import com.evo.attendance.domain.RzAttendance;
import com.evo.attendance.domain.vo.RzAttendanceDetailVO;
import com.evo.common.core.domain.AjaxResult;
-import com.evo.attendance.domain.RzAttendance;
+import com.evo.ding.domain.DingShiftGroup;
+import com.evo.system.domain.SysStaff;
import java.util.Date;
import java.util.List;
@@ -62,4 +65,15 @@ public interface IRzAttendanceService extends IService
RzAttendance selectRzAttendanceBySfIdAndNameAndTime(Long userId, String employeeName, String replacementTime,Boolean isAttendance);
public void sendAbnormalAttendance();
+
+ public void dingDing(String dingUserId, String bizId, Long checkTime);
+
+ void addRzOverTime(String result, SysStaff sysStaff, DingShiftGroup dingsShiftGroup, String checkType, Date formatWorkDate, Date formatUserCheckTime, String timeResult, String remark, Double debiting, RzAttendance attendance);
+
+ public RzAttendance queryNowDayAttendanceByStatisticalIdAndDate(Long staffId, Date date);
+
+ public RzAttendance loadAttendance(Long id);
+
+ public AjaxResult loadAllAttendance(RzAttendance rzAttendance);
+
}
diff --git a/evo-admin/src/main/java/com/evo/attendance/service/IRzSpecialAttendanceService.java b/evo-admin/src/main/java/com/evo/attendance/service/IRzSpecialAttendanceService.java
index 0e9a2da..7be6ba1 100644
--- a/evo-admin/src/main/java/com/evo/attendance/service/IRzSpecialAttendanceService.java
+++ b/evo-admin/src/main/java/com/evo/attendance/service/IRzSpecialAttendanceService.java
@@ -1,7 +1,10 @@
package com.evo.attendance.service;
+import com.baomidou.mybatisplus.extension.service.IService;
import com.evo.attendance.domain.RzSpecialAttendance;
+import org.apache.ibatis.annotations.Param;
+import java.util.Date;
import java.util.List;
/**
@@ -10,7 +13,7 @@ import java.util.List;
* @author evo
* @date 2025-04-15
*/
-public interface IRzSpecialAttendanceService
+public interface IRzSpecialAttendanceService extends IService
{
/**
* 查询加班考勤记录
@@ -51,4 +54,6 @@ public interface IRzSpecialAttendanceService
* @return 结果
*/
public int deleteRzSpecialAttendanceById(Long id);
+
+ public RzSpecialAttendance selectRzSpecialAttendanceByUserIdAndDate(Long staffId, Date date);
}
diff --git a/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceDetailServiceImpl.java b/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceDetailServiceImpl.java
index 1beca89..bf6a8a4 100644
--- a/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceDetailServiceImpl.java
+++ b/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceDetailServiceImpl.java
@@ -141,11 +141,6 @@ public class RzAttendanceDetailServiceImpl extends ServiceImpl().eq(RzAttendanceDetail::getAttendanceId, attId)
-// .select(RzAttendanceDetail::getButtonType, RzAttendanceDetail::getPhoto, RzAttendanceDetail::getDateTime,RzAttendanceDetail::getDelFlag)).stream().map(data ->{
-// return new RzAttendanceDetailTimeLineVO(data.getButtonType(), data.getPhoto(), DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss",data.getDateTime()), data.getDelFlag());
-// }).collect(Collectors.toList());
}
SimpleDateFormat sdfd = new SimpleDateFormat("yyyy-MM-dd");
diff --git a/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceServiceImpl.java b/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceServiceImpl.java
index 4b69aef..65c4a19 100644
--- a/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceServiceImpl.java
+++ b/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceServiceImpl.java
@@ -1,5 +1,8 @@
package com.evo.attendance.service.impl;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -13,11 +16,15 @@ import com.evo.attendance.service.RzAttendanceDetailService;
import com.evo.common.annotation.DataScope;
import com.evo.common.core.domain.AjaxResult;
import com.evo.common.core.domain.entity.SysDept;
-import com.evo.common.utils.Collections;
-import com.evo.common.utils.DateUtils;
-import com.evo.common.utils.SecurityUtils;
-import com.evo.common.utils.StringUtils;
+import com.evo.common.handler.thread.RzAttendanceBatchHandler;
+import com.evo.common.utils.*;
+import com.evo.ding.DingUtils;
+import com.evo.ding.domain.DingShift;
+import com.evo.ding.domain.DingShiftGroup;
+import com.evo.ding.mapper.DingShiftGroupMapper;
+import com.evo.ding.mapper.DingShiftMapper;
import com.evo.equipment.service.IEqImagesService;
+import com.evo.personnelMatters.service.IRzOverTimeService;
import com.evo.system.domain.SysStaff;
import com.evo.system.mapper.SysDeptMapper;
import com.evo.system.mapper.SysStaffMapper;
@@ -31,6 +38,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
+import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -56,6 +64,13 @@ public class RzAttendanceServiceImpl extends ServiceImpl"+result);
+ JSONObject resultJson = JSON.parseObject(result);
+ if(Integer.valueOf(0).equals(resultJson.getInteger("errcode")))
+ try {
+ {
+ JSONArray jsonArray = resultJson.getJSONArray("recordresult");
+ for (int i = 0; i < jsonArray.size(); i++) {
+ JSONObject ob = jsonArray.getJSONObject(i);
+ if(bizId.equals(ob.getString("bizId"))){
+ calculationAddRzOverTime(result, sysStaff, ob, null);
+// //打卡类型
+// String checkType = ob.getString("checkType");
+// //考勤组
+// String groupId = ob.getString("groupId");
+// //排班时间
+// Long planCheckTime = ob.getLong("planCheckTime");
+// // 格式化:先转Date对象,再格式化
+// String formatPlanCheckTime = DateUtils.parseDateToStr("HH:mm:ss", new Date(planCheckTime));
+// //用户打卡时间
+// Long userCheckTime = ob.getLong("userCheckTime");
+// //工作时间 (这里指的是哪一天的卡)
+// Long workDate = ob.getLong("workDate");
+// /***
+// * 打卡结果
+// * Normal:正常
+// * Early:早退
+// * Late:迟到
+// * SeriousLate:严重迟到
+// * Absenteeism:旷工迟到
+// * NotSigned:未打卡
+// */
+// String timeResult = ob.getString("timeResult");
+//
+// Date formatWorkDate = Date.from(Instant.ofEpochMilli(workDate));
+// Date formatUserCheckTime = Date.from(Instant.ofEpochMilli(userCheckTime));
+// DingShiftGroup dingsShiftGroup = dingsShiftGroupMapper.selectByGroupIdAndTypeAndCheckTime(groupId, checkType, formatPlanCheckTime);
+// addRzOverTime(result, sysStaff, dingsShiftGroup, checkType, formatWorkDate, formatUserCheckTime, timeResult, null, 0d);
+ break;
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ log.error("处理钉钉打卡出现异常, 异常原因是==>>{}, 打卡结果是=====>>>>> {}", e.getMessage(), result);
+ }
+ }
+ }
+ @Override
+ public void addRzOverTime(String result, SysStaff sysStaff, DingShiftGroup dingsShiftGroup, String checkType, Date formatWorkDate, Date formatUserCheckTime, String timeResult, String remark, Double debiting,RzAttendance attendance){
+ if(dingsShiftGroup != null){
+ //通过打卡类型, classId, 和排班打卡时间, 获取工作时长
+ //通过班次id拿到工作时长
+ DingShift dingsShift = dingsShiftMapper.selectByDingId(dingsShiftGroup.getShiftId());
+ //通过workDate 获取哪一天的卡
+ if(attendance == null){
+ attendance = queryNowDayAttendanceByStatisticalIdAndDate(sysStaff.getUserId(),formatWorkDate);
+ }
+ //生成打卡记录
+ rzAttendanceDetailService.addDetail(attendance, ("OnDuty".equals(checkType) ? "上班卡" : "下班卡"), "/", formatUserCheckTime, StringUtils.isEmpty(remark) ? "钉钉打卡" : remark, "");
+ //上班
+ if("OnDuty".equals(checkType)){
+ attendance.setRules("上班卡");
+ attendance.setWorkStartTime(formatUserCheckTime);
+ if(!"Normal".equals(timeResult)){
+ attendance.setYcsFlag("1");
+ }
+ }else if("OffDuty".equals(checkType)){
+ attendance.setWorkEndTime(formatUserCheckTime);
+ if("Normal".equals(timeResult) && !"1".equals(attendance.getYcsFlag()) && attendance.getWorkStartTime() != null){
+ attendance.setWorkSum(BigDecimal.valueOf(dingsShift.getWorkHour()));
+ }else{
+ //早退需要计算工时
+ if(attendance.getWorkStartTime() != null){
+ Long hours = (attendance.getWorkEndTime().getTime() - attendance.getWorkStartTime().getTime())/1000/60/60;
+ attendance.setWorkSum((BigDecimal.valueOf(dingsShift.getWorkHour()).compareTo(BigDecimal.valueOf(hours)) < 0) ? BigDecimal.valueOf(dingsShift.getWorkHour()) : BigDecimal.valueOf(hours));
+ }else {
+ attendance.setWorkSum(DataUtils.DEFAULT_VALUE);
+ }
+ if(!"Normal".equals(timeResult)){
+ attendance.setYcxFlag("1");
+ }
+ }
+ }else{
+ log.error("钉钉打卡=====>>>>>>>>>>>>>>>>>> 出现未知打卡类型, 当前打卡类型");
+ //终止继续执行
+ return;
+ }
+ //判断打卡时间, 添加夜班次数 打卡时间在晚上7点以后
+ if(dingsShift.getWorkHour() == 12 && attendance.getWorkSum().intValue() >= 12 && attendance.getWorkStartTime()!= null && attendance.getWorkStartTime().getHours() > 12){
+ attendance.setNightNumber(1);
+ //必须是8小时倒班制or单班制, 并且工作时长>=8小时, 并且 上班打开时间在12点至21点 算作中班 -- 2026-02-04 因1月份张克龙中班考勤问题, 刘杰面谈
+ }else if(dingsShift.getWorkHour() == 8 && attendance.getWorkSum().intValue() >= 8 && attendance.getWorkStartTime() != null && attendance.getWorkStartTime().getHours() >= 12 && attendance.getWorkStartTime().getHours() < 21){
+ attendance.setMiddleShiftNumber(1);
+ }
+ //记录补卡扣款
+ attendance.setDebiting(DataUtils.findDefaultValue(attendance.getDebiting(), DataUtils.DEFAULT_VALUE).add(new BigDecimal(DataUtils.findDefaultValue(debiting, 0d))));
+ getBaseMapper().updateRzAttendance(attendance);
+ //检查是否开启加班, 所有的加班调整为, 扫脸打卡
+// if(dingsShift.getOvertimeWork()){
+// //使用排班下班时间作为加班开始时间
+// rzOverTimeService.overTimeCard(sysStaff, DateUtils.parseDate(DateUtils.parseDateToStr("yyyy-MM-dd", formatWorkDate)+" "+ dingsShiftGroup.getCheckTime()));
+// //员工打卡时间作为加班下班时间
+// rzOverTimeService.overTimeOffDutyCard(sysStaff, formatUserCheckTime, "/");
+// }
+ }else{
+ log.info("钉钉回调打卡信息=====>>>>> {}", result);
+ }
+ }
+ @Override
+ public RzAttendance queryNowDayAttendanceByStatisticalIdAndDate(Long staffId, Date date) {
+ return getBaseMapper().queryNowDayAttendanceByStatisticalIdAndDate(staffId,date);
+ }
+
+ @Override
+ public RzAttendance loadAttendance(Long id) {
+ RzAttendance attendance = getById(id);
+ SysStaff sysStaff = sysStaffMapper.selectById(attendance.getStaffId());
+ if(StringUtils.isNotEmpty(sysStaff.getDingUserId())){
+ String result = DingUtils.getListRecord(Collections.asList(sysStaff.getDingUserId()), DateUtils.parseDateToStr("yyyy-MM-dd", attendance.getAttendanceDate())+" 00:00:00", DateUtils.parseDateToStr("yyyy-MM-dd", attendance.getAttendanceDate())+" 23:59:59");
+ JSONObject resultJson = JSON.parseObject(result);
+ if(Integer.valueOf(0).equals(resultJson.getInteger("errcode")))
+ try {
+ JSONArray jsonArray = resultJson.getJSONArray("recordresult");
+ JSONObject onDutyObject = (JSONObject) jsonArray.stream().filter(data -> "OnDuty".equals(((JSONObject)data).getString("checkType"))).findFirst().orElse(null);
+ if(onDutyObject != null){
+ calculationAddRzOverTime(result, sysStaff, onDutyObject, attendance);
+ }
+ JSONObject offDutyObject = (JSONObject) jsonArray.stream().filter(data -> "OffDuty".equals(((JSONObject)data).getString("checkType"))).findFirst().orElse(null);
+ if(offDutyObject != null){
+ calculationAddRzOverTime(result, sysStaff, offDutyObject, attendance);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ log.error("重新抓取钉钉考勤结果异常, 异常原因是==>>{}, 打卡结果是=====>>>>> {}", e.getMessage(), result);
+ }
+ }
+ return attendance;
+ }
+
+ @Override
+ public AjaxResult loadAllAttendance(RzAttendance rzAttendance) {
+ if (StringUtils.isNull(rzAttendance.getAttendanceDate())) {
+ return AjaxResult.error("请选择需要重新抓取的月份!!");
+ }
+ dataBatchHandler.batchProcess(rzAttendance.getAttendanceDate(), (result, sysStaff, ob, attendance) -> {
+ // 调用你当前 ServiceImpl 里的方法
+ try {
+ this.calculationAddRzOverTime(result, sysStaff, ob, attendance);
+ } catch (Exception e) {
+ e.printStackTrace();
+ log.error("刷新用户===>>{}, 出现异常, 异常原因为====>{}", JSONObject.toJSONString(sysStaff), e.getMessage());
+ }
+ });
+ return null;
+ }
+
+ public void calculationAddRzOverTime(String result ,SysStaff sysStaff, JSONObject ob, RzAttendance attendance){
+ //打卡类型
+ String checkType = ob.getString("checkType");
+ //考勤组
+ String groupId = ob.getString("groupId");
+ //
+ String shiftId = ob.getString("classId");
+ //排班时间
+ Long planCheckTime = ob.getLong("planCheckTime");
+ // 格式化:先转Date对象,再格式化
+ String formatPlanCheckTime = DateUtils.parseDateToStr("HH:mm:ss", new Date(planCheckTime));
+ //用户打卡时间
+ Long userCheckTime = ob.getLong("userCheckTime");
+ //工作时间 (这里指的是哪一天的卡)
+ Long workDate = ob.getLong("workDate");
+ /***
+ * 打卡结果
+ * Normal:正常
+ * Early:早退
+ * Late:迟到
+ * SeriousLate:严重迟到
+ * Absenteeism:旷工迟到
+ * NotSigned:未打卡
+ */
+ String timeResult = ob.getString("timeResult");
+
+ Date formatWorkDate = Date.from(Instant.ofEpochMilli(workDate));
+ Date formatUserCheckTime = Date.from(Instant.ofEpochMilli(userCheckTime));
+ DingShiftGroup dingsShiftGroup = dingsShiftGroupMapper.selectByGroupIdAndTypeAndCheckTime(groupId, shiftId, checkType, formatPlanCheckTime);
+ addRzOverTime(result, sysStaff, dingsShiftGroup, checkType, formatWorkDate, formatUserCheckTime, timeResult, null, 0d, attendance);
+ }
@Async
protected void sendAbnormalAttendance(SysStaff sysStaff, Date date){
diff --git a/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceStatisticalServiceImpl.java b/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceStatisticalServiceImpl.java
index 0e52e0c..f690e8b 100644
--- a/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceStatisticalServiceImpl.java
+++ b/evo-admin/src/main/java/com/evo/attendance/service/impl/RzAttendanceStatisticalServiceImpl.java
@@ -171,16 +171,21 @@ public class RzAttendanceStatisticalServiceImpl extends ServiceImpl kq_list = rzAttendanceMapper.queryMonthAttendanceByStaffId(sysStaff.getUserId(),new Date());
//循环修改信息
for (RzAttendance rzAttendance : kq_list) {
- rzAttendance.setDeptId(sysStaff.getDeptId());
+ if(rzAttendanceStatistical.getDeptId() == null){
+ rzAttendance.setDeptId(sysStaff.getDeptId());
+ }
rzAttendance.setName(sysStaff.getName());
rzAttendanceMapper.updateRzAttendance(rzAttendance);
}
diff --git a/evo-admin/src/main/java/com/evo/attendance/service/impl/RzSpecialAttendanceServiceImpl.java b/evo-admin/src/main/java/com/evo/attendance/service/impl/RzSpecialAttendanceServiceImpl.java
index dcdd65a..1df5206 100644
--- a/evo-admin/src/main/java/com/evo/attendance/service/impl/RzSpecialAttendanceServiceImpl.java
+++ b/evo-admin/src/main/java/com/evo/attendance/service/impl/RzSpecialAttendanceServiceImpl.java
@@ -4,6 +4,7 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evo.attendance.domain.RzAttendanceStatistical;
import com.evo.attendance.domain.RzSpecialAttendance;
import com.evo.attendance.domain.RzSpecialOverTime;
@@ -23,7 +24,7 @@ import javax.annotation.Resource;
* @date 2025-04-15
*/
@Service
-public class RzSpecialAttendanceServiceImpl implements IRzSpecialAttendanceService
+public class RzSpecialAttendanceServiceImpl extends ServiceImpl implements IRzSpecialAttendanceService
{
@Resource
private RzSpecialAttendanceMapper rzSpecialAttendanceMapper;
@@ -131,4 +132,9 @@ public class RzSpecialAttendanceServiceImpl implements IRzSpecialAttendanceServi
rzAttendanceStatistical.setOverTimeHours(rzAttendanceStatistical.getOverTimeHours().subtract(rzSpecialAttendance.getWorkHours()));
return rzAttendanceStatisticalMapper.updateRzAttendanceStatistical(rzAttendanceStatistical);
}
+
+ @Override
+ public RzSpecialAttendance selectRzSpecialAttendanceByUserIdAndDate(Long staffId, Date date) {
+ return getBaseMapper().selectRzSpecialAttendanceByUserIdAndDate(staffId, date);
+ }
}
diff --git a/evo-admin/src/main/java/com/evo/attendance/service/impl/RzSysParamServiceImpl.java b/evo-admin/src/main/java/com/evo/attendance/service/impl/RzSysParamServiceImpl.java
index 58bacfa..135e187 100644
--- a/evo-admin/src/main/java/com/evo/attendance/service/impl/RzSysParamServiceImpl.java
+++ b/evo-admin/src/main/java/com/evo/attendance/service/impl/RzSysParamServiceImpl.java
@@ -4,6 +4,7 @@ import java.util.List;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.evo.common.constant.CacheConstants;
import com.evo.common.utils.DateUtils;
import com.evo.common.utils.SecurityUtils;
import com.evo.equipment.constant.Constants;
@@ -103,6 +104,7 @@ public class RzSysParamServiceImpl extends ServiceImpl().eq(RzSysParam::getParamCode, code));
}
diff --git a/evo-admin/src/main/java/com/evo/common/async/AsyncConfig.java b/evo-admin/src/main/java/com/evo/common/async/AsyncConfig.java
new file mode 100644
index 0000000..ab7a14d
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/common/async/AsyncConfig.java
@@ -0,0 +1,52 @@
+package com.evo.common.async;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+/**
+ * AsyncConfig
+ *
+ * @author andy.shi
+ * @ClassName:AsyncConfig
+ * @date: 2026年03月05日 8:48
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Configuration
+//@EnableAsync
+public class AsyncConfig {
+
+ @Value("${yt.async.corePoolSize:'5'}")
+ private Integer corePoolSize;
+ @Value("${yt.async.maxPoolSize:'10'}")
+ private Integer maxPoolSize;
+ @Value("${yt.async.queueCapacity:'20'}")
+ private Integer queueCapacity;
+ @Value("${yt.async.keepAliveSeconds:'60'}")
+ private Integer keepAliveSeconds;
+
+
+ @Bean("asyncExecutor") // 自定义线程池名称
+ public Executor asyncExecutor() {
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ // 核心线程数
+ executor.setCorePoolSize(corePoolSize);
+ // 最大线程数
+ executor.setMaxPoolSize(maxPoolSize);
+ // 队列容量
+ executor.setQueueCapacity(queueCapacity);
+ // 线程空闲超时时间
+ executor.setKeepAliveSeconds(keepAliveSeconds);
+ // 线程名称前缀
+ executor.setThreadNamePrefix("async-thread-");
+ // 拒绝策略(队列满+最大线程数满时的处理方式)
+ executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+ // 初始化
+ executor.initialize();
+ return executor;
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/common/config/thread/DynamicThreadPoolConfig.java b/evo-admin/src/main/java/com/evo/common/config/thread/DynamicThreadPoolConfig.java
new file mode 100644
index 0000000..06e3cca
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/common/config/thread/DynamicThreadPoolConfig.java
@@ -0,0 +1,73 @@
+package com.evo.common.config.thread;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicLong;
+/**
+ * SpringBoot 动态安全线程池(生产级)
+ * @ClassName:DynamicThreadPoolConfig
+ * @date: 2026年04月25日 15:00
+ * @author: andy.shi
+ * @contact: 17330188597
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Configuration
+public class DynamicThreadPoolConfig {
+
+ private final ThreadPoolProperties poolProperties;
+
+ public DynamicThreadPoolConfig(ThreadPoolProperties poolProperties) {
+ this.poolProperties = poolProperties;
+ }
+
+ @Bean(name = "dataBatchThreadPool")
+ public ThreadPoolTaskExecutor dataBatchThreadPool() {
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+
+ // 基础配置
+ executor.setCorePoolSize(poolProperties.getCorePoolSize());
+ executor.setMaxPoolSize(poolProperties.getMaxPoolSize());
+ executor.setQueueCapacity(poolProperties.getQueueCapacity());
+ executor.setKeepAliveSeconds(poolProperties.getKeepAliveSeconds());
+ executor.setThreadNamePrefix(poolProperties.getThreadNamePrefix());
+
+ // 核心线程允许超时回收
+ executor.setAllowCoreThreadTimeOut(true);
+
+ // 自定义线程工厂(异常捕获)
+ executor.setThreadFactory(new ThreadFactory() {
+ private final AtomicLong num = new AtomicLong(1);
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(r);
+ thread.setName(poolProperties.getThreadNamePrefix() + num.getAndIncrement());
+ thread.setDaemon(true);
+ thread.setUncaughtExceptionHandler((t, e) ->
+ System.err.println("线程异常:" + t.getName() + "," + e.getMessage())
+ );
+ return thread;
+ }
+ });
+
+ // 拒绝策略:阻塞入队,不丢任务
+ executor.setRejectedExecutionHandler((r, e) -> {
+ if (!e.isShutdown()) {
+ try {
+ e.getQueue().put(r);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ });
+
+ // 优雅关闭
+ executor.setWaitForTasksToCompleteOnShutdown(true);
+ executor.setAwaitTerminationSeconds(60);
+ executor.initialize();
+ return executor;
+ }
+}
\ No newline at end of file
diff --git a/evo-admin/src/main/java/com/evo/common/config/thread/ThreadPoolManager.java b/evo-admin/src/main/java/com/evo/common/config/thread/ThreadPoolManager.java
new file mode 100644
index 0000000..65f853f
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/common/config/thread/ThreadPoolManager.java
@@ -0,0 +1,39 @@
+package com.evo.common.config.thread;
+
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
+/**
+ * 线程池工具类(静态调用)
+ * @ClassName:ThreadPoolManager
+ * @date: 2026年04月25日 15:02
+ * @author: andy.shi
+ * @contact: 17330188597
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+
+@Component
+public class ThreadPoolManager {
+
+ private static ThreadPoolTaskExecutor executor;
+
+ @Resource(name = "dataBatchThreadPool")
+ public void setExecutor(ThreadPoolTaskExecutor executor) {
+ ThreadPoolManager.executor = executor;
+ }
+
+ // 提交任务
+ public static void execute(Runnable task) {
+ executor.execute(task);
+ }
+
+ // 动态修改核心线程
+ public static void setCorePoolSize(int size) {
+ executor.setCorePoolSize(size);
+ }
+
+ // 动态修改最大线程
+ public static void setMaxPoolSize(int size) {
+ executor.setMaxPoolSize(size);
+ }
+}
\ No newline at end of file
diff --git a/evo-admin/src/main/java/com/evo/common/config/thread/ThreadPoolProperties.java b/evo-admin/src/main/java/com/evo/common/config/thread/ThreadPoolProperties.java
new file mode 100644
index 0000000..42900cd
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/common/config/thread/ThreadPoolProperties.java
@@ -0,0 +1,26 @@
+package com.evo.common.config.thread;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @desc:
+ * @ClassName:ThreadPoolProperties
+ * @date: 2026年04月25日 14:57
+ * @author: andy.shi
+ * @contact: 17330188597
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "thread.pool")
+public class ThreadPoolProperties {
+ private Integer corePoolSize;
+ private Integer maxPoolSize;
+ private Integer keepAliveSeconds;
+ private Integer queueCapacity;
+ private String threadNamePrefix;
+ private Integer batchSize; // 每线程处理条数
+}
\ No newline at end of file
diff --git a/evo-admin/src/main/java/com/evo/common/constant/Constants.java b/evo-admin/src/main/java/com/evo/common/constant/Constants.java
index 2a395ac..62f6e59 100644
--- a/evo-admin/src/main/java/com/evo/common/constant/Constants.java
+++ b/evo-admin/src/main/java/com/evo/common/constant/Constants.java
@@ -133,4 +133,9 @@ public class Constants
public static final BigDecimal DAY_WORK_HOUR = new BigDecimal(8);
+
+ public static final String SOURCE_PT = "1";
+ public static final String SOURCE_DING = "2";
+
+
}
diff --git a/evo-admin/src/main/java/com/evo/common/controller/CallbackController.java b/evo-admin/src/main/java/com/evo/common/controller/CallbackController.java
new file mode 100644
index 0000000..dd36a58
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/common/controller/CallbackController.java
@@ -0,0 +1,113 @@
+package com.evo.common.controller;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.evo.attendance.service.IRzAttendanceService;
+import com.evo.common.core.controller.BaseController;
+import com.evo.ding.DingGlobalParams;
+import com.evo.ding.DingCallbackCrypto;
+import com.evo.ding.processor.instance.InstanceChangeExchangeProcessor;
+import com.evo.ding.processor.callback.DingCallBackExchangeProcessor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CallbackController
+ *
+ * @author andy.shi
+ * @ClassName:CallbackController
+ * @date: 2026年03月04日 11:27
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@RestController
+@RequestMapping("/callback")
+@Slf4j
+public class CallbackController extends BaseController {
+
+
+ @Autowired
+ IRzAttendanceService rzAttendanceService;
+ @Autowired
+ List instanceChangeExchangeProcessorList;
+ @Autowired
+ List dingCallBackExchangeProcessorList;
+ @PostMapping("/ding/clock")
+ public Map dingDingClock(@RequestBody Map params){
+ System.out.println(JSONObject.toJSONString(params));
+ try {
+ // 1. 从http请求中获取加解密参数
+
+ // 2. 使用加解密类型
+ // 2、调用订阅事件接口订阅的事件为企业级事件推送,此时OWNER_KEY为:开发者后台应用的Client ID(原企业内部应用 appKey )
+ DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(DingGlobalParams.getToken(), DingGlobalParams.getAesKey(), DingGlobalParams.getClientId());
+ String encryptMsg = String.valueOf(params.get("encrypt"));
+ String decryptMsg = callbackCrypto.getDecryptMsg(String.valueOf(params.get("msg_signature")), String.valueOf(params.get("timestamp")), String.valueOf(params.get("nonce")), encryptMsg);
+
+ System.out.println("==============>"+decryptMsg);
+ // 3. 反序列化回调事件json数据
+ JSONObject eventJson = JSON.parseObject(decryptMsg);
+ String eventType = eventJson.getString("EventType");
+ log.info("发生了:" + eventType + "事件");
+ for (DingCallBackExchangeProcessor processor : dingCallBackExchangeProcessorList) {
+ if (processor.accept(eventType)) {
+ processor.exchange(eventJson);
+ }
+ }
+
+ // 4. 根据EventType分类处理
+// if ("check_url".equals(eventType)) {
+// log.info("打卡回调");
+// } else if ("attendance_check_record".equals(eventType)) { //打卡推送
+// JSONArray dataList = eventJson.getJSONArray("DataList");
+// if(dataList!= null && dataList.size() > 0){
+// JSONObject jsonObject = dataList.getJSONObject(0);
+// String userId = jsonObject.getString("userId");
+// if(StringUtils.isNotEmpty(userId)){
+// rzAttendanceService.dingDing(userId, jsonObject.getString("bizId"));
+// }
+// }
+// //审批实例订阅
+// }else if ("bpms_instance_change".equals(eventType)) {
+// log.info("审批实例订阅回调");
+// for (InstanceChangeExchangeProcessor processor : instanceChangeExchangeProcessorList) {
+// if (processor.accept(eventJson.getString("processCode"))) {
+// processor.exchange(eventJson);
+// }
+// }
+// // 审批任务订阅
+// }else if ("bpms_task_change".equals(eventType)) {
+// log.info("审批任务订阅回调");
+// // 处理通讯录用户增加事件
+// }else if ("attendance_approve_status_change".equals(eventType)) {
+// log.info("请假、加班、出差、外出状态变更事件");
+//
+//
+//
+// // 处理通讯录用户增加事件
+// }else if ("user_add_org".equals(eventType)) {
+// // 处理通讯录用户增加事件
+// log.info("发生了:" + eventType + "事件");
+//
+//
+// } else {
+// // 添加其他已注册的
+// log.info("发生了:" + eventType + "事件");
+// }
+
+ // 5. 返回success的加密数据
+ Map successMap = callbackCrypto.getEncryptedMap("success");
+ return successMap;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/common/controller/TestController.java b/evo-admin/src/main/java/com/evo/common/controller/TestController.java
index cb862c8..c982c10 100644
--- a/evo-admin/src/main/java/com/evo/common/controller/TestController.java
+++ b/evo-admin/src/main/java/com/evo/common/controller/TestController.java
@@ -3,6 +3,7 @@ package com.evo.common.controller;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
+import com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoResponseBody;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.evo.attendance.service.IRzAttendanceService;
import com.evo.attendance.service.IRzAttendanceStatisticalService;
@@ -11,6 +12,8 @@ import com.evo.common.config.Global;
import com.evo.common.constant.Constants;
import com.evo.common.core.domain.AjaxResult;
import com.evo.common.utils.Collections;
+import com.evo.common.utils.http.HttpUtils;
+import com.evo.ding.DingUtils;
import com.evo.personnelMatters.mapper.RzOverTimeDetailMapper;
import com.evo.personnelMatters.mapper.RzOverTimeMapper;
import com.evo.restaurant.service.IRzRestaurantStatisticsService;
@@ -20,21 +23,27 @@ import com.evo.wechat.TemplateMessageUtil;
import com.evo.wechat.dto.AbnormalAttendanceTemplate;
import com.evo.wechat.dto.MessageTemplate;
import com.evo.wechat.service.GZHAccessTokenService;
-import org.springframework.stereotype.Controller;
+import org.apache.ibatis.annotations.Param;
+import org.jasypt.encryption.StringEncryptor;
import org.springframework.ui.Model;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@@ -67,6 +76,208 @@ public class TestController {
private RzAttendanceDetailService rzAttendanceDetailService;
@Resource
private GZHAccessTokenService gzhAccessTokenService;
+ @Resource
+ private StringEncryptor stringEncryptor;
+ /**
+ * 清洗加班
+ */
+ @GetMapping("/dept")
+ public AjaxResult dept(Long deptId) throws ParseException {
+ return AjaxResult.success("");
+ }
+
+ /**
+ * 清洗加班
+ */
+ @GetMapping("/user")
+ public AjaxResult user(String phone) throws ParseException {
+ return AjaxResult.success(DingUtils.getUserIdByPhoto(phone));
+ }
+
+
+
+ /**
+ * 清洗加班
+ */
+ @GetMapping("/aes")
+ public void aes() throws ParseException {
+ // 1. 解密用户名(去掉ENC())
+ String usernameCipher = "QpRNGkSzaeihXTOqXCqXCdAgTTBTOD8M";
+ String usernamePlain = stringEncryptor.decrypt(usernameCipher);
+ System.out.println("解密后的用户名:" + usernamePlain);
+
+ // 2. 解密密码(去掉ENC())
+ String passwordCipher = "c5Sf+6DNzIgtVm2BoHLFmJqnsTJhqRQd721fQ5HXFEY=";
+ String passwordPlain = stringEncryptor.decrypt(passwordCipher);
+ System.out.println("解密后的密码:" + passwordPlain);
+ }
+
+
+ @GetMapping("/buildUser")
+ public AjaxResult buildUser() throws ParseException {
+ List staffList = sysStaffService.list(new LambdaQueryWrapper().ne(SysStaff::getStatus, -1).isNull(SysStaff::getDingUserId));
+ List notDingDing = Collections.emptyList();
+ if(Collections.isNotEmpty(staffList)){
+ staffList.stream().forEach(data ->{
+ String result = DingUtils.getUserIdByPhoto(data.getPhone());
+ JSONObject resObject = JSON.parseObject(result);
+ if(Integer.valueOf(0).equals(resObject.getInteger("errcode"))){
+ JSONObject dingRes = resObject.getJSONObject("result");
+ String userId = dingRes.getString("userid");
+ data.setDingUserId(userId);
+ }else{
+ notDingDing.add(data.getName());
+ }
+ });
+ }
+ sysStaffService.updateBatchById(staffList);
+ if(Collections.isNotEmpty(notDingDing)){
+ return AjaxResult.success("部分更新失败, 未找到钉钉数据, 人员名单如下: "+ String.join(",", notDingDing));
+ }
+ return AjaxResult.success("更新完成");
+ }
+
+
+
+ /**
+ * 上传
+ */
+ @GetMapping("/update")
+ public void testUpload(@Param("file") MultipartFile filePath) throws Exception {
+ GetFileUploadInfoResponseBody responseBody = DingUtils.resourceUrls();
+ // 从接口返回信息中拿到url
+ String resourceUrl = responseBody.getHeaderSignatureInfo().getResourceUrls().get(0);
+ // 从接口返回信息中拿到headers
+ Map headers = responseBody.getHeaderSignatureInfo().getHeaders();
+ URL url = new URL(resourceUrl);
+ HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+ if (headers != null) {
+ for (Map.Entry entry : headers.entrySet()) {
+ connection.setRequestProperty(entry.getKey(), entry.getValue());
+ }
+ }
+ connection.setDoOutput(true);
+ connection.setRequestMethod("PUT");
+ connection.setUseCaches(false);
+ connection.setReadTimeout(10000);
+ connection.setConnectTimeout(10000);
+ connection.connect();
+ OutputStream out = connection.getOutputStream();
+ InputStream is = filePath.getInputStream();
+ byte[] b =new byte[1024];
+ int temp;
+ while ((temp=is.read(b))!=-1){
+ out.write(b,0,temp);
+ }
+ out.flush();
+ out.close();
+ int responseCode = connection.getResponseCode();
+ connection.disconnect();
+ if (responseCode == 200) {
+ System.out.println("上传成功");
+ } else {
+ System.out.println("上传失败");
+ }
+
+
+
+ }
+
+
+
+
+
+ /**
+ * 清洗加班
+ */
+ @GetMapping("/testAdapterSend")
+ public AjaxResult testSend() throws ParseException {
+ RestTemplate restTemplate = new RestTemplate();
+ MultiValueMap params = new LinkedMultiValueMap<>();
+// // 文件
+// File file = new File("D:\\andy\\车辆管理.png");
+//// File file1 = new File("D:\\andy\\index.ts");
+// FileSystemResource resource = new FileSystemResource(file);
+//// FileSystemResource resource1 = new FileSystemResource(file1);
+// params.add("files", resource);
+//// params.add("files", resource1);
+// StringBuilder markdownMsg = new StringBuilder();
+// markdownMsg.append("### 🚀 安全库存提醒\n\n")
+// .append("今日暂无安全库存预警数据。\n\n");
+// // 只显示前三个安全库存数据
+// markdownMsg.append("**物料编号: ").append("123321").append("**\n")
+// .append("- 物料名称: ").append("测试测试567").append("\n")
+// .append("- 预计可用量: ").append("200").append("\n")
+// .append("- 即时库存: ").append("100").append("\n")
+// .append("- 最大库存: ").append("50").append("\n")
+// .append("- 创建时间: ").append(DateUtil.formatDateTime(new Date())).append("\n")
+// .append("---\n"); // 添加分隔线
+// // 添加总数汇总
+// markdownMsg.append("**总数汇总**\n")
+// .append("- 今日安全库存预警数据总数: **").append(50).append("**\n");
+//
+//
+//
+//
+// {"month":"sendMarkdownAndFileToUser","robotCode":"dinge794oclijuyxsyub","clientId":"dinge794oclijuyxsyub","clientSecret":"4ryzLl48I8ghhKqEMPCwablFqbs6PCmadqfDAPFWBNuIEBMJvPFQg-XHAe2oH2Fe","recipientType":"code","users":["00312","00150"],"title":"消息通知测试","content":"消息正文内容。。。。。。。。"}
+
+ Map dataMap = Collections.emptyMap();
+ dataMap.put("month", "sendMarkdownAndFileToUser");
+ dataMap.put("robotCode","dinge794oclijuyxsyub");
+ dataMap.put("clientId", "dinge794oclijuyxsyub");
+ dataMap.put("clientSecret", "4ryzLl48I8ghhKqEMPCwablFqbs6PCmadqfDAPFWBNuIEBMJvPFQg-XHAe2oH2Fe");
+ dataMap.put("recipientType", "code");//00146
+ dataMap.put("users", JSON.toJSONString(Collections.asList("00312","00150")));
+ dataMap.put("title", "消息通知测试");
+ dataMap.put("content", "消息正文内容。。。。。。。。");
+//
+//// dataMap.put("parameterTypes", JSON.toJSONString(Collections.asList("java.lang.String","java.lang.String")));
+//// dataMap.put("parameters", JSON.toJSONString(Collections.asList("test测试",markdownMsg.toString())));
+// //推送数据类型
+ params.add("data", JSON.toJSONString(dataMap));
+//
+ String url = "http://localhost:8089/ding/send";
+ return AjaxResult.success(restTemplate.postForObject(url, params, String.class));
+//
+
+ //String data = "{\"FormId\":\"BD_Empinfo\",\"FieldKeys\":\"FID\",\"FilterString\":[{\"Left\":\"\",\"FieldName\":\"FStaffNumber\",\"Compare\":\"17\",\"Value\":\"00046\",\"Right\":\"\",\"Logic\":0}],\"OrderString\":\"\",\"TopRowCount\":0,\"StartRow\":0,\"Limit\":2000,\"SubSystemId\":\"\"}";
+//
+// String data = "{\n" +
+// " \"FormId\" : \"PRD_MO\",\n" +
+// " \"FieldKeys\" : \"F_HBYT_SCLH,FBillNo,FWorkShopID.FName,FMaterialId.FNumber,FMaterialName,FPlanStartDate,FPlanFinishDate,FPickMtrlStatus\",\n" +
+// " \"FilterString\" : [ {\n" +
+// " \"FieldName\" : \"FStatus\",\n" +
+// " \"Compare\" : \"105\",\n" +
+// " \"Value\" : \"4\",\n" +
+// " \"Left\" : \"\",\n" +
+// " \"Right\" : \"\",\n" +
+// " \"Logic\" : 0\n" +
+// " }, {\n" +
+// " \"FieldName\" : \"FPickMtrlStatus\",\n" +
+// " \"Compare\" : \"29\",\n" +
+// " \"Value\" : \"1\",\n" +
+// " \"Left\" : \"\",\n" +
+// " \"Right\" : \"\",\n" +
+// " \"Logic\" : 0\n" +
+// " }, {\n" +
+// " \"FieldName\" : \"F_HBYT_SCLH\",\n" +
+// " \"Compare\" : \"67\",\n" +
+// " \"Value\" : \"QB-26-163-XCL\",\n" +
+// " \"Left\" : \"\",\n" +
+// " \"Right\" : \"\",\n" +
+// " \"Logic\" : 0\n" +
+// " } ],\n" +
+// " \"OrderString\" : \"\",\n" +
+// " \"TopRowCount\" : 0,\n" +
+// " \"StartRow\" : 0,\n" +
+// " \"Limit\" : 10000,\n" +
+// " \"SubSystemId\" : \"\"\n" +
+// "}";
+//
+// return AjaxResult.success(HttpUtils.sendPost("http://localhost:8089/distribution/kingdee/send", JSONObject.toJSONString(
+// Collections.asMap("fromId","PRD_MO", "month","billQuery", "parameterTypes",Collections.asList(String.class),"data",Collections.asMap("123",data)))));
+ }
+
/**
* 清洗加班
diff --git a/evo-admin/src/main/java/com/evo/common/core/domain/entity/SysDept.java b/evo-admin/src/main/java/com/evo/common/core/domain/entity/SysDept.java
index ede1e15..70dfc67 100644
--- a/evo-admin/src/main/java/com/evo/common/core/domain/entity/SysDept.java
+++ b/evo-admin/src/main/java/com/evo/common/core/domain/entity/SysDept.java
@@ -60,6 +60,8 @@ public class SysDept extends BaseEntity
/** 是否加班 */
private String isShowFinancial;
+ private String dingId;
+
/** 子部门 */
@TableField(exist = false)
private List children = new ArrayList();
diff --git a/evo-admin/src/main/java/com/evo/common/function/FourParamFunction.java b/evo-admin/src/main/java/com/evo/common/function/FourParamFunction.java
new file mode 100644
index 0000000..f13ad0b
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/common/function/FourParamFunction.java
@@ -0,0 +1,14 @@
+package com.evo.common.function;
+
+/**
+ * @desc: 四参数
+ * @ClassName:FourParamFunction
+ * @date: 2026年04月27日 11:46
+ * @author: andy.shi
+ * @contact: 17330188597
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@FunctionalInterface
+public interface FourParamFunction {
+ void apply(A a, B b, C c, D d);
+}
\ No newline at end of file
diff --git a/evo-admin/src/main/java/com/evo/common/handler/thread/RzAttendanceBatchHandler.java b/evo-admin/src/main/java/com/evo/common/handler/thread/RzAttendanceBatchHandler.java
new file mode 100644
index 0000000..8871b3c
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/common/handler/thread/RzAttendanceBatchHandler.java
@@ -0,0 +1,119 @@
+package com.evo.common.handler.thread;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.evo.attendance.domain.RzAttendance;
+import com.evo.common.config.thread.ThreadPoolManager;
+import com.evo.common.config.thread.ThreadPoolProperties;
+import com.evo.common.function.FourParamFunction;
+import com.evo.common.utils.Collections;
+import com.evo.common.utils.DateUtils;
+import com.evo.common.utils.StringUtils;
+import com.evo.ding.DingUtils;
+import com.evo.system.domain.SysStaff;
+import com.evo.system.service.ISysStaffService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 考勤 批量数据处理器 每个线程 固定处理 50 条数据
+ * @ClassName:DataBatchHandler
+ * @date: 2026年04月25日 15:01
+ * @author: andy.shi
+ * @contact: 17330188597
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+
+@Slf4j
+@Component
+public class RzAttendanceBatchHandler {
+
+ private final ThreadPoolProperties poolProperties;
+
+ @Autowired
+ private ISysStaffService sysStaffService;
+
+ public RzAttendanceBatchHandler(ThreadPoolProperties poolProperties) {
+ this.poolProperties = poolProperties;
+ }
+
+ /**
+ * 批量处理任务(自动分片,每片50条)
+ */
+ public void batchProcess(Date date, FourParamFunction calculationAddRzOverTime) {
+ int batchSize = poolProperties.getBatchSize(); // 50条/线程
+ AtomicInteger index = new AtomicInteger(0);
+ List sysStaffList = sysStaffService.selectSysStaffListAll();
+
+ Date firstDate = DateUtils.getMonthFirst(date);
+ Date endDate = DateUtils.getMonthEnd(date);
+ // 获取有多少天
+ List dateTimeList = new ArrayList<>();
+
+ while (firstDate.compareTo(endDate)<=0) {
+ dateTimeList.add(DateUtils.parseDateToStr("yyyy-MM-dd", firstDate));
+ firstDate = DateUtils.addDays(firstDate, 1);
+ }
+
+
+ // 循环分片提交任务
+ while (index.get() < sysStaffList.size()) {
+ int start = index.getAndAdd(batchSize);
+ int end = Math.min(start + batchSize, sysStaffList.size());
+
+ List batchData = sysStaffList.subList(start, end);
+
+ // 提交到线程池,一个线程处理这 50 条
+ ThreadPoolManager.execute(() -> {
+ try {
+ handleBatchData(batchData, dateTimeList, calculationAddRzOverTime);
+ } catch (Exception e) {
+ System.err.println("批量处理异常:" + e.getMessage());
+ }
+ });
+ }
+ }
+
+ /**
+ * 真正业务:单个线程处理 50 条数据
+ */
+ private void handleBatchData(List batchData, List dateTimeList, FourParamFunction calculationAddRzOverTime) {
+ String threadName = Thread.currentThread().getName();
+ System.out.println("========================================");
+ System.out.println(threadName + " 开始处理,条数:" + batchData.size());
+ //将数据的钉钉钉钉list获取
+ Map map = batchData.stream().filter(data -> StringUtils.isNotEmpty(data.getDingUserId())).collect(Collectors.toMap(SysStaff::getDingUserId, data -> data));
+ List dingUserIds = map.keySet().stream().collect(Collectors.toList());
+ System.out.println("==================="+JSONObject.toJSONString(dingUserIds));
+ for (String date : dateTimeList){
+ String result = DingUtils.getListRecord(dingUserIds, date+" 00:00:00", date+" 23:59:59");
+ JSONObject resultJson = JSON.parseObject(result);
+ if(Integer.valueOf(0).equals(resultJson.getInteger("errcode"))){
+ try {
+ JSONArray jsonArray = resultJson.getJSONArray("recordresult");
+ for (int i = 0; i < jsonArray.size(); i++) {
+ JSONObject object = jsonArray.getJSONObject(i);
+ calculationAddRzOverTime.apply(result, map.get(object.getString("userId")), object, null);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ log.error("重新抓取钉钉考勤结果异常, 抓取用户为====>>>{}, 异常原因是==>>{}, 打卡结果是=====>>>>> {}", JSONObject.toJSONString(dingUserIds), e.getMessage(), result);
+ }
+ }
+
+ }
+ System.out.println(threadName + " 处理完成");
+ }
+
+}
diff --git a/evo-admin/src/main/java/com/evo/common/utils/ParamUtils.java b/evo-admin/src/main/java/com/evo/common/utils/ParamUtils.java
index 49af078..99317d2 100644
--- a/evo-admin/src/main/java/com/evo/common/utils/ParamUtils.java
+++ b/evo-admin/src/main/java/com/evo/common/utils/ParamUtils.java
@@ -76,6 +76,17 @@ public class ParamUtils {
return Double.valueOf(param.getParamValue());
}
+ /***
+ * 获取补卡单次扣款
+ * @return
+ */
+ public static Double getDebiting(){
+ RzSysParam param= paramService.getRzSysParam("补卡单次扣款", "recharge_card_debiting","2","单次补卡扣款");
+ return Double.valueOf(param.getParamValue());
+ }
+
+
+
/***
* 获取餐厅的开放时间
* @return
@@ -122,7 +133,7 @@ public class ParamUtils {
* @return
*/
public static List getInternDailyWage(){
- RzSysParam param= paramService.getRzSysParam("实习生不住宿的日薪人员名单", "intern_not_accommodation","乔克俭","实习生不住宿的日薪人员名单");
+ RzSysParam param= paramService.getRzSysParam("实习生不住宿的日薪人员名单", "intern_daily_wage","乔克俭","实习生不住宿的日薪人员名单");
return Collections.asList(param.getParamValue().split(","));
}
@@ -215,6 +226,9 @@ public class ParamUtils {
public static JSONObject getDeviceOverTimeRules(String sn){
if(StringUtils.isEmpty(sn)) return null;
RzSysParam param= paramService.getRzSysParam("特殊加班的考勤规则", "device_over_time_rules","{\"ET74336\":{\"minHour\":2, \"maxHour\":3,\"endMealTime\":\"19:00\"}}","特殊加班的考勤规则, minHour:最小加班时长. maxHour: 最大加班时长, endMealTime: 餐厅闭厅时间");
+ if(param == null){
+ return getDefaultMaxOverTime();
+ }
JSONObject jsonObject = JSONObject.parseObject(param.getParamValue());
JSONObject rules = jsonObject.getJSONObject(sn);
return (ObjectUtils.isEmpty(rules) ? getDefaultMaxOverTime(): rules);
@@ -299,14 +313,13 @@ public class ParamUtils {
return false;
}
//检查格式, 如果格式不匹配, 输出日志, 直接返回true
- if(!Pattern.compile("").matcher(checkDate).matches()){
+ if(!Pattern.compile("^\\d{4}-\\d{2}-\\d{2}$").matcher(checkDate).matches()){
log.error("当前检查日期{}, 不符合检查规则", checkDate);
return false;
}
//检查是否为假期
return getHoliddayList(Integer.valueOf(checkDate.substring(0,4)), 0).contains(checkDate);
}
-
/***
* 获取假期范围 0 全部 1法休 2 公休
* @param year
@@ -332,6 +345,16 @@ public class ParamUtils {
return Collections.asList(param.getParamValue().split(",")).stream().filter(StringUtils::isNotEmpty).map(Long::valueOf).collect(Collectors.toList());
}
+ /***
+ * 获取基本工资全新的假期
+ * @return
+ */
+ public static List getBasicFullPaidLeave(){
+ RzSysParam param= paramService.getRzSysParam("基本薪全薪发放的假期", "basic_full_paid_leave","58","基本薪全薪发放的假期; 58-陪产假");
+ return Collections.asList(param.getParamValue().split(",")).stream().filter(StringUtils::isNotEmpty).map(Long::valueOf).collect(Collectors.toList());
+ }
+
+
public static List getWebCompany(){
RzSysParam param= paramService.getRzSysParam("外包公司名称", "web_company","WB,LWWB","外包公司, 不扣个税)");
return Collections.asList(param.getParamValue().split(",")).stream().filter(StringUtils::isNotEmpty).collect(Collectors.toList());
diff --git a/evo-admin/src/main/java/com/evo/common/utils/SecurityUtils.java b/evo-admin/src/main/java/com/evo/common/utils/SecurityUtils.java
index 63b0e5b..705c5f5 100644
--- a/evo-admin/src/main/java/com/evo/common/utils/SecurityUtils.java
+++ b/evo-admin/src/main/java/com/evo/common/utils/SecurityUtils.java
@@ -3,6 +3,8 @@ package com.evo.common.utils;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
+
+import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@@ -18,6 +20,7 @@ import com.evo.common.exception.ServiceException;
*
* @author evo
*/
+@Slf4j
public class SecurityUtils
{
@@ -62,7 +65,9 @@ public class SecurityUtils
}
catch (Exception e)
{
- throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED);
+ log.error("获取用户账户异常", HttpStatus.UNAUTHORIZED);
+ return "system";
+// throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED);
}
}
diff --git a/evo-admin/src/main/java/com/evo/common/utils/http/HttpUtils.java b/evo-admin/src/main/java/com/evo/common/utils/http/HttpUtils.java
index 93935e1..aaaba66 100644
--- a/evo-admin/src/main/java/com/evo/common/utils/http/HttpUtils.java
+++ b/evo-admin/src/main/java/com/evo/common/utils/http/HttpUtils.java
@@ -138,7 +138,74 @@ public class HttpUtils
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "UTF-8");
- conn.setRequestProperty("contentType", "UTF-8");
+ //conn.setRequestProperty("contentType", "UTF-8");
+ conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");
+
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+ out = new PrintWriter(conn.getOutputStream());
+ out.print(param);
+ out.flush();
+ in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
+ String line;
+ while ((line = in.readLine()) != null)
+ {
+ result.append(line);
+ }
+ log.info("recv - {}", result);
+ }
+ catch (ConnectException e)
+ {
+ log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
+ }
+ catch (SocketTimeoutException e)
+ {
+ log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+ }
+ catch (IOException e)
+ {
+ log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
+ }
+ catch (Exception e)
+ {
+ log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
+ }
+ finally
+ {
+ try
+ {
+ if (out != null)
+ {
+ out.close();
+ }
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ catch (IOException ex)
+ {
+ log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+ }
+ }
+ return result.toString();
+ }
+
+ public static String sendPost(String url, String param, String contentType) {
+ PrintWriter out = null;
+ BufferedReader in = null;
+ StringBuilder result = new StringBuilder();
+ try
+ {
+ log.info("sendPost - {}", url);
+ URL realUrl = new URL(url);
+ URLConnection conn = realUrl.openConnection();
+ conn.setRequestProperty("accept", "*/*");
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
+ conn.setRequestProperty("Accept-Charset", "UTF-8");
+ conn.setRequestProperty("Content-Type", contentType);
+
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
diff --git a/evo-admin/src/main/java/com/evo/ding/DingCallbackCrypto.java b/evo-admin/src/main/java/com/evo/ding/DingCallbackCrypto.java
new file mode 100644
index 0000000..9de042a
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/DingCallbackCrypto.java
@@ -0,0 +1,400 @@
+package com.evo.ding;
+
+/**
+ * DingCallbackCrypto
+ *
+ * @author andy.shi
+ * @ClassName:DingCallbackCrypto
+ * @date: 2026年03月04日 11:55
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+
+import com.alibaba.fastjson2.JSON;
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.lang.reflect.Field;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+public class DingCallbackCrypto {
+
+ private static final Charset CHARSET = Charset.forName("utf-8");
+ private static final Base64 base64 = new Base64();
+ private byte[] aesKey;
+ private String token;
+ private String corpId;
+ /**
+ * ask getPaddingBytes key固定长度
+ **/
+ private static final Integer AES_ENCODE_KEY_LENGTH = 43;
+ /**
+ * 加密随机字符串字节长度
+ **/
+ private static final Integer RANDOM_LENGTH = 16;
+
+ /**
+ * 构造函数
+ *
+ * @param token 钉钉开放平台上,开发者设置的token
+ * @param encodingAesKey 钉钉开放台上,开发者设置的EncodingAESKey
+ * @param corpId 企业自建应用-事件订阅, 使用appKey
+ * 企业自建应用-注册回调地址, 使用corpId
+ * 第三方企业应用, 使用suiteKey
+ *
+ * @throws DingTalkEncryptException 执行失败,请查看该异常的错误码和具体的错误信息
+ */
+ public DingCallbackCrypto(String token, String encodingAesKey, String corpId) throws DingTalkEncryptException {
+ if (null == encodingAesKey || encodingAesKey.length() != AES_ENCODE_KEY_LENGTH) {
+ throw new DingTalkEncryptException(DingTalkEncryptException.AES_KEY_ILLEGAL);
+ }
+ this.token = token;
+ this.corpId = corpId;
+ aesKey = Base64.decodeBase64(encodingAesKey + "=");
+ }
+
+ public Map getEncryptedMap(String plaintext) throws DingTalkEncryptException {
+ return getEncryptedMap(plaintext, System.currentTimeMillis(), Utils.getRandomStr(16));
+ }
+
+ /**
+ * 将和钉钉开放平台同步的消息体加密,返回加密Map
+ *
+ * @param plaintext 传递的消息体明文
+ * @param timeStamp 时间戳
+ * @param nonce 随机字符串
+ * @return
+ * @throws DingTalkEncryptException
+ */
+ public Map getEncryptedMap(String plaintext, Long timeStamp, String nonce)
+ throws DingTalkEncryptException {
+ if (null == plaintext) {
+ throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL);
+ }
+ if (null == timeStamp) {
+ throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL);
+ }
+ if (null == nonce) {
+ throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_NONCE_ILLEGAL);
+ }
+ // 加密
+ String encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext);
+ String signature = getSignature(token, String.valueOf(timeStamp), nonce, encrypt);
+ Map resultMap = new HashMap();
+ resultMap.put("msg_signature", signature);
+ resultMap.put("encrypt", encrypt);
+ resultMap.put("timeStamp", String.valueOf(timeStamp));
+ resultMap.put("nonce", nonce);
+ return resultMap;
+ }
+
+ /**
+ * 密文解密
+ *
+ * @param msgSignature 签名串
+ * @param timeStamp 时间戳
+ * @param nonce 随机串
+ * @param encryptMsg 密文
+ * @return 解密后的原文
+ * @throws DingTalkEncryptException
+ */
+ public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg)
+ throws DingTalkEncryptException {
+ //校验签名
+ String signature = getSignature(token, timeStamp, nonce, encryptMsg);
+ if (!signature.equals(msgSignature)) {
+ throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
+ }
+ // 解密
+ String result = decrypt(encryptMsg);
+ return result;
+ }
+
+ /*
+ * 对明文加密.
+ * @param text 需要加密的明文
+ * @return 加密后base64编码的字符串
+ */
+ private String encrypt(String random, String plaintext) throws DingTalkEncryptException {
+ try {
+ byte[] randomBytes = random.getBytes(CHARSET);
+ byte[] plainTextBytes = plaintext.getBytes(CHARSET);
+ byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length);
+ byte[] corpidBytes = corpId.getBytes(CHARSET);
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ byteStream.write(randomBytes);
+ byteStream.write(lengthByte);
+ byteStream.write(plainTextBytes);
+ byteStream.write(corpidBytes);
+ byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size());
+ byteStream.write(padBytes);
+ byte[] unencrypted = byteStream.toByteArray();
+ byteStream.close();
+ Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+ SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
+ IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
+ byte[] encrypted = cipher.doFinal(unencrypted);
+ String result = base64.encodeToString(encrypted);
+ return result;
+ } catch (Exception e) {
+ throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR);
+ }
+ }
+
+ /*
+ * 对密文进行解密.
+ * @param text 需要解密的密文
+ * @return 解密得到的明文
+ */
+ private String decrypt(String text) throws DingTalkEncryptException {
+ byte[] originalArr;
+ try {
+ // 设置解密模式为AES的CBC模式
+ Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+ SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
+ IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
+ cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
+ // 使用BASE64对密文进行解码
+ byte[] encrypted = Base64.decodeBase64(text);
+ // 解密
+ originalArr = cipher.doFinal(encrypted);
+ } catch (Exception e) {
+ throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_ERROR);
+ }
+
+ String plainText;
+ String fromCorpid;
+ try {
+ // 去除补位字符
+ byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);
+ // 分离16位随机字符串,网络字节序和corpId
+ byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
+ int plainTextLegth = Utils.bytes2int(networkOrder);
+ plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET);
+ fromCorpid = new String(Arrays.copyOfRange(bytes, 20 + plainTextLegth, bytes.length), CHARSET);
+ } catch (Exception e) {
+ throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR);
+ }
+
+ // corpid不相同的情况
+ if (!fromCorpid.equals(corpId)) {
+ throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_CORPID_ERROR);
+ }
+ return plainText;
+ }
+
+ /**
+ * 数字签名
+ *
+ * @param token isv token
+ * @param timestamp 时间戳
+ * @param nonce 随机串
+ * @param encrypt 加密文本
+ * @return
+ * @throws DingTalkEncryptException
+ */
+ public String getSignature(String token, String timestamp, String nonce, String encrypt)
+ throws DingTalkEncryptException {
+ try {
+ String[] array = new String[] {token, timestamp, nonce, encrypt};
+ Arrays.sort(array);
+ System.out.println(JSON.toJSONString(array));
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < 4; i++) {
+ sb.append(array[i]);
+ }
+ String str = sb.toString();
+ System.out.println(str);
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ md.update(str.getBytes());
+ byte[] digest = md.digest();
+
+ StringBuffer hexstr = new StringBuffer();
+ String shaHex = "";
+ for (int i = 0; i < digest.length; i++) {
+ shaHex = Integer.toHexString(digest[i] & 0xFF);
+ if (shaHex.length() < 2) {
+ hexstr.append(0);
+ }
+ hexstr.append(shaHex);
+ }
+ return hexstr.toString();
+ } catch (Exception e) {
+ throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);
+ }
+ }
+
+ public static class Utils {
+ public Utils() {
+ }
+
+ public static String getRandomStr(int count) {
+ String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ Random random = new Random();
+ StringBuffer sb = new StringBuffer();
+
+ for (int i = 0; i < count; ++i) {
+ int number = random.nextInt(base.length());
+ sb.append(base.charAt(number));
+ }
+
+ return sb.toString();
+ }
+
+ public static byte[] int2Bytes(int count) {
+ byte[] byteArr = new byte[] {(byte)(count >> 24 & 255), (byte)(count >> 16 & 255), (byte)(count >> 8 & 255),
+ (byte)(count & 255)};
+ return byteArr;
+ }
+
+ public static int bytes2int(byte[] byteArr) {
+ int count = 0;
+
+ for (int i = 0; i < 4; ++i) {
+ count <<= 8;
+ count |= byteArr[i] & 255;
+ }
+
+ return count;
+ }
+ }
+
+ public static class PKCS7Padding {
+ private static final Charset CHARSET = Charset.forName("utf-8");
+ private static final int BLOCK_SIZE = 32;
+
+ public PKCS7Padding() {
+ }
+
+ public static byte[] getPaddingBytes(int count) {
+ int amountToPad = 32 - count % 32;
+ if (amountToPad == 0) {
+ amountToPad = 32;
+ }
+
+ char padChr = chr(amountToPad);
+ String tmp = new String();
+
+ for (int index = 0; index < amountToPad; ++index) {
+ tmp = tmp + padChr;
+ }
+
+ return tmp.getBytes(CHARSET);
+ }
+
+ public static byte[] removePaddingBytes(byte[] decrypted) {
+ int pad = decrypted[decrypted.length - 1];
+ if (pad < 1 || pad > 32) {
+ pad = 0;
+ }
+
+ return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
+ }
+
+ private static char chr(int a) {
+ byte target = (byte)(a & 255);
+ return (char)target;
+ }
+ }
+
+ public static class DingTalkEncryptException extends Exception {
+ public static final int SUCCESS = 0;
+ public static final int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;
+ public static final int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;
+ public static final int ENCRYPTION_NONCE_ILLEGAL = 900003;
+ public static final int AES_KEY_ILLEGAL = 900004;
+ public static final int SIGNATURE_NOT_MATCH = 900005;
+ public static final int COMPUTE_SIGNATURE_ERROR = 900006;
+ public static final int COMPUTE_ENCRYPT_TEXT_ERROR = 900007;
+ public static final int COMPUTE_DECRYPT_TEXT_ERROR = 900008;
+ public static final int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009;
+ public static final int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010;
+ private static Map msgMap = new HashMap();
+ private Integer code;
+
+ static {
+ msgMap.put(0, "成功");
+ msgMap.put(900001, "加密明文文本非法");
+ msgMap.put(900002, "加密时间戳参数非法");
+ msgMap.put(900003, "加密随机字符串参数非法");
+ msgMap.put(900005, "签名不匹配");
+ msgMap.put(900006, "签名计算失败");
+ msgMap.put(900004, "不合法的aes key");
+ msgMap.put(900007, "计算加密文字错误");
+ msgMap.put(900008, "计算解密文字错误");
+ msgMap.put(900009, "计算解密文字长度不匹配");
+ msgMap.put(900010, "计算解密文字corpid不匹配");
+ }
+
+ public Integer getCode() {
+ return this.code;
+ }
+
+ public DingTalkEncryptException(Integer exceptionCode) {
+ super((String)msgMap.get(exceptionCode));
+ this.code = exceptionCode;
+ }
+ }
+ static {
+ try {
+ Security.setProperty("crypto.policy", "limited");
+ RemoveCryptographyRestrictions();
+ } catch (Exception var1) {
+ }
+
+ }
+ private static void RemoveCryptographyRestrictions() throws Exception {
+ Class> jceSecurity = getClazz("javax.crypto.JceSecurity");
+ Class> cryptoPermissions = getClazz("javax.crypto.CryptoPermissions");
+ Class> cryptoAllPermission = getClazz("javax.crypto.CryptoAllPermission");
+ if (jceSecurity != null) {
+ setFinalStaticValue(jceSecurity, "isRestricted", false);
+ PermissionCollection defaultPolicy = (PermissionCollection)getFieldValue(jceSecurity, "defaultPolicy", (Object)null, PermissionCollection.class);
+ if (cryptoPermissions != null) {
+ Map, ?> map = (Map)getFieldValue(cryptoPermissions, "perms", defaultPolicy, Map.class);
+ map.clear();
+ }
+
+ if (cryptoAllPermission != null) {
+ Permission permission = (Permission)getFieldValue(cryptoAllPermission, "INSTANCE", (Object)null, Permission.class);
+ defaultPolicy.add(permission);
+ }
+ }
+
+ }
+ private static Class> getClazz(String className) {
+ Class clazz = null;
+
+ try {
+ clazz = Class.forName(className);
+ } catch (Exception var3) {
+ }
+
+ return clazz;
+ }
+ private static void setFinalStaticValue(Class> srcClazz, String fieldName, Object newValue) throws Exception {
+ Field field = srcClazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ Field modifiersField = Field.class.getDeclaredField("modifiers");
+ modifiersField.setAccessible(true);
+ modifiersField.setInt(field, field.getModifiers() & -17);
+ field.set((Object)null, newValue);
+ }
+ private static T getFieldValue(Class> srcClazz, String fieldName, Object owner, Class dstClazz) throws Exception {
+ Field field = srcClazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return dstClazz.cast(field.get(owner));
+ }
+
+}
\ No newline at end of file
diff --git a/evo-admin/src/main/java/com/evo/ding/DingGlobalParams.java b/evo-admin/src/main/java/com/evo/ding/DingGlobalParams.java
new file mode 100644
index 0000000..f2f3283
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/DingGlobalParams.java
@@ -0,0 +1,91 @@
+package com.evo.ding;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * DingBase
+ *
+ * @author andy.shi
+ * @ClassName:DingBase
+ * @date: 2026年03月02日 8:48
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "ding")
+public class DingGlobalParams {
+
+ public static String baseUrl;
+
+ public static String url;
+
+ public static String clientId;
+
+ public static String clientSecret;
+
+ public static String aesKey;
+
+ public static String token;
+
+ public static String corpId;
+
+
+ public static String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public void setBaseUrl(String baseUrl) {
+ DingGlobalParams.baseUrl = baseUrl;
+ }
+
+ public static String getCorpId() {
+ return corpId;
+ }
+
+ public void setCorpId(String corpId) {
+ DingGlobalParams.corpId = corpId;
+ }
+
+ public static String getAesKey() {
+ return aesKey;
+ }
+
+ public void setAesKey(String aesKey) {
+ DingGlobalParams.aesKey = aesKey;
+ }
+
+ public static String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ DingGlobalParams.token = token;
+ }
+
+ public static String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ DingGlobalParams.url = url;
+ }
+
+ public static String getClientId() {
+ return DingGlobalParams.clientId;
+ }
+
+ public void setClientId(String clientId) {
+ DingGlobalParams.clientId = clientId;
+ }
+
+ public static String getClientSecret() {
+ return clientSecret;
+ }
+
+
+ public void setClientSecret(String clientSecret) {
+ DingGlobalParams.clientSecret = clientSecret;
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/DingUtils.java b/evo-admin/src/main/java/com/evo/ding/DingUtils.java
new file mode 100644
index 0000000..493ec8b
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/DingUtils.java
@@ -0,0 +1,316 @@
+package com.evo.ding;
+
+import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
+import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
+import com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoResponseBody;
+import com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceResponseBody;
+import com.aliyun.tea.TeaException;
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.DingTalkClient;
+import com.dingtalk.api.request.*;
+import com.dingtalk.api.response.*;
+import com.evo.common.core.redis.RedisCache;
+import com.evo.common.utils.StringUtils;
+import com.taobao.api.ApiException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * DingUtils
+ *
+ * @author andy.shi
+ * @ClassName:DingUtils
+ * @date: 2026年03月02日 8:54
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Service
+@Slf4j
+public class DingUtils {
+
+ private static RedisCache redisCache;
+
+ private static String ACCESS_TOKEN_KEY = "ding_access_token";
+
+ @Autowired
+ public DingUtils(RedisCache redisCache) {
+ this.redisCache = redisCache;
+ }
+
+ public static com.aliyun.teaopenapi.Client client(Class> t) throws Exception {
+ com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
+ config.protocol = "https";
+ config.regionId = "central";
+ if(t == com.aliyun.dingtalkoauth2_1_0.Client.class){
+ return new com.aliyun.dingtalkoauth2_1_0.Client(config);
+ }else if(t == com.aliyun.dingtalkworkflow_1_0.Client.class){
+ return new com.aliyun.dingtalkworkflow_1_0.Client(config);
+ }else if(t == com.aliyun.dingtalkstorage_1_0.Client.class){
+ return new com.aliyun.dingtalkstorage_1_0.Client(config);
+ }
+ return null;
+ }
+
+
+ public static String getAccessToken() throws Exception {
+ String token = redisCache.getCacheObject(ACCESS_TOKEN_KEY);
+ if(StringUtils.isEmpty(token)){
+ com.aliyun.dingtalkoauth2_1_0.Client client = (com.aliyun.dingtalkoauth2_1_0.Client) client(com.aliyun.dingtalkoauth2_1_0.Client.class);
+ GetAccessTokenRequest getAccessTokenRequest = new GetAccessTokenRequest().setAppKey(DingGlobalParams.getClientId()).setAppSecret(DingGlobalParams.getClientSecret());
+ try {
+ GetAccessTokenResponse res = client.getAccessToken(getAccessTokenRequest);
+ token = res.getBody().getAccessToken();
+ //7200s- 10s,防止出现读取不超时, 请求超时
+ redisCache.setCacheObject(ACCESS_TOKEN_KEY, token, res.getBody().getExpireIn()-10);
+ } catch (TeaException err) {
+ if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+ // err 中含有 code 和 message 属性,可帮助开发定位问题
+ log.error("获取token出现 TeaException 异常, 异常编码{}, 异常原因{}", err.getCode(), err.getMessage());
+ }
+ } catch (Exception _err) {
+ TeaException err = new TeaException(_err.getMessage(), _err);
+ if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+ // err 中含有 code 和 message 属性,可帮助开发定位问题
+ log.error("获取token出现 Exception 异常, 异常编码{}, 异常原因{}", err.getCode(), err.getMessage());
+ }
+ }
+
+ }
+ return token;
+ }
+
+ //根据手机号获取用户id
+ public static String getUserIdByPhoto(String photo){
+ try {
+ DingTalkClient client = new DefaultDingTalkClient(DingGlobalParams.getUrl()+"v2/user/getbymobile");
+ OapiV2UserGetbymobileRequest req = new OapiV2UserGetbymobileRequest();
+ req.setMobile(photo);
+ OapiV2UserGetbymobileResponse rsp = client.execute(req, getAccessToken());
+ return rsp.getBody();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ //根据手机号获取用户id
+ public static String getUserInfoByUserId(String userId){
+ try {
+ DingTalkClient client = new DefaultDingTalkClient(DingGlobalParams.getUrl()+"v2/user/get");
+ OapiV2UserGetRequest req = new OapiV2UserGetRequest();
+ req.setUserid(userId);
+ OapiV2UserGetResponse rsp = client.execute(req, getAccessToken());
+ return rsp.getBody();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+ /***
+ * 获取班次信息
+ *
+ */
+ public static String getShift(){
+ try {
+ DingTalkClient client = new DefaultDingTalkClient(DingGlobalParams.getUrl()+"attendance/shift/list");
+ OapiAttendanceShiftListRequest req = new OapiAttendanceShiftListRequest();
+ req.setOpUserId("03491015494921375480");
+ OapiAttendanceShiftListResponse rsp = client.execute(req, getAccessToken());
+ return rsp.getBody();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ public static OapiAttendanceShiftQueryResponse.TopShiftVo getShiftDetail(Long id){
+ try {
+ DingTalkClient client = new DefaultDingTalkClient(DingGlobalParams.getBaseUrl()+"topapi/attendance/shift/query");
+ OapiAttendanceShiftQueryRequest req = new OapiAttendanceShiftQueryRequest();
+ req.setOpUserId("03491015494921375480");
+ req.setShiftId(id);
+ OapiAttendanceShiftQueryResponse rsp = client.execute(req, getAccessToken());
+ return rsp.getResult();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static OapiAttendanceGroupQueryResponse.TopSimpleGroupVO getGroupDetail(Long id){
+ try {
+ DingTalkClient client = new DefaultDingTalkClient(DingGlobalParams.getBaseUrl()+"topapi/attendance/group/query");
+ OapiAttendanceGroupQueryRequest req = new OapiAttendanceGroupQueryRequest();
+ req.setOpUserId("03491015494921375480");
+ req.setGroupId(id);
+ OapiAttendanceGroupQueryResponse rsp = client.execute(req, getAccessToken());
+ return rsp.getResult();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /***
+ * 获取考勤组信息
+ *
+ */
+ public static String getAttendanceGroupList(){
+ try {
+ DingTalkClient client = new DefaultDingTalkClient(DingGlobalParams.getUrl()+"attendance/getsimplegroups");
+ OapiAttendanceGetsimplegroupsRequest req = new OapiAttendanceGetsimplegroupsRequest();
+ OapiAttendanceGetsimplegroupsResponse rsp = client.execute(req, getAccessToken());
+ return rsp.getBody();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+
+ /***
+ * 获取打卡详情
+ * @param dingUserIds 钉钉userId
+ * @param checkDateFrom 开始时间 yyyy-MM-dd HH:mm:ss
+ * @param checkDateTo 结束时间 yyyy-MM-dd HH:mm:ss
+ */
+ public static String getListRecord(List dingUserIds, String checkDateFrom, String checkDateTo){
+ try {
+ DingTalkClient client = new DefaultDingTalkClient(DingGlobalParams.getBaseUrl()+"attendance/listRecord");
+ OapiAttendanceListRecordRequest req = new OapiAttendanceListRecordRequest();
+ req.setUserIds(dingUserIds);
+ req.setCheckDateFrom(checkDateFrom);
+ req.setCheckDateTo(checkDateTo);
+ req.setIsI18n(false);
+ OapiAttendanceListRecordResponse rsp = client.execute(req, getAccessToken());
+ System.out.println(rsp.getBody());
+ return rsp.getBody();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+
+
+ /***
+ * 获取审批详情
+ * @param processInstanceId 审批示例id
+ */
+ public static GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult getProcessInstanceInfo(String processInstanceId){
+ try {
+ com.aliyun.dingtalkworkflow_1_0.Client client = (com.aliyun.dingtalkworkflow_1_0.Client) client(com.aliyun.dingtalkworkflow_1_0.Client.class);
+ com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceHeaders getProcessInstanceHeaders = new com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceHeaders();
+ getProcessInstanceHeaders.xAcsDingtalkAccessToken = getAccessToken();
+ com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceRequest getProcessInstanceRequest = new com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceRequest();
+ getProcessInstanceRequest.setProcessInstanceId(processInstanceId);
+ try {
+ return client.getProcessInstanceWithOptions(getProcessInstanceRequest, getProcessInstanceHeaders, new com.aliyun.teautil.models.RuntimeOptions()).getBody().getResult();
+ } catch (TeaException err) {
+ if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+ // err 中含有 code 和 message 属性,可帮助开发定位问题
+ log.error("获取token出现 TeaException 异常, 异常编码{}, 异常原因{}", err.getCode(), err.getMessage());
+ }
+ } catch (Exception _err) {
+ TeaException err = new TeaException(_err.getMessage(), _err);
+ if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+ // err 中含有 code 和 message 属性,可帮助开发定位问题
+ log.error("获取token出现 Exception 异常, 异常编码{}, 异常原因{}", err.getCode(), err.getMessage());
+ }
+
+ }
+ } catch (Exception e) {
+ log.error("=============>>> 获取审批实例出现异常, 异常原因为: {}", e.getMessage());
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /***
+ * 获取排班详情
+ * @param userId
+ * @param planId
+ * @return
+ */
+ public static String getPlanInfo(String userId, String planId){
+ try {
+ DingTalkClient client = new DefaultDingTalkClient(DingGlobalParams.getBaseUrl()+"attendance/schedule/result/listbyids");
+ OapiAttendanceScheduleResultListbyidsRequest req = new OapiAttendanceScheduleResultListbyidsRequest();
+ req.setOpUserId(userId);
+ req.setScheduleIds(planId);
+ OapiAttendanceScheduleResultListbyidsResponse rsp = client.execute(req, getAccessToken());
+ return rsp.getBody();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+
+ public static String getDeptInfo(Long deptId){
+ try {
+ DingTalkClient client = new DefaultDingTalkClient(DingGlobalParams.getUrl()+"v2/department/get");
+ OapiV2DepartmentGetRequest req = new OapiV2DepartmentGetRequest();
+ req.setDeptId(deptId);
+ OapiV2DepartmentGetResponse rsp = client.execute(req, getAccessToken());
+ return rsp.getBody();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+
+ public static GetFileUploadInfoResponseBody resourceUrls() {
+
+ try {
+ com.aliyun.dingtalkstorage_1_0.Client client = (com.aliyun.dingtalkstorage_1_0.Client) client(com.aliyun.dingtalkstorage_1_0.Client.class);
+ com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoHeaders getFileUploadInfoHeaders = new com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoHeaders();
+ getFileUploadInfoHeaders.xAcsDingtalkAccessToken = getAccessToken();
+ com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoRequest getFileUploadInfoRequest = new com.aliyun.dingtalkstorage_1_0.models.GetFileUploadInfoRequest();
+ getFileUploadInfoRequest.setUnionId("IQ4KFIKVugR3UYfNOii5VzwiEiE");
+ getFileUploadInfoRequest.setProtocol("HEADER_SIGNATURE");
+ return client.getFileUploadInfoWithOptions("28629173113", getFileUploadInfoRequest, getFileUploadInfoHeaders, new com.aliyun.teautil.models.RuntimeOptions()).getBody();
+ } catch (TeaException err) {
+ if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+ // err 中含有 code 和 message 属性,可帮助开发定位问题
+ log.error("获取resourceUrls出现 TeaException 异常, 异常编码{}, 异常原因{}", err.getCode(), err.getMessage());
+ }
+
+ } catch (Exception _err) {
+ TeaException err = new TeaException(_err.getMessage(), _err);
+ if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+ // err 中含有 code 和 message 属性,可帮助开发定位问题
+ log.error("获取resourceUrls出现 Exception 异常, 异常编码{}, 异常原因{}", err.getCode(), err.getMessage());
+
+ }
+ }
+ return null;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/domain/DingGroup.java b/evo-admin/src/main/java/com/evo/ding/domain/DingGroup.java
new file mode 100644
index 0000000..0816658
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/domain/DingGroup.java
@@ -0,0 +1,25 @@
+package com.evo.ding.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * DingsShift
+ *
+ * @author andy.shi
+ * @ClassName:DingsShift
+ * @date: 2026年03月04日 13:50
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Data
+public class DingGroup implements Serializable {
+
+ /** 主键 */
+ private Long id;
+ /** 班次名称 */
+ public String name;
+ /** 钉钉的班次id */
+ public String dingId;
+
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/domain/DingShift.java b/evo-admin/src/main/java/com/evo/ding/domain/DingShift.java
new file mode 100644
index 0000000..1d98439
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/domain/DingShift.java
@@ -0,0 +1,33 @@
+package com.evo.ding.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * DingsShift
+ *
+ * @author andy.shi
+ * @ClassName:DingsShift
+ * @date: 2026年03月04日 13:50
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Data
+public class DingShift implements Serializable {
+
+ /** 主键 */
+ @TableId
+ private Long id;
+ /** 班次名称 */
+ public String name;
+ /** 钉钉的班次id */
+ public String dingId;
+ /** 工作时长 */
+ public Integer workHour;
+ /** 上班基准时间节点 */
+ public String onDutyCheckTime;
+ /** 下班基准时间节点 */
+ public String offDutyCheckTime;
+
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/domain/DingShiftGroup.java b/evo-admin/src/main/java/com/evo/ding/domain/DingShiftGroup.java
new file mode 100644
index 0000000..475e7e6
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/domain/DingShiftGroup.java
@@ -0,0 +1,35 @@
+package com.evo.ding.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * DingsShift
+ *
+ * @author andy.shi
+ * @ClassName:DingsShift
+ * @date: 2026年03月04日 13:50
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Data
+public class DingShiftGroup implements Serializable {
+
+ /** 主键 */
+ private Long id;
+ /** 钉钉的groupId */
+ public String groupId;
+ /** 钉钉的班次id */
+ public String shiftId;
+ /** type 打卡类型 */
+ public String type;
+ /** type 打卡类型 */
+ public String checkTime;
+
+ @TableField(exist = false)
+ private String shiftName;
+ @TableField(exist = false)
+ private String groupName;
+
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/mapper/DingGroupMapper.java b/evo-admin/src/main/java/com/evo/ding/mapper/DingGroupMapper.java
new file mode 100644
index 0000000..42d3334
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/mapper/DingGroupMapper.java
@@ -0,0 +1,19 @@
+package com.evo.ding.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.evo.ding.domain.DingGroup;
+import com.evo.ding.domain.DingShift;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * DingGroupMapper
+ *
+ * @author andy.shi
+ * @ClassName:DingsShiftGroupMapper
+ * @date: 2026年03月05日 14:47
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+public interface DingGroupMapper extends BaseMapper {
+
+ public DingGroup selectByDingId(@Param("dingId") String dingId);
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/mapper/DingShiftGroupMapper.java b/evo-admin/src/main/java/com/evo/ding/mapper/DingShiftGroupMapper.java
new file mode 100644
index 0000000..c047dd4
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/mapper/DingShiftGroupMapper.java
@@ -0,0 +1,24 @@
+package com.evo.ding.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.evo.ding.domain.DingShiftGroup;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * DingsShiftGroupMapper
+ *
+ * @author andy.shi
+ * @ClassName:DingsShiftGroupMapper
+ * @date: 2026年03月05日 14:47
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+public interface DingShiftGroupMapper extends BaseMapper {
+
+ DingShiftGroup selectByGroupIdAndTypeAndCheckTime(@Param("groupId") String groupId, @Param("shiftId")String shiftId, @Param("type") String type, @Param("checkTime") String checkTime);
+
+ int updateCheckTimeByShiftIdAndType(@Param("shiftId")String shiftId, @Param("type")String type, @Param("checkTime")String checkTime);
+
+ List selectListByShiftId(@Param("shiftId") String shiftId);
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/mapper/DingShiftMapper.java b/evo-admin/src/main/java/com/evo/ding/mapper/DingShiftMapper.java
new file mode 100644
index 0000000..3b3d258
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/mapper/DingShiftMapper.java
@@ -0,0 +1,24 @@
+package com.evo.ding.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.evo.ding.domain.DingShift;
+import com.evo.system.domain.SysStaff;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/**
+ * DingsShiftGroupMapper
+ *
+ * @author andy.shi
+ * @ClassName:DingsShiftGroupMapper
+ * @date: 2026年03月05日 14:47
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+public interface DingShiftMapper extends BaseMapper {
+
+ public DingShift selectByDingId(@Param("dingId") String dingId);
+
+ public List selectList(DingShift dingShift);
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/DingBasicExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/DingBasicExchangeProcessor.java
new file mode 100644
index 0000000..929bd43
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/DingBasicExchangeProcessor.java
@@ -0,0 +1,108 @@
+package com.evo.ding.processor;
+
+import com.evo.attendance.service.IRzAttendanceService;
+import com.evo.attendance.service.RzAttendanceDetailService;
+import com.evo.common.utils.Collections;
+import com.evo.common.utils.spring.SpringUtils;
+import com.evo.ding.service.DingShiftGroupService;
+import com.evo.ding.service.DingShiftService;
+import com.evo.personnelMatters.service.IRzLeaveDetailService;
+import com.evo.personnelMatters.service.IRzLeaveService;
+import com.evo.personnelMatters.service.IRzOverTimeDetailService;
+import com.evo.personnelMatters.service.IRzOverTimeService;
+import com.evo.system.domain.SysStaff;
+import com.evo.system.service.ISysStaffService;
+
+import java.util.Map;
+
+/**
+ * 钉钉回调基础类型 DingBaseExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:DingBaseExchangeProcessor
+ * @date: 2026年03月12日 11:05
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+public interface DingBasicExchangeProcessor {
+
+ final Map p = Collections.emptyMap();
+ //审批状态
+ String APPROVE_STATUS = "COMPLETED";
+
+ String APPROVE_RESULT = "AGREE";
+
+ default ISysStaffService getISysStaffService(){
+ if(!p.containsKey("sysStaffService")){
+ p.put("sysStaffService", SpringUtils.getBean(ISysStaffService.class));
+ }
+ return (ISysStaffService)p.get("sysStaffService");
+ }
+
+ default IRzAttendanceService getIRzAttendanceService(){
+ if(!p.containsKey("rzAttendanceService")){
+ p.put("rzAttendanceService", SpringUtils.getBean(IRzAttendanceService.class));
+ }
+ return (IRzAttendanceService)p.get("rzAttendanceService");
+ }
+
+ default RzAttendanceDetailService getRzAttendanceDetailService(){
+ if(!p.containsKey("rzAttendanceDetailService")){
+ p.put("rzAttendanceDetailService", SpringUtils.getBean(RzAttendanceDetailService.class));
+ }
+ return (RzAttendanceDetailService)p.get("rzAttendanceDetailService");
+ }
+
+ default IRzOverTimeService getIRzOverTimeService(){
+ if(!p.containsKey("rzOverTimeService")){
+ p.put("rzOverTimeService", SpringUtils.getBean(IRzOverTimeService.class));
+ }
+ return (IRzOverTimeService)p.get("rzOverTimeService");
+ }
+
+ default IRzOverTimeDetailService getIRzOverTimeDetailService(){
+ if(!p.containsKey("rzOverTimeDetailService")){
+ p.put("rzOverTimeDetailService", SpringUtils.getBean(IRzOverTimeDetailService.class));
+ }
+ return (IRzOverTimeDetailService)p.get("rzOverTimeDetailService");
+ }
+
+ default DingShiftService getDingShiftService(){
+ if(!p.containsKey("dingShiftService")){
+ p.put("dingShiftService", SpringUtils.getBean(DingShiftService.class));
+ }
+ return (DingShiftService)p.get("dingShiftService");
+ }
+
+ default DingShiftGroupService getDingShiftGroupService(){
+ if(!p.containsKey("dingShiftGroupService")){
+ p.put("dingShiftGroupService", SpringUtils.getBean(DingShiftGroupService.class));
+ }
+ return (DingShiftGroupService)p.get("dingShiftGroupService");
+ }
+
+ default IRzLeaveService getIRzLeaveService(){
+ if(!p.containsKey("rzLeaveService")){
+ p.put("rzLeaveService", SpringUtils.getBean(IRzLeaveService.class));
+ }
+ return (IRzLeaveService)p.get("rzLeaveService");
+ }
+
+ default IRzLeaveDetailService getIRzLeaveDetailService(){
+ if(!p.containsKey("rzLeaveDetailService")){
+ p.put("rzLeaveDetailService", SpringUtils.getBean(IRzLeaveDetailService.class));
+ }
+ return (IRzLeaveDetailService)p.get("rzLeaveDetailService");
+ }
+
+
+
+
+
+ default SysStaff getSysStaffByDingUserId(String dingUserId){
+ SysStaff sysStaff = getISysStaffService().selectSysStaffByDingUserId(dingUserId);
+ if(sysStaff == null){
+ throw new RuntimeException("未查询到用户信息, 用户的DingUserId为:"+dingUserId);
+ }
+ return sysStaff;
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/AttendanceApproveStatusChangeExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/AttendanceApproveStatusChangeExchangeProcessor.java
new file mode 100644
index 0000000..83db63e
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/AttendanceApproveStatusChangeExchangeProcessor.java
@@ -0,0 +1,60 @@
+package com.evo.ding.processor.approveStatus;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceResponseBody;
+import com.evo.ding.processor.DingBasicExchangeProcessor;
+import com.evo.system.domain.SysStaff;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+/**
+ * 请假、加班、出差、外出状态变更事件 回调 InstanceChangeExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:InstanceChangeExchangeProcessor
+ * @date: 2026年03月09日 15:25
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+
+public interface AttendanceApproveStatusChangeExchangeProcessor extends DingBasicExchangeProcessor {
+
+ Logger logger = LoggerFactory.getLogger(AttendanceApproveStatusChangeExchangeProcessor.class);
+ //请假
+ String APPROVE_TYPE_LEAVE = "LEAVE";
+ //加班
+ String APPROVE_TYPE_OVERTIME = "OVERTIME";
+ //请假
+ String APPROVE_TYPE_OUT = "OUT";
+ //撤销
+ String BIZ_ACTION_REVOKE = "REVOKE";
+
+ boolean accept(String approveType, String result, String status);
+
+ void exchange(String processInstanceId, GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult json);
+
+ default JSONObject getExtValue(GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult json, String componentType) {
+ List formComponentValueList = json.getFormComponentValues();
+ GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultFormComponentValues formComponentValue = formComponentValueList.stream().filter(data -> componentType.equals(data.getComponentType())).findAny().orElse(null);
+ if(formComponentValue != null){
+ return JSON.parseObject(formComponentValue.getExtValue());
+ }
+ throw new RuntimeException("未查询到相关审批信息, 审批类型为:"+componentType+"; 参数信息为: "+ json.toString());
+ }
+
+ default JSONArray getValue(GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult json, String componentType) {
+ List formComponentValueList = json.getFormComponentValues();
+ GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultFormComponentValues formComponentValue = formComponentValueList.stream().filter(data -> componentType.equals(data.getComponentType())).findAny().orElse(null);
+ if(formComponentValue != null){
+ return JSON.parseArray(formComponentValue.getValue());
+ }
+ throw new RuntimeException("未查询到相关审批信息, 审批类型为:"+componentType+"; 参数信息为: "+ json.toString());
+ }
+
+
+
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/impl/ApproveStatusLeaveExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/impl/ApproveStatusLeaveExchangeProcessor.java
new file mode 100644
index 0000000..8757420
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/impl/ApproveStatusLeaveExchangeProcessor.java
@@ -0,0 +1,127 @@
+package com.evo.ding.processor.approveStatus.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceResponseBody;
+import com.evo.common.constant.Constants;
+import com.evo.common.core.domain.entity.SysDictData;
+import com.evo.common.utils.DataUtils;
+import com.evo.common.utils.StringUtils;
+import com.evo.ding.processor.approveStatus.AttendanceApproveStatusChangeExchangeProcessor;
+import com.evo.personnelMatters.domain.RzLeave;
+import com.evo.personnelMatters.domain.RzLeaveDetail;
+import com.evo.system.domain.SysStaff;
+import com.evo.system.service.ISysDictDataService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 请假事件处理 ApproveStatusLeaveExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:ApproveStatusLeaveExchangeProcessor
+ * @date: 2026年03月12日 13:32
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Slf4j
+@Service
+public class ApproveStatusLeaveExchangeProcessor implements AttendanceApproveStatusChangeExchangeProcessor {
+
+ @Autowired
+ ISysDictDataService sysDictDataService;
+
+ @Override
+ public boolean accept(String approveType, String result, String status) {
+ return APPROVE_TYPE_LEAVE.equals(approveType.toUpperCase()) && APPROVE_STATUS.equals(status.toUpperCase()) && APPROVE_RESULT.equals(result.toUpperCase());
+ }
+
+ @Override
+ public void exchange(String processInstanceId, GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult json) {
+ try {
+
+ //如果是撤销, 执行撤销
+ if(BIZ_ACTION_REVOKE.equals(json.getBizAction())){
+ revoke(processInstanceId);
+ }else{
+ leave(processInstanceId, getSysStaffByDingUserId(json.getOriginatorUserId()), json);
+ }
+ } catch (Exception e) {
+ log.error("执行钉钉 -["+(BIZ_ACTION_REVOKE.equals(json.getBizAction()) ? "撤销" : "请假")+"审批]-回调出现异常, 异常原因为:{}", e.getMessage());
+ e.printStackTrace();
+ }
+
+ }
+
+
+ /***
+ * 撤销假期
+ * @param processInstanceId 钉钉审批单详情
+ */
+ public void revoke(String processInstanceId){
+ RzLeaveDetail rzLeaveDetail = getIRzLeaveDetailService().selectRzLeaveDetailByProcessInstanceId(processInstanceId);
+ if(rzLeaveDetail != null){
+ getIRzLeaveDetailService().deleteRzLeaveDetailById(rzLeaveDetail.getId());
+ }
+ }
+
+ /***
+ * 请假
+ * @param processInstanceId 钉钉审批单详情
+ * @param sysStaff 平台用户
+ * @param json 请求数据信息
+ */
+ public void leave(String processInstanceId, SysStaff sysStaff, GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult json) {
+
+ JSONObject extValueJson = getExtValue(json, "DDHolidayField");
+ //解析假期类型
+ JSONObject extension = JSON.parseObject(extValueJson.getString("extension"));
+ //查询假期类型
+ String tag = extension.getString("tag");
+ SysDictData sysDictData = sysDictDataService.selectDictDataListByType("holidays_type").stream().filter(data-> StringUtils.equals(data.getDictLabel(),tag)).findFirst().orElse(null);
+ if(sysDictData != null){
+ //假期类型的code
+ Long dictCode = sysDictData.getDictCode();
+ JSONArray detailList = extValueJson.getJSONArray("detailList");
+ Date beginTime = null;
+ Date endTime = null;
+ //如果大于1 证明是多天请假, 需要排序
+ if(detailList.size() > 1){
+ detailList.sort((a,b)->{
+ return ((JSONObject) a).getLong("workDate").compareTo(((JSONObject) b).getLong("workDate"));
+ });
+ //获取第一天
+ beginTime = new Date(detailList.getJSONObject(0).getJSONObject("approveInfo").getLong("fromTime"));
+ //获取最后一天
+ endTime = new Date(detailList.getJSONObject(detailList.size()-1).getJSONObject("approveInfo").getLong("toTime"));
+ }else{
+ //只请一天假
+ JSONObject approveInfo = detailList.getJSONObject(0).getJSONObject("approveInfo");
+ beginTime = new Date(approveInfo.getLong("fromTime"));
+ //获取最后一天
+ endTime = new Date(approveInfo.getLong("toTime"));
+ }
+
+ RzLeave rzLeave = getIRzLeaveService().selectRzLeaveByUserId(sysStaff.getUserId());
+ RzLeaveDetail rzLeaveDetail = new RzLeaveDetail();
+ rzLeaveDetail.setName(sysStaff.getName());
+ rzLeaveDetail.setLeaveStartTime(beginTime);
+ rzLeaveDetail.setLeaveEndTime(endTime);
+ rzLeaveDetail.setLeaveId(rzLeave.getId());
+ rzLeaveDetail.setType(dictCode);
+// if("调休".equals(tag)){
+ rzLeaveDetail.setLeaveHour(new BigDecimal(extValueJson.getDouble("durationInDay")).multiply(Constants.DAY_WORK_HOUR).intValue());
+// }else{
+// rzLeaveDetail.setLeaveHour(extValueJson.getInteger("durationInHour"));
+// }
+ rzLeaveDetail.setRemarks("钉钉"+tag);
+ rzLeaveDetail.setProcessInstanceId(processInstanceId);
+ getIRzLeaveDetailService().insertRzLeaveDetail(rzLeaveDetail);
+ }
+ }
+
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/impl/ApproveStatusOutExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/impl/ApproveStatusOutExchangeProcessor.java
new file mode 100644
index 0000000..ce53ebe
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/impl/ApproveStatusOutExchangeProcessor.java
@@ -0,0 +1,127 @@
+package com.evo.ding.processor.approveStatus.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceResponseBody;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.evo.attendance.domain.RzAttendance;
+import com.evo.attendance.domain.RzAttendanceDetail;
+import com.evo.attendance.mapper.RzAttendanceMapper;
+import com.evo.attendance.service.IRzAttendanceService;
+import com.evo.attendance.service.RzAttendanceDetailService;
+import com.evo.common.core.domain.entity.SysDictData;
+import com.evo.common.utils.ParamUtils;
+import com.evo.common.utils.StringUtils;
+import com.evo.ding.processor.approveStatus.AttendanceApproveStatusChangeExchangeProcessor;
+import com.evo.personnelMatters.domain.RzLeave;
+import com.evo.personnelMatters.domain.RzLeaveDetail;
+import com.evo.system.domain.SysStaff;
+import com.evo.system.service.ISysDictDataService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.time.Instant;
+import java.util.Date;
+
+/**
+ * 出差事件处理 ApproveStatusOutExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:ApproveStatusLeaveExchangeProcessor
+ * @date: 2026年03月12日 13:32
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Slf4j
+@Service
+public class ApproveStatusOutExchangeProcessor implements AttendanceApproveStatusChangeExchangeProcessor {
+
+ @Resource
+ private RzAttendanceMapper rzAttendanceMapper;
+ @Resource
+ private RzAttendanceDetailService rzAttendanceDetailService;
+
+ @Override
+ public boolean accept(String approveType, String result, String status) {
+ return APPROVE_TYPE_OUT.equals(approveType.toUpperCase()) && APPROVE_STATUS.equals(status.toUpperCase()) && APPROVE_RESULT.equals(result.toUpperCase());
+ }
+
+ @Override
+ public void exchange(String processInstanceId, GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult json) {
+ try {
+ rzAttendance(json, BIZ_ACTION_REVOKE.equals(json.getBizAction()));
+ } catch (Exception e) {
+ log.error("执行钉钉 -[出差审批]-回调出现异常, 异常原因为:{}", e.getMessage());
+ e.printStackTrace();
+ }
+
+ }
+ /***
+ *
+ * @param json 审批单数据
+ * @param isRevoke 是否为撤回
+ */
+ public void rzAttendance(GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult json, Boolean isRevoke){
+ SysStaff sysStaff = getSysStaffByDingUserId(json.getOriginatorUserId());
+ //出差
+ JSONArray valueJson = getValue(json, "TableField");
+ JSONObject jsonObject = valueJson.getJSONObject(0);
+ JSONArray rowValue = jsonObject.getJSONArray("rowValue");
+ for (int i = 0; i < rowValue.size(); i++) {
+ JSONObject rowValueObject = rowValue.getJSONObject(i);
+ if ("duration".equals(rowValueObject.getString("bizAlias"))) {
+ JSONObject extendValue = rowValueObject.getJSONObject("extendValue");
+ JSONArray detailList = extendValue.getJSONArray("detailList");
+ for (int j = 0; j < detailList.size(); j++) {
+ JSONObject detailObject = detailList.getJSONObject(j);
+ Date formatWorkDate = Date.from(Instant.ofEpochMilli(detailObject.getLong("workDate")));
+ //检查是否为假期, 如果为假期, 不做记录, 只记录不是假期的数据, 假期的外出, 需要提交加班审批
+ if (!ParamUtils.checkHoliday(formatWorkDate)) {
+ JSONObject classInfo = detailObject.getJSONObject("classInfo");
+ JSONObject section = classInfo.getJSONArray("sections").getJSONObject(0);
+ Long startTime = section.getLong("startTime");
+ Long endTime = section.getLong("endTime");
+ Integer durationInHour = detailObject.getJSONObject("approveInfo").getInteger("durationInHour");
+ Date formatStartTime = Date.from(Instant.ofEpochMilli(startTime));
+ Date formatEndTime = Date.from(Instant.ofEpochMilli(endTime));
+ //通过workDate 获取哪一天的卡
+ RzAttendance attendance = rzAttendanceMapper.queryNowDayAttendanceByStatisticalIdAndDate(sysStaff.getUserId(), formatStartTime);
+ if(isRevoke){
+ //清除打卡记录
+ rzAttendanceDetailService.remove(new LambdaQueryWrapper().eq(RzAttendanceDetail::getAttendanceId, attendance.getId()));
+ //重置打卡信息
+ attendance.setRules("");
+ attendance.setWorkStartTime(null);
+ attendance.setWorkEndTime(null);
+ attendance.setWorkSum(BigDecimal.valueOf(0));
+ attendance.setNightNumber(0);
+ attendance.setMiddleShiftNumber(0);
+ }else{
+ //生成打卡记录
+ rzAttendanceDetailService.addDetail(attendance, "上班卡", "/", formatStartTime, "出差自动打卡", "");
+ rzAttendanceDetailService.addDetail(attendance, "下班卡", "/", formatEndTime, "出差自动打卡", "");
+ //上班
+ attendance.setRules("上班卡");
+ attendance.setWorkStartTime(formatStartTime);
+ attendance.setWorkEndTime(formatEndTime);
+ attendance.setWorkSum(BigDecimal.valueOf(durationInHour));
+ if (attendance.getWorkSum().intValue() >= 12 && attendance.getWorkStartTime() != null && attendance.getWorkStartTime().getHours() > 12) {
+ attendance.setNightNumber(1);
+ } else if (attendance.getWorkSum().intValue() >= 8 && attendance.getWorkStartTime() != null && attendance.getWorkStartTime().getHours() >= 12 && attendance.getWorkStartTime().getHours() < 21) {
+ attendance.setMiddleShiftNumber(1);
+ }
+ }
+
+ rzAttendanceMapper.updateRzAttendance(attendance);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/impl/ApproveStatusOvertimeExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/impl/ApproveStatusOvertimeExchangeProcessor.java
new file mode 100644
index 0000000..52a7106
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/approveStatus/impl/ApproveStatusOvertimeExchangeProcessor.java
@@ -0,0 +1,119 @@
+package com.evo.ding.processor.approveStatus.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceResponseBody;
+import com.evo.attendance.domain.RzAttendanceStatistical;
+import com.evo.attendance.domain.RzSpecialAttendance;
+import com.evo.attendance.domain.RzSpecialOverTime;
+import com.evo.attendance.mapper.RzAttendanceStatisticalMapper;
+import com.evo.attendance.mapper.RzSpecialAttendanceMapper;
+import com.evo.attendance.mapper.RzSpecialOverTimeMapper;
+import com.evo.attendance.service.IRzAttendanceStatisticalService;
+import com.evo.attendance.service.IRzSpecialAttendanceService;
+import com.evo.common.core.domain.entity.SysDictData;
+import com.evo.common.utils.DataUtils;
+import com.evo.common.utils.DateUtils;
+import com.evo.common.utils.StringUtils;
+import com.evo.ding.processor.approveStatus.AttendanceApproveStatusChangeExchangeProcessor;
+import com.evo.personnelMatters.domain.RzLeave;
+import com.evo.personnelMatters.domain.RzLeaveDetail;
+import com.evo.system.domain.SysStaff;
+import com.evo.system.service.ISysDictDataService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ *加班事件处理 ApproveStatusOvertimeExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:ApproveStatusLeaveExchangeProcessor
+ * @date: 2026年03月12日 13:32
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Slf4j
+@Service
+public class ApproveStatusOvertimeExchangeProcessor implements AttendanceApproveStatusChangeExchangeProcessor {
+
+ @Autowired
+ IRzSpecialAttendanceService rzSpecialAttendanceService;
+ @Resource
+ private RzSpecialOverTimeMapper rzSpecialOverTimeMapper;
+ @Resource
+ private RzAttendanceStatisticalMapper rzAttendanceStatisticalMapper;
+
+ @Override
+ public boolean accept(String approveType, String result, String status) {
+ //加班不在对接
+ return false; //APPROVE_TYPE_OVERTIME.equals(approveType.toUpperCase()) && APPROVE_STATUS.equals(status.toUpperCase()) && APPROVE_RESULT.equals(result.toUpperCase());
+ }
+
+ @Override
+ @Transactional
+ public void exchange(String processInstanceId, GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult result) {
+ try {
+ SysStaff sysStaff = getSysStaffByDingUserId(result.getOriginatorUserId());
+ List formComponentValueList = result.getFormComponentValues();
+ GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultFormComponentValues startComponentValue = formComponentValueList.stream().filter(data -> "开始时间".equals(data.getName())).findAny().orElse(null);
+ GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultFormComponentValues endValue = formComponentValueList.stream().filter(data -> "结束时间".equals(data.getName())).findAny().orElse(null);
+ GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultFormComponentValues durationValue = formComponentValueList.stream().filter(data -> "时长".equals(data.getName())).findAny().orElse(null);
+ if(startComponentValue != null && endValue != null && durationValue != null){
+ Date startDate = DateUtils.parseDate(startComponentValue.getValue(), "yyyy-MM-dd HH:mm");
+ Date endDate = DateUtils.parseDate(endValue.getValue(), "yyyy-MM-dd HH:mm");
+ BigDecimal duration = new BigDecimal(durationValue.getValue());
+ //获取加班详情数据
+ RzSpecialAttendance rzSpecialAttendance = rzSpecialAttendanceService.selectRzSpecialAttendanceByUserIdAndDate(sysStaff.getUserId(), startDate);
+ //如果没有, 则新增
+ if(rzSpecialAttendance == null){
+ rzSpecialAttendance = new RzSpecialAttendance();
+ rzSpecialAttendance.setAttendanceDate(startDate);
+ rzSpecialAttendance.setStaffId(sysStaff.getUserId());
+ rzSpecialAttendance.setName(sysStaff.getName());
+ rzSpecialAttendance.setDeptId(sysStaff.getDeptId());
+ rzSpecialAttendance.setDelFlag("0");
+ rzSpecialAttendance.setCreateBy("admin");
+ rzSpecialAttendance.setCreateTime(new Date());
+ }
+ rzSpecialAttendance.setWorkStartTime(startDate);
+ rzSpecialAttendance.setWorkEndTime(endDate);
+ rzSpecialAttendance.setWorkHours(duration);
+ rzSpecialAttendanceService.saveOrUpdate(rzSpecialAttendance);
+
+ //获取加班统计数据
+ RzSpecialOverTime rzSpecialOverTime = rzSpecialOverTimeMapper.selectRzSpecialOverTimeByUserIdAndDate(rzSpecialAttendance.getStaffId(),rzSpecialAttendance.getAttendanceDate());
+ if(ObjectUtils.isEmpty(rzSpecialOverTime)){
+ rzSpecialOverTime = new RzSpecialOverTime();
+ rzSpecialOverTime.setUserId(sysStaff.getUserId());
+ rzSpecialOverTime.setName(sysStaff.getName());
+ rzSpecialOverTime.setDeptId(sysStaff.getDeptId());
+ rzSpecialOverTime.setDelFlag("0");
+ rzSpecialOverTime.setOverDate(startDate);
+ rzSpecialOverTime.setCreateTime(new Date());
+ rzSpecialOverTime.setCreateBy("admin");
+ rzSpecialOverTimeMapper.insertRzSpecialOverTime(rzSpecialOverTime);
+ }
+ if(DataUtils.DEFAULT_VALUE.compareTo(rzSpecialAttendance.getWorkHours()) != 0){
+ rzSpecialOverTime.setSickHours(DataUtils.findDefaultValue(rzSpecialOverTime.getSickHours(), new BigDecimal(0)).add(rzSpecialAttendance.getWorkHours()));
+ rzSpecialOverTimeMapper.updateRzSpecialOverTime(rzSpecialOverTime);
+ //更新加班统计
+ RzAttendanceStatistical rzAttendanceStatistical = rzAttendanceStatisticalMapper.getRzAttendanceStatisticalByDateAndName(rzSpecialAttendance.getStaffId(),rzSpecialAttendance.getAttendanceDate());
+ rzAttendanceStatistical.setOverTimeHours(DataUtils.findDefaultValue(rzAttendanceStatistical.getOverTimeHours(), new BigDecimal(0)).add(rzSpecialAttendance.getWorkHours()));
+ rzAttendanceStatisticalMapper.updateRzAttendanceStatistical(rzAttendanceStatistical);
+ }
+ }
+ } catch (Exception e) {
+ log.error("执行钉钉 -[加班审批]-回调出现异常, 异常原因为:{}", e.getMessage());
+ e.printStackTrace();
+ }
+
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/callback/DingCallBackExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/callback/DingCallBackExchangeProcessor.java
new file mode 100644
index 0000000..ca7ab11
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/callback/DingCallBackExchangeProcessor.java
@@ -0,0 +1,37 @@
+package com.evo.ding.processor.callback;
+
+import com.alibaba.fastjson2.JSONObject;
+
+/**
+ * 钉钉回调入口 DingCallBackExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:DingCallBackExchanageProcessor
+ * @date: 2026年03月12日 8:49
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+public interface DingCallBackExchangeProcessor {
+
+ //入职
+ static final String EVENT_TYPE_USER_ADD_ORG = "user_add_org";
+ //离职
+ static final String EVENT_TYPE_USER_LEAVE_ORG = "user_leave_org";
+ //打卡
+ static final String EVENT_TYPE_CHECK_RECORD = "attendance_check_record";
+ //审批实例订阅-> 补卡
+ static final String EVENT_TYPE_INSTANCE_CHANGE = "bpms_instance_change";
+ //请假、加班、出差、外出状态变更事件
+ static final String EVENT_TYPE_APPROVE_STATUS_CHANGE = "attendance_approve_status_change";
+ //班制调整
+ static final String EVENT_TYPE_ATTEND_SHIFT_CHANGE = "attend_shift_change";
+ //考勤组调整
+ static final String EVENT_TYPE_ATTEND_GROUP_CHANGE = "attend_group_change";
+
+
+
+ boolean accept(String eventType);
+
+ void exchange(JSONObject json);
+
+
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackApproveStatusChangeExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackApproveStatusChangeExchangeProcessor.java
new file mode 100644
index 0000000..3fe2417
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackApproveStatusChangeExchangeProcessor.java
@@ -0,0 +1,49 @@
+package com.evo.ding.processor.callback.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceResponseBody;
+import com.evo.ding.DingUtils;
+import com.evo.ding.processor.approveStatus.AttendanceApproveStatusChangeExchangeProcessor;
+import com.evo.ding.processor.instance.InstanceChangeExchangeProcessor;
+import com.evo.ding.processor.callback.DingCallBackExchangeProcessor;
+import com.evo.system.domain.SysStaff;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 请假、加班、出差、外出状态变更事件 DingCallBackInstanceChangeExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:DingCallBackCheckRecordExchangeProcessor
+ * @date: 2026年03月12日 9:02
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Slf4j
+@Service
+public class DingCallBackApproveStatusChangeExchangeProcessor implements DingCallBackExchangeProcessor {
+
+ @Autowired
+ List approveStatusChangeExchangeProcessorList;
+
+ @Override
+ public boolean accept(String eventType) {
+ return EVENT_TYPE_APPROVE_STATUS_CHANGE.equals(eventType);
+ }
+
+ @Override
+ public void exchange(JSONObject json) {
+ log.info("请假、加班、出差、外出状态变更事件 回调 ========================>>>{}", json.toString());
+ //获取审批示例id
+ String processInstanceId = json.getString("processInstanceId");
+ GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult result = DingUtils.getProcessInstanceInfo(processInstanceId);
+ System.out.println(JSONObject.toJSONString(result));
+ for (AttendanceApproveStatusChangeExchangeProcessor processor : approveStatusChangeExchangeProcessorList) {
+ if (processor.accept(json.getString("approveType"), result.getResult(), result.getStatus())) {
+ processor.exchange(processInstanceId, result);
+ }
+ }
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackCheckRecordExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackCheckRecordExchangeProcessor.java
new file mode 100644
index 0000000..2d05c89
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackCheckRecordExchangeProcessor.java
@@ -0,0 +1,44 @@
+package com.evo.ding.processor.callback.impl;
+
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.evo.attendance.service.IRzAttendanceService;
+import com.evo.common.utils.StringUtils;
+import com.evo.ding.processor.callback.DingCallBackExchangeProcessor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 员工打开回调 DingCallBackCheckRecordExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:DingCallBackCheckRecordExchangeProcessor
+ * @date: 2026年03月12日 9:02
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Slf4j
+@Service
+public class DingCallBackCheckRecordExchangeProcessor implements DingCallBackExchangeProcessor {
+
+ @Autowired
+ IRzAttendanceService rzAttendanceService;
+
+ @Override
+ public boolean accept(String eventType) {
+ return EVENT_TYPE_CHECK_RECORD.equals(eventType);
+ }
+
+ @Override
+ public void exchange(JSONObject json) {
+ log.info("钉钉 打卡回调========================>>>{}", json.toString());
+ JSONArray dataList = json.getJSONArray("DataList");
+ if(dataList!= null && dataList.size() > 0){
+ JSONObject jsonObject = dataList.getJSONObject(0);
+ String userId = jsonObject.getString("userId");
+ if(StringUtils.isNotEmpty(userId)){
+ rzAttendanceService.dingDing(userId, jsonObject.getString("bizId"), jsonObject.getLong("checkTime"));
+ }
+ }
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackGroupChangeExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackGroupChangeExchangeProcessor.java
new file mode 100644
index 0000000..d01f23d
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackGroupChangeExchangeProcessor.java
@@ -0,0 +1,113 @@
+package com.evo.ding.processor.callback.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.dingtalk.api.response.OapiAttendanceGroupQueryResponse;
+import com.dingtalk.api.response.OapiAttendanceShiftQueryResponse;
+import com.evo.common.utils.Collections;
+import com.evo.common.utils.DateUtils;
+import com.evo.ding.DingUtils;
+import com.evo.ding.domain.DingGroup;
+import com.evo.ding.domain.DingShift;
+import com.evo.ding.domain.DingShiftGroup;
+import com.evo.ding.processor.callback.DingCallBackExchangeProcessor;
+import com.evo.ding.service.DingGroupService;
+import com.evo.ding.service.DingShiftGroupService;
+import com.evo.ding.service.DingShiftService;
+import io.swagger.models.auth.In;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * 考勤组信息调整 DingCallBackGroupChangeExchangeProcessor
+ * @author andy.shi
+ * @ClassName:DingCallBackCheckRecordExchangeProcessor
+ * @date: 2026年03月12日 9:02
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Slf4j
+@Service
+public class DingCallBackGroupChangeExchangeProcessor implements DingCallBackExchangeProcessor {
+
+ @Autowired
+ DingGroupService dingGroupService;
+
+ @Autowired
+ DingShiftService dingShiftService;
+
+ @Autowired
+ DingShiftGroupService dingShiftGroupService;
+
+ @Override
+ public boolean accept(String eventType) {
+ return EVENT_TYPE_ATTEND_GROUP_CHANGE.equals(eventType);
+ }
+
+ @Override
+ public void exchange(JSONObject json) {
+ log.info("钉钉 班制调整回调========================>>>{}", json.toString());
+ OapiAttendanceGroupQueryResponse.TopSimpleGroupVO groupDetail = DingUtils.getGroupDetail(json.getLong("id"));
+ if(groupDetail != null){
+ String groupDingId = String.valueOf(groupDetail.getId());
+ DingGroup dingGroup = dingGroupService.selectByDingId(groupDingId);
+ if(dingGroup == null){
+ dingGroup = new DingGroup();
+ }
+ dingGroup.setName(groupDetail.getName());
+ dingGroup.setDingId(groupDingId);
+ dingGroupService.saveOrUpdate(dingGroup);
+
+ //获取现在所有的数据
+ List dingShiftGroupList = dingShiftGroupService.selectListByGroupId(groupDingId);
+ //如果返回为空, 旧数据不为空, 直接清除
+ if(Collections.isEmpty(groupDetail.getShiftIds()) && Collections.isNotEmpty(dingShiftGroupList)){
+ dingShiftGroupService.removeByIds(dingShiftGroupList);
+ }else if(Collections.isNotEmpty(groupDetail.getShiftIds()) && Collections.isEmpty(dingShiftGroupList)){
+ //更新绑定信息
+ for (Long shiftId : groupDetail.getShiftIds()) {
+ crateDingShiftGroup(groupDingId, String.valueOf(shiftId));
+ }
+ }else if(Collections.isNotEmpty(groupDetail.getShiftIds()) && Collections.isNotEmpty(dingShiftGroupList)){
+ Map> map = dingShiftGroupList.stream().collect(Collectors.groupingBy(DingShiftGroup::getShiftId));
+ for (Long shiftId : groupDetail.getShiftIds()) {
+ String strShiftId = String.valueOf(shiftId);
+ if(map.containsKey(strShiftId)){
+ DingShift dingShift = dingShiftService.selectByDingId(strShiftId);
+ for (DingShiftGroup dingShiftGroup : map.get(strShiftId)){
+ dingShiftGroup.setCheckTime(("OnDuty".equals(dingShiftGroup.getType()) ? dingShift.getOnDutyCheckTime() : dingShift.getOffDutyCheckTime()));
+ dingShiftGroupService.updateById(dingShiftGroup);
+ }
+ map.remove(strShiftId);
+ }else{
+ crateDingShiftGroup(groupDingId, String.valueOf(shiftId));
+ }
+ }
+ //如果还存在, 需要删除
+ if(Collections.isNotEmpty(map)){
+ dingShiftGroupService.removeByIds(map.values().stream().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList()));
+ }
+ }
+ }
+ }
+ public void crateDingShiftGroup(String groupId, String shiftId){
+ DingShift dingShift = dingShiftService.selectByDingId(shiftId);
+ //直接更新上班, 下班
+ for (int i = 0; i < 2; i++) {
+ DingShiftGroup dingShiftGroup = new DingShiftGroup();
+ dingShiftGroup.setGroupId(groupId);
+ dingShiftGroup.setShiftId(String.valueOf(shiftId));
+ dingShiftGroup.setType(i == 0 ? "OnDuty" : "OffDuty");
+ if(dingShift != null){
+ dingShiftGroup.setCheckTime(i == 0 ? dingShift.getOnDutyCheckTime() : dingShift.getOffDutyCheckTime());
+ }
+ dingShiftGroupService.save(dingShiftGroup);
+ }
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackInstanceChangeExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackInstanceChangeExchangeProcessor.java
new file mode 100644
index 0000000..a401827
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackInstanceChangeExchangeProcessor.java
@@ -0,0 +1,41 @@
+package com.evo.ding.processor.callback.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.evo.ding.processor.instance.InstanceChangeExchangeProcessor;
+import com.evo.ding.processor.callback.DingCallBackExchangeProcessor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 审批实例订阅 DingCallBackInstanceChangeExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:DingCallBackCheckRecordExchangeProcessor
+ * @date: 2026年03月12日 9:02
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Slf4j
+@Service
+public class DingCallBackInstanceChangeExchangeProcessor implements DingCallBackExchangeProcessor {
+
+ @Autowired
+ List instanceChangeExchangeProcessorList;
+
+ @Override
+ public boolean accept(String eventType) {
+ return EVENT_TYPE_INSTANCE_CHANGE.equals(eventType);
+ }
+
+ @Override
+ public void exchange(JSONObject json) {
+ log.info("审批实例订阅回调========================>>>{}", json.toString());
+ for (InstanceChangeExchangeProcessor processor : instanceChangeExchangeProcessorList) {
+ if (processor.accept(json.getString("processCode"))) {
+ processor.exchange(json);
+ }
+ }
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackShiftChangeExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackShiftChangeExchangeProcessor.java
new file mode 100644
index 0000000..d9a1939
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackShiftChangeExchangeProcessor.java
@@ -0,0 +1,69 @@
+package com.evo.ding.processor.callback.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.dingtalk.api.response.OapiAttendanceShiftQueryResponse;
+import com.evo.common.utils.DateUtils;
+import com.evo.ding.DingUtils;
+import com.evo.ding.domain.DingShift;
+import com.evo.ding.processor.callback.DingCallBackExchangeProcessor;
+import com.evo.ding.service.DingShiftGroupService;
+import com.evo.ding.service.DingShiftService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+
+/**
+ * 班制信息调整 DingCallBackShiftChangeExchangeProcessor
+ * @author andy.shi
+ * @ClassName:DingCallBackCheckRecordExchangeProcessor
+ * @date: 2026年03月12日 9:02
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Slf4j
+@Service
+public class DingCallBackShiftChangeExchangeProcessor implements DingCallBackExchangeProcessor {
+
+ @Autowired
+ DingShiftService dingShiftService;
+
+ @Autowired
+ DingShiftGroupService dingShiftGroupService;
+
+ @Override
+ public boolean accept(String eventType) {
+ return EVENT_TYPE_ATTEND_SHIFT_CHANGE.equals(eventType);
+ }
+
+ @Override
+ public void exchange(JSONObject json) {
+ log.info("钉钉 班制调整回调========================>>>{}", json.toString());
+ OapiAttendanceShiftQueryResponse.TopShiftVo shiftDetail = DingUtils.getShiftDetail(json.getLong("id"));
+ if(shiftDetail != null){
+ String shiftDingId = String.valueOf(shiftDetail.getId());
+ DingShift dingShift = dingShiftService.selectByDingId(shiftDingId);
+ //获取工作时长
+ OapiAttendanceShiftQueryResponse.TopShiftSettingVo topShiftSettingVo = shiftDetail.getShiftSetting();
+
+ OapiAttendanceShiftQueryResponse.TopSectionVo topSectionVo = shiftDetail.getSections().get(0);
+ OapiAttendanceShiftQueryResponse.TopPunchVo newOnDutyTopPunchVo = topSectionVo.getPunches().stream().filter(data -> data.getCheckType().equals("OnDuty")).findAny().orElse(null);
+ OapiAttendanceShiftQueryResponse.TopPunchVo newOffDutyTopPunchVo = topSectionVo.getPunches().stream().filter(data -> data.getCheckType().equals("OffDuty")).findAny().orElse(null);
+ //如果不存在班制, 创建新的班制
+ if(dingShift == null){
+ //如果本地不存在, 直接创建新的
+ dingShift = new DingShift();
+ dingShift.setName(shiftDetail.getName());
+ dingShift.setDingId(shiftDingId);
+ }
+ dingShift.setWorkHour(new BigDecimal(topShiftSettingVo.getWorkTimeMinutes()).divide(new BigDecimal(60), 2, java.math.BigDecimal.ROUND_HALF_UP).intValue());
+ dingShift.setOnDutyCheckTime(DateUtils.parseDateToStr("HH:mm:ss",newOnDutyTopPunchVo.getCheckTime()));
+ dingShift.setOffDutyCheckTime(DateUtils.parseDateToStr("HH:mm:ss",newOffDutyTopPunchVo.getCheckTime()));
+ //通过三目判定, 如果id为空, 则走保存,如果存在id 则更新
+ if((dingShift.getId() == null ? dingShiftService.save(dingShift) : dingShiftService.updateById(dingShift))){
+ dingShiftGroupService.updateCheckTimeByShiftIdAndType(shiftDingId, newOnDutyTopPunchVo.getCheckType(), dingShift.getOnDutyCheckTime());
+ dingShiftGroupService.updateCheckTimeByShiftIdAndType(shiftDingId, newOffDutyTopPunchVo.getCheckType(), dingShift.getOffDutyCheckTime());
+ }
+ }
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackUseLeaveOrgExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackUseLeaveOrgExchangeProcessor.java
new file mode 100644
index 0000000..4430eb7
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackUseLeaveOrgExchangeProcessor.java
@@ -0,0 +1,41 @@
+package com.evo.ding.processor.callback.impl;
+
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.evo.ding.processor.callback.DingCallBackExchangeProcessor;
+import com.evo.system.service.ISysStaffService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 员工离职 DingCallBackUseLeaveOrgExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:DingCallBackUseLeaveOrgExchangeProcessor
+ * @date: 2026年03月12日 9:02
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Slf4j
+@Service
+public class DingCallBackUseLeaveOrgExchangeProcessor implements DingCallBackExchangeProcessor {
+
+ @Autowired
+ ISysStaffService sysStaffService;
+
+ @Override
+ public boolean accept(String eventType) {
+ return EVENT_TYPE_USER_LEAVE_ORG.equals(eventType);
+ }
+
+ @Override
+ public void exchange(JSONObject json) {
+ log.info("钉钉 员工离职========================>>>{}", json.toString());
+ JSONArray dataList = json.getJSONArray("userId");
+ if(dataList!= null && dataList.size() > 0){
+ dataList.forEach(data->{
+ sysStaffService.sysStaffLeaveByDingUserId(String.valueOf(data));
+ });
+ }
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackUserAddOrgExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackUserAddOrgExchangeProcessor.java
new file mode 100644
index 0000000..642e0c1
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/callback/impl/DingCallBackUserAddOrgExchangeProcessor.java
@@ -0,0 +1,44 @@
+package com.evo.ding.processor.callback.impl;
+
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.evo.attendance.service.IRzAttendanceService;
+import com.evo.common.utils.StringUtils;
+import com.evo.ding.DingUtils;
+import com.evo.ding.processor.callback.DingCallBackExchangeProcessor;
+import com.evo.system.service.ISysStaffService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 员工入职 DingCallBackUserAddOrgExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:DingCallBackUserAddOrgExchangeProcessor
+ * @date: 2026年03月12日 9:02
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Slf4j
+@Service
+public class DingCallBackUserAddOrgExchangeProcessor implements DingCallBackExchangeProcessor {
+
+ @Autowired
+ ISysStaffService sysStaffService;
+
+ @Override
+ public boolean accept(String eventType) {
+ return EVENT_TYPE_USER_ADD_ORG.equals(eventType);
+ }
+
+ @Override
+ public void exchange(JSONObject json) {
+ log.info("钉钉 新增员工========================>>>{}", json.toString());
+ JSONArray dataList = json.getJSONArray("UserId");
+ if(dataList!= null && dataList.size() > 0){
+ dataList.forEach(data->{
+ sysStaffService.insertSysStaffByDingDing(DingUtils.getUserInfoByUserId(String.valueOf(data)));
+ });
+ }
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/instance/InstanceChangeExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/instance/InstanceChangeExchangeProcessor.java
new file mode 100644
index 0000000..3b2af57
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/instance/InstanceChangeExchangeProcessor.java
@@ -0,0 +1,32 @@
+package com.evo.ding.processor.instance;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.evo.ding.processor.DingBasicExchangeProcessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 钉钉审批实例回调处理接口 InstanceChangeExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:InstanceChangeExchangeProcessor
+ * @date: 2026年03月09日 15:25
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+
+public interface InstanceChangeExchangeProcessor extends DingBasicExchangeProcessor {
+
+ Logger logger = LoggerFactory.getLogger(InstanceChangeExchangeProcessor.class);
+
+ //补卡
+ static final String PROCESS_CODE_RECHARGE_CARD = "PROC-19617D02-F632-402E-9ACD-134CC47E462F";
+
+ boolean accept(String processCode);
+
+ void exchange(JSONObject json);
+
+
+
+
+
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/processor/instance/impl/InstanceChangeRechargeCardExchangeProcessor.java b/evo-admin/src/main/java/com/evo/ding/processor/instance/impl/InstanceChangeRechargeCardExchangeProcessor.java
new file mode 100644
index 0000000..4d8c36c
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/processor/instance/impl/InstanceChangeRechargeCardExchangeProcessor.java
@@ -0,0 +1,78 @@
+package com.evo.ding.processor.instance.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceResponseBody;
+import com.evo.common.utils.DateUtils;
+import com.evo.common.utils.ParamUtils;
+import com.evo.common.utils.StringUtils;
+import com.evo.ding.DingUtils;
+import com.evo.ding.domain.DingShiftGroup;
+import com.evo.ding.processor.instance.InstanceChangeExchangeProcessor;
+import com.evo.system.domain.SysStaff;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 钉钉补卡接口回调 InstanceChangeRechargeCardExchangeProcessor
+ *
+ * @author andy.shi
+ * @ClassName:InstanceChangeRechargeCardExchangeProcessor
+ * @date: 2026年03月10日 10:24
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Slf4j
+@Service
+public class InstanceChangeRechargeCardExchangeProcessor implements InstanceChangeExchangeProcessor {
+
+ @Override
+ public boolean accept(String processCode) {
+ return InstanceChangeExchangeProcessor.PROCESS_CODE_RECHARGE_CARD.equals(processCode);
+ }
+ @Override
+ public void exchange(JSONObject json) {
+ //获取审批示例id
+ String processInstanceId = json.getString("processInstanceId");
+ GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult result = DingUtils.getProcessInstanceInfo(processInstanceId);
+ //根据操作人id, 获取user
+ SysStaff sysStaff = getSysStaffByDingUserId(result.getOriginatorUserId());
+ if(sysStaff != null){
+ List resultFormValues = result.getFormComponentValues();
+ //补卡时间
+ String bkTime = resultFormValues.stream().filter(data-> "补卡时间".equals(data.getName()) && StringUtils.isNotEmpty(data.getValue())).findFirst().get().getValue();
+ String extValue = resultFormValues.stream().filter(data-> "补卡时间".equals(data.getName()) && StringUtils.isNotEmpty(data.getExtValue())).findFirst().get().getExtValue();
+ //补卡类型
+ String bkType = resultFormValues.stream().filter(data-> "补卡类型".equals(data.getName()) && StringUtils.isNotEmpty(data.getValue())).findFirst().get().getValue();
+ //补卡原因
+ String bkYi = resultFormValues.stream().filter(data-> "补卡原因".equals(data.getName()) && StringUtils.isNotEmpty(data.getValue())).findFirst().get().getValue();
+ //获取补卡班次
+ String checkType = "上班卡".equals(bkType) ? "OnDuty" : "OffDuty";
+ DingShiftGroup dingsShiftGroup = null;
+ try {
+ if(StringUtils.isNotEmpty(extValue)){
+ JSONObject js = JSONObject.parseObject(extValue);
+ String planId = js.getString("planId");
+ String s = DingUtils.getPlanInfo(result.getOriginatorUserId(), planId);
+ JSONObject planResult = JSONObject.parseObject(s);
+ if(Integer.valueOf(0).equals(planResult.getInteger("errcode"))){
+ JSONObject planResultObject = planResult.getJSONArray("result").getJSONObject(0);
+ String formatPlanCheckTime = DateUtils.parseDateToStr("HH:mm:ss", new Date(planResultObject.getString("plan_check_time")));
+ dingsShiftGroup = getDingShiftGroupService().selectByGroupIdAndTypeAndCheckTime(planResultObject.getString("group_id"), planResultObject.getString("class_id"), checkType, formatPlanCheckTime);
+ Date date= DateUtils.parseDate(bkTime, "yyyy-MM-dd HH:mm");
+ //生成补卡记录信息
+ getIRzAttendanceService().addRzOverTime(json.toString(), sysStaff, dingsShiftGroup, checkType, date, date, "/", "钉钉补卡-"+bkYi, ParamUtils.getDebiting(), null);
+ }
+ }
+ } catch (ParseException e) {
+ log.info("补卡出现错误, 为在本地找到班次数据, 错误信息为:{}, 回调信息为: {}", e.getMessage(), json.toString());
+ e.printStackTrace();
+ }
+ }
+ }
+}
+
+
+
diff --git a/evo-admin/src/main/java/com/evo/ding/service/DingGroupService.java b/evo-admin/src/main/java/com/evo/ding/service/DingGroupService.java
new file mode 100644
index 0000000..7f236e2
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/service/DingGroupService.java
@@ -0,0 +1,18 @@
+package com.evo.ding.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.evo.ding.domain.DingGroup;
+import com.evo.ding.domain.DingShift;
+
+/**
+ * DingsShiftService
+ *
+ * @author andy.shi
+ * @ClassName:DingsShiftService
+ * @date: 2026年03月11日 15:55
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+public interface DingGroupService extends IService {
+
+ public DingGroup selectByDingId(String dingId);
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/service/DingShiftGroupService.java b/evo-admin/src/main/java/com/evo/ding/service/DingShiftGroupService.java
new file mode 100644
index 0000000..6c95875
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/service/DingShiftGroupService.java
@@ -0,0 +1,26 @@
+package com.evo.ding.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.evo.ding.domain.DingShift;
+import com.evo.ding.domain.DingShiftGroup;
+
+import java.util.List;
+
+/**
+ * DingShiftGroupService
+ *
+ * @author andy.shi
+ * @ClassName:DingShiftGroupService
+ * @date: 2026年03月11日 15:48
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+public interface DingShiftGroupService extends IService {
+
+ DingShiftGroup selectByGroupIdAndTypeAndCheckTime(String groupId, String shiftId, String type, String checkTime);
+
+ int updateCheckTimeByShiftIdAndType(String shiftId, String type, String checkTime);
+
+ List selectListByGroupId(String groupId);
+
+ public List selectList(DingShiftGroup dingShiftGroup);
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/service/DingShiftService.java b/evo-admin/src/main/java/com/evo/ding/service/DingShiftService.java
new file mode 100644
index 0000000..950c4fc
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/service/DingShiftService.java
@@ -0,0 +1,22 @@
+package com.evo.ding.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.evo.ding.domain.DingShift;
+import com.evo.system.domain.SysStaff;
+
+import java.util.List;
+
+/**
+ * DingsShiftService
+ *
+ * @author andy.shi
+ * @ClassName:DingsShiftService
+ * @date: 2026年03月11日 15:55
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+public interface DingShiftService extends IService {
+
+ public DingShift selectByDingId(String dingId);
+
+ public List selectList(DingShift dingShift);
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/service/impl/DingGroupServiceImpl.java b/evo-admin/src/main/java/com/evo/ding/service/impl/DingGroupServiceImpl.java
new file mode 100644
index 0000000..39f6e9a
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/service/impl/DingGroupServiceImpl.java
@@ -0,0 +1,26 @@
+package com.evo.ding.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.evo.ding.domain.DingGroup;
+import com.evo.ding.domain.DingShift;
+import com.evo.ding.mapper.DingGroupMapper;
+import com.evo.ding.mapper.DingShiftMapper;
+import com.evo.ding.service.DingGroupService;
+import com.evo.ding.service.DingShiftService;
+import org.springframework.stereotype.Service;
+
+/**
+ * DingShiftGroupServiceImpl
+ *
+ * @author andy.shi
+ * @ClassName:DingShiftGroupServiceImpl
+ * @date: 2026年03月11日 15:49
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Service
+public class DingGroupServiceImpl extends ServiceImpl implements DingGroupService {
+ @Override
+ public DingGroup selectByDingId(String dingId) {
+ return getBaseMapper().selectByDingId(dingId);
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/service/impl/DingShiftGroupServiceImpl.java b/evo-admin/src/main/java/com/evo/ding/service/impl/DingShiftGroupServiceImpl.java
new file mode 100644
index 0000000..db9f5b2
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/service/impl/DingShiftGroupServiceImpl.java
@@ -0,0 +1,41 @@
+package com.evo.ding.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.evo.ding.domain.DingShiftGroup;
+import com.evo.ding.mapper.DingShiftGroupMapper;
+import com.evo.ding.service.DingShiftGroupService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * DingShiftGroupServiceImpl
+ *
+ * @author andy.shi
+ * @ClassName:DingShiftGroupServiceImpl
+ * @date: 2026年03月11日 15:49
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Service
+public class DingShiftGroupServiceImpl extends ServiceImpl implements DingShiftGroupService {
+ @Override
+ public DingShiftGroup selectByGroupIdAndTypeAndCheckTime(String groupId, String shiftId, String type, String checkTime) {
+ return getBaseMapper().selectByGroupIdAndTypeAndCheckTime(groupId, shiftId, type, checkTime);
+ }
+
+ @Override
+ public int updateCheckTimeByShiftIdAndType(String shiftId, String type, String checkTime) {
+ return getBaseMapper().updateCheckTimeByShiftIdAndType(shiftId, type, checkTime);
+ }
+
+ @Override
+ public List selectListByGroupId(String groupId) {
+ return list(new LambdaQueryWrapper().eq(DingShiftGroup::getGroupId, groupId));
+ }
+
+ @Override
+ public List selectList(DingShiftGroup dingShiftGroup) {
+ return getBaseMapper().selectListByShiftId(dingShiftGroup.getShiftId());
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/ding/service/impl/DingShiftServiceImpl.java b/evo-admin/src/main/java/com/evo/ding/service/impl/DingShiftServiceImpl.java
new file mode 100644
index 0000000..8e4cabc
--- /dev/null
+++ b/evo-admin/src/main/java/com/evo/ding/service/impl/DingShiftServiceImpl.java
@@ -0,0 +1,33 @@
+package com.evo.ding.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.evo.ding.domain.DingShift;
+import com.evo.ding.domain.DingShiftGroup;
+import com.evo.ding.mapper.DingShiftGroupMapper;
+import com.evo.ding.mapper.DingShiftMapper;
+import com.evo.ding.service.DingShiftGroupService;
+import com.evo.ding.service.DingShiftService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * DingShiftGroupServiceImpl
+ *
+ * @author andy.shi
+ * @ClassName:DingShiftGroupServiceImpl
+ * @date: 2026年03月11日 15:49
+ * @remark: 开发人员联系方式 1042025947@qq.com/微信同步
+ */
+@Service
+public class DingShiftServiceImpl extends ServiceImpl implements DingShiftService {
+ @Override
+ public DingShift selectByDingId(String dingId) {
+ return getBaseMapper().selectByDingId(dingId);
+ }
+
+ @Override
+ public List selectList(DingShift dingShift) {
+ return getBaseMapper().selectList(dingShift);
+ }
+}
diff --git a/evo-admin/src/main/java/com/evo/finance/controller/RzSalaryDetailController.java b/evo-admin/src/main/java/com/evo/finance/controller/RzSalaryDetailController.java
index ae85ef3..7a1df85 100644
--- a/evo-admin/src/main/java/com/evo/finance/controller/RzSalaryDetailController.java
+++ b/evo-admin/src/main/java/com/evo/finance/controller/RzSalaryDetailController.java
@@ -42,9 +42,6 @@ public class RzSalaryDetailController extends BaseController
private IRzSalaryDetailService rzSalaryDetailService;
@Autowired
private ISysDeptService deptService; //部门
- @Resource
- private SysDictDataMapper sysDictDataMapper; //数据字典
-
/**
* 查询工资详情列表
*/
@@ -101,403 +98,6 @@ public class RzSalaryDetailController extends BaseController
return toAjax(rzSalaryDetailService.deleteRzSalaryDetailById(id));
}
- /**
- * 导出工资详情列表
- */
- @PreAuthorize("@ss.hasPermi('finance:financeDetail:export')")
- @Log(title = "工资详情", businessType = BusinessType.EXPORT)
- @PostMapping("/export1")
- public AjaxResult export1(RzSalaryDetail rzSalaryDetail)
- {
- ExcelUtilSs util = new ExcelUtilSs(SalaryVo.class);
- //创建总表
- List list = new ArrayList();
- //创建各个公司的数据
- List> lists = new ArrayList<>();
- //创建各个部门sheetname集合
- List sheetNameList = new ArrayList<>();
- //汇总表表头
- String title = new SimpleDateFormat("yyyy-MM").format(rzSalaryDetail.getMonth())+"月工资汇总表";
- //获取所有的最小独立部门
- List dept_list = deptService.queryAllDeptForMin();
- //员工工资信息
- SalaryVo salaryVo = null;
- //工资数据转化的保存
- List list0 =new ArrayList();
- for (SysDept sysDept : dept_list) {
- sheetNameList.add(sysDept.getDeptName());
- //部门小计
- SalaryVo xj_salaryVo = new SalaryVo();
-// xj_salaryVo.setBasicSalary(new BigDecimal("0.00"));
- xj_salaryVo.setName("小计:");
- //查询部门下的数据
- List pay_list = rzSalaryDetailService.selectSalaryDetailByDeptId(sysDept.getDeptId(),rzSalaryDetail.getMonth());
- for (RzSalaryDetail salaryDetail : pay_list) {
- salaryVo = new SalaryVo();
-// xj_salaryVo.setBasicSalary(xj_salaryVo.getBasicSalary().add(salaryDetail.getBasicSalary()));
-// if(StringUtils.isNull(xj_salaryVo.getOvertimeSalary())){
-// xj_salaryVo.setOvertimeSalary(new BigDecimal("0.00"));
-// }
-// xj_salaryVo.setOvertimeSalary(xj_salaryVo.getOvertimeSalary().add(salaryDetail.getOvertimeSalary()));
-// if(StringUtils.isNull(xj_salaryVo.getLevelSubsidies())){
-// xj_salaryVo.setLevelSubsidies(new BigDecimal("0.00"));
-// }
- xj_salaryVo.setLevelSubsidies(xj_salaryVo.getLevelSubsidies().add(salaryDetail.getLevelSubsidies()));
- if(StringUtils.isNull(xj_salaryVo.getContractSubsidies())){
- xj_salaryVo.setContractSubsidies(new BigDecimal("0.00"));
- }
- xj_salaryVo.setContractSubsidies(xj_salaryVo.getContractSubsidies().add(salaryDetail.getContractSubsidies()));
- if(StringUtils.isNull(xj_salaryVo.getSenioritySalary())){
- xj_salaryVo.setSenioritySalary(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSenioritySalary(xj_salaryVo.getSenioritySalary().add(salaryDetail.getSenioritySalary()));
- if(StringUtils.isNull(xj_salaryVo.getSocialSubsidies())){
- xj_salaryVo.setSocialSubsidies(new BigDecimal("0.00"));
- }
-// xj_salaryVo.setSocialSubsidies(xj_salaryVo.getSocialSubsidies().add(salaryDetail.getSocialSubsidies()));
-// if(StringUtils.isNull(xj_salaryVo.getFullSubsidies())){
-// xj_salaryVo.setFullSubsidies(new BigDecimal("0.00"));
-// }
-// xj_salaryVo.setFullSubsidies(xj_salaryVo.getFullSubsidies().add(salaryDetail.getFullSubsidies()));
-// if(StringUtils.isNull(xj_salaryVo.getNightSubsidies())){
-// xj_salaryVo.setNightSubsidies(new BigDecimal("0.00"));
-// }
- xj_salaryVo.setNightSubsidies(xj_salaryVo.getNightSubsidies().add(salaryDetail.getNightSubsidies()));
- if(StringUtils.isNull(xj_salaryVo.getDinnerSubsidies())){
- xj_salaryVo.setDinnerSubsidies(new BigDecimal("0.00"));
- }
- xj_salaryVo.setDinnerSubsidies(xj_salaryVo.getDinnerSubsidies().add(salaryDetail.getDinnerSubsidies()));
- if(StringUtils.isNull(xj_salaryVo.getSubsidyOrBonus())){
- xj_salaryVo.setSubsidyOrBonus(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSubsidyOrBonus(xj_salaryVo.getSubsidyOrBonus().add(salaryDetail.getSubsidyOrBonus()));
- if(StringUtils.isNull(xj_salaryVo.getAbsenteeismSalary())){
- xj_salaryVo.setAbsenteeismSalary(new BigDecimal("0.00"));
- }
- xj_salaryVo.setAbsenteeismSalary(xj_salaryVo.getAbsenteeismSalary().add(salaryDetail.getAbsenteeismSalary()));
- if(StringUtils.isNull(xj_salaryVo.getAbsenteeismSubsidies())){
- xj_salaryVo.setAbsenteeismSubsidies(new BigDecimal("0.00"));
- }
- xj_salaryVo.setAbsenteeismSubsidies(xj_salaryVo.getAbsenteeismSubsidies().add(salaryDetail.getAbsenteeismSubsidies()));
- if(StringUtils.isNull(xj_salaryVo.getMealFee())){
- xj_salaryVo.setMealFee(new BigDecimal("0.00"));
- }
- xj_salaryVo.setMealFee(xj_salaryVo.getMealFee().add(salaryDetail.getMealFee()));
- if(StringUtils.isNull(xj_salaryVo.getDeductions())){
- xj_salaryVo.setDeductions(new BigDecimal("0.00"));
- }
- xj_salaryVo.setDeductions(xj_salaryVo.getDeductions().add(salaryDetail.getDeductions()));
- if(StringUtils.isNull(xj_salaryVo.getSalary())){
- xj_salaryVo.setSalary(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSalary(xj_salaryVo.getSalary().add(salaryDetail.getSalary()));
- if(StringUtils.isNull(xj_salaryVo.getPayInsurance())){
- xj_salaryVo.setPayInsurance(new BigDecimal("0.00"));
- }
- xj_salaryVo.setPayInsurance(xj_salaryVo.getPayInsurance().add(salaryDetail.getPayInsurance()));
- if(StringUtils.isNull(xj_salaryVo.getSalaryBeforeTax())){
- xj_salaryVo.setSalaryBeforeTax(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSalaryBeforeTax(xj_salaryVo.getSalaryBeforeTax().add(salaryDetail.getSalaryBeforeTax()));
- if(StringUtils.isNull(xj_salaryVo.getTotalWages())){
- xj_salaryVo.setTotalWages(new BigDecimal("0.00"));
- }
- xj_salaryVo.setTotalWages(xj_salaryVo.getTotalWages().add(salaryDetail.getTotalWages()));
- if(StringUtils.isNull(xj_salaryVo.getAnnualExemptionAmount())){
- xj_salaryVo.setAnnualExemptionAmount(new BigDecimal("0.00"));
- }
- xj_salaryVo.setAnnualExemptionAmount(xj_salaryVo.getAnnualExemptionAmount().add(salaryDetail.getAnnualExemptionAmount()));
- if(StringUtils.isNull(xj_salaryVo.getSpecialDeduction())){
- xj_salaryVo.setSpecialDeduction(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSpecialDeduction(xj_salaryVo.getSpecialDeduction().add(salaryDetail.getSpecialDeduction()));
- if(StringUtils.isNull(xj_salaryVo.getSlowDownTheDeduction())){
- xj_salaryVo.setSlowDownTheDeduction(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSlowDownTheDeduction(xj_salaryVo.getSlowDownTheDeduction().add(salaryDetail.getSlowDownTheDeduction()));
- if(StringUtils.isNull(xj_salaryVo.getAggregatePersonalIncomeTax())){
- xj_salaryVo.setAggregatePersonalIncomeTax(new BigDecimal("0.00"));
- }
- xj_salaryVo.setAggregatePersonalIncomeTax(xj_salaryVo.getAggregatePersonalIncomeTax().add(salaryDetail.getAggregatePersonalIncomeTax()));
- if(StringUtils.isNull(xj_salaryVo.getTaxPayable())){
- xj_salaryVo.setTaxPayable(new BigDecimal("0.00"));
- }
- xj_salaryVo.setTaxPayable(xj_salaryVo.getTaxPayable().add(salaryDetail.getTaxPayable()));
- if(StringUtils.isNull(xj_salaryVo.getNetPayroll())){
- xj_salaryVo.setNetPayroll(new BigDecimal("0.00"));
- }
- xj_salaryVo.setNetPayroll(xj_salaryVo.getNetPayroll().add(salaryDetail.getNetPayroll()));
- BeanUtils.copyProperties(salaryDetail, salaryVo);
- salaryVo.setDeptName(sysDept.getDeptName());
- list0.add(salaryVo);
- }
- list0.add(xj_salaryVo);
- lists.add(list0);
- list.add(xj_salaryVo);
- }
- //计算伊特总计数据
- SalaryVo gj_salaryVo = new SalaryVo();
- for (SalaryVo salaryVoz : list) {
-// if(StringUtils.isNull(gj_salaryVo.getBasicSalary())){
-// gj_salaryVo.setBasicSalary(new BigDecimal("0.00"));
-// }
-// gj_salaryVo.setBasicSalary(gj_salaryVo.getBasicSalary().add(salaryVoz.getBasicSalary()));
-// if(StringUtils.isNull(gj_salaryVo.getOvertimeSalary())){
-// gj_salaryVo.setOvertimeSalary(new BigDecimal("0.00"));
-// }
-// gj_salaryVo.setOvertimeSalary(gj_salaryVo.getOvertimeSalary().add(DataUtils.findDefaultValue(salaryVoz.getOvertimeSalary(), DataUtils.DEFAULT_VALUE)));
-// if(StringUtils.isNull(gj_salaryVo.getLevelSubsidies())){
-// gj_salaryVo.setLevelSubsidies(new BigDecimal("0.00"));
-// }
- gj_salaryVo.setLevelSubsidies(gj_salaryVo.getLevelSubsidies().add(DataUtils.findDefaultValue(salaryVoz.getLevelSubsidies(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getContractSubsidies())){
- gj_salaryVo.setContractSubsidies(new BigDecimal("0.00"));
- }
- gj_salaryVo.setContractSubsidies(gj_salaryVo.getContractSubsidies().add(DataUtils.findDefaultValue(salaryVoz.getContractSubsidies(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getSenioritySalary())){
- gj_salaryVo.setSenioritySalary(new BigDecimal("0.00"));
- }
- gj_salaryVo.setSenioritySalary(gj_salaryVo.getSenioritySalary().add(DataUtils.findDefaultValue(salaryVoz.getSenioritySalary(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getSocialSubsidies())){
- gj_salaryVo.setSocialSubsidies(new BigDecimal("0.00"));
- }
-// gj_salaryVo.setSocialSubsidies(gj_salaryVo.getSocialSubsidies().add(DataUtils.findDefaultValue(salaryVoz.getSocialSubsidies(), DataUtils.DEFAULT_VALUE)));
-// if(StringUtils.isNull(gj_salaryVo.getFullSubsidies())){
-// gj_salaryVo.setFullSubsidies(new BigDecimal("0.00"));
-// }
-// gj_salaryVo.setFullSubsidies(gj_salaryVo.getFullSubsidies().add(DataUtils.findDefaultValue(salaryVoz.getFullSubsidies(), DataUtils.DEFAULT_VALUE)));
-// if(StringUtils.isNull(gj_salaryVo.getNightSubsidies())){
-// gj_salaryVo.setNightSubsidies(new BigDecimal("0.00"));
-// }
- gj_salaryVo.setNightSubsidies(gj_salaryVo.getNightSubsidies().add(DataUtils.findDefaultValue(salaryVoz.getNightSubsidies(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getDinnerSubsidies())){
- gj_salaryVo.setDinnerSubsidies(new BigDecimal("0.00"));
- }
- gj_salaryVo.setDinnerSubsidies(gj_salaryVo.getDinnerSubsidies().add(DataUtils.findDefaultValue(salaryVoz.getDinnerSubsidies(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getSubsidyOrBonus())){
- gj_salaryVo.setSubsidyOrBonus(new BigDecimal("0.00"));
- }
- gj_salaryVo.setSubsidyOrBonus(gj_salaryVo.getSubsidyOrBonus().add(DataUtils.findDefaultValue(salaryVoz.getSubsidyOrBonus(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getAbsenteeismSalary())){
- gj_salaryVo.setAbsenteeismSalary(new BigDecimal("0.00"));
- }
- gj_salaryVo.setAbsenteeismSalary(gj_salaryVo.getAbsenteeismSalary().add(DataUtils.findDefaultValue(salaryVoz.getAbsenteeismSalary(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getAbsenteeismSubsidies())){
- gj_salaryVo.setAbsenteeismSubsidies(new BigDecimal("0.00"));
- }
- gj_salaryVo.setAbsenteeismSubsidies(gj_salaryVo.getAbsenteeismSubsidies().add(DataUtils.findDefaultValue(salaryVoz.getAbsenteeismSubsidies(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getMealFee())){
- gj_salaryVo.setMealFee(new BigDecimal("0.00"));
- }
- gj_salaryVo.setMealFee(gj_salaryVo.getMealFee().add(DataUtils.findDefaultValue(salaryVoz.getMealFee(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getDeductions())){
- gj_salaryVo.setDeductions(new BigDecimal("0.00"));
- }
- gj_salaryVo.setDeductions(gj_salaryVo.getDeductions().add(DataUtils.findDefaultValue(salaryVoz.getDeductions(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getSalary())){
- gj_salaryVo.setSalary(new BigDecimal("0.00"));
- }
- gj_salaryVo.setSalary(gj_salaryVo.getSalary().add(DataUtils.findDefaultValue(salaryVoz.getSalary(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getPayInsurance())){
- gj_salaryVo.setPayInsurance(new BigDecimal("0.00"));
- }
- gj_salaryVo.setPayInsurance(gj_salaryVo.getPayInsurance().add(DataUtils.findDefaultValue(salaryVoz.getPayInsurance(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getSalaryBeforeTax())){
- gj_salaryVo.setSalaryBeforeTax(new BigDecimal("0.00"));
- }
- gj_salaryVo.setSalaryBeforeTax(gj_salaryVo.getSalaryBeforeTax().add(DataUtils.findDefaultValue(salaryVoz.getSalaryBeforeTax(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getTotalWages())){
- gj_salaryVo.setTotalWages(new BigDecimal("0.00"));
- }
- gj_salaryVo.setTotalWages(gj_salaryVo.getTotalWages().add(DataUtils.findDefaultValue(salaryVoz.getTotalWages(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getAnnualExemptionAmount())){
- gj_salaryVo.setAnnualExemptionAmount(new BigDecimal("0.00"));
- }
- gj_salaryVo.setAnnualExemptionAmount(gj_salaryVo.getAnnualExemptionAmount().add(DataUtils.findDefaultValue(salaryVoz.getAnnualExemptionAmount(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getSpecialDeduction())){
- gj_salaryVo.setSpecialDeduction(new BigDecimal("0.00"));
- }
- gj_salaryVo.setSpecialDeduction(gj_salaryVo.getSpecialDeduction().add(DataUtils.findDefaultValue(salaryVoz.getSpecialDeduction(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getSlowDownTheDeduction())){
- gj_salaryVo.setSlowDownTheDeduction(new BigDecimal("0.00"));
- }
- gj_salaryVo.setSlowDownTheDeduction(gj_salaryVo.getSlowDownTheDeduction().add(DataUtils.findDefaultValue(salaryVoz.getSlowDownTheDeduction(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getAggregatePersonalIncomeTax())){
- gj_salaryVo.setAggregatePersonalIncomeTax(new BigDecimal("0.00"));
- }
- gj_salaryVo.setAggregatePersonalIncomeTax(gj_salaryVo.getAggregatePersonalIncomeTax().add(DataUtils.findDefaultValue(salaryVoz.getAggregatePersonalIncomeTax(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getTaxPayable())){
- gj_salaryVo.setTaxPayable(new BigDecimal("0.00"));
- }
- gj_salaryVo.setTaxPayable(gj_salaryVo.getTaxPayable().add(DataUtils.findDefaultValue(salaryVoz.getTaxPayable(), DataUtils.DEFAULT_VALUE)));
- if(StringUtils.isNull(gj_salaryVo.getNetPayroll())){
- gj_salaryVo.setNetPayroll(new BigDecimal("0.00"));
- }
- gj_salaryVo.setNetPayroll(gj_salaryVo.getNetPayroll().add(DataUtils.findDefaultValue(salaryVoz.getNetPayroll(), DataUtils.DEFAULT_VALUE)));
- }
- gj_salaryVo.setName("伊特总计:");
- list.add(gj_salaryVo);
-
- //创建总表的对象
- SalaryVo payRollZong = new SalaryVo();
- payRollZong.setName("总计:");
- BeanUtils.copyProperties(gj_salaryVo, payRollZong);
- //查询非伊特数据
- List cy_list = sysDictDataMapper.selectDictDataByType(Constants.SYS_COMPANY);
- for (SysDictData sysDictData : cy_list) {
- if(!"YT".equals(sysDictData.getDictValue())){
- sheetNameList.add(sysDictData.getDictLabel());
- //部门小计
- SalaryVo xj_salaryVo = new SalaryVo();
- xj_salaryVo.setName("小计:");
- //查询非伊特下的数据
- List f_list = rzSalaryDetailService.selectSalaryDetailByWbFlag(sysDictData.getDictValue(),rzSalaryDetail.getMonth());
- for (RzSalaryDetail salaryDetail : f_list) {
- salaryVo = new SalaryVo();
-// if(StringUtils.isNull(xj_salaryVo.getBasicSalary())){
-// xj_salaryVo.setBasicSalary(new BigDecimal("0.00"));
-// }
-// xj_salaryVo.setBasicSalary(xj_salaryVo.getBasicSalary().add(salaryDetail.getBasicSalary()));
-// if(StringUtils.isNull(xj_salaryVo.getOvertimeSalary())){
-// xj_salaryVo.setOvertimeSalary(new BigDecimal("0.00"));
-// }
-// xj_salaryVo.setOvertimeSalary(xj_salaryVo.getOvertimeSalary().add(salaryDetail.getOvertimeSalary()));
-// if(StringUtils.isNull(xj_salaryVo.getLevelSubsidies())){
-// xj_salaryVo.setLevelSubsidies(new BigDecimal("0.00"));
-// }
- xj_salaryVo.setLevelSubsidies(xj_salaryVo.getLevelSubsidies().add(salaryDetail.getLevelSubsidies()));
- if(StringUtils.isNull(xj_salaryVo.getContractSubsidies())){
- xj_salaryVo.setContractSubsidies(new BigDecimal("0.00"));
- }
- xj_salaryVo.setContractSubsidies(xj_salaryVo.getContractSubsidies().add(salaryDetail.getContractSubsidies()));
- if(StringUtils.isNull(xj_salaryVo.getSenioritySalary())){
- xj_salaryVo.setSenioritySalary(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSenioritySalary(xj_salaryVo.getSenioritySalary().add(salaryDetail.getSenioritySalary()));
- if(StringUtils.isNull(xj_salaryVo.getSocialSubsidies())){
- xj_salaryVo.setSocialSubsidies(new BigDecimal("0.00"));
- }
-// xj_salaryVo.setSocialSubsidies(xj_salaryVo.getSocialSubsidies().add(salaryDetail.getSocialSubsidies()));
-// if(StringUtils.isNull(xj_salaryVo.getFullSubsidies())){
-// xj_salaryVo.setFullSubsidies(new BigDecimal("0.00"));
-// }
-// xj_salaryVo.setFullSubsidies(xj_salaryVo.getFullSubsidies().add(salaryDetail.getFullSubsidies()));
-// if(StringUtils.isNull(xj_salaryVo.getNightSubsidies())){
-// xj_salaryVo.setNightSubsidies(new BigDecimal("0.00"));
-// }
- xj_salaryVo.setNightSubsidies(xj_salaryVo.getNightSubsidies().add(salaryDetail.getNightSubsidies()));
- if(StringUtils.isNull(xj_salaryVo.getDinnerSubsidies())){
- xj_salaryVo.setDinnerSubsidies(new BigDecimal("0.00"));
- }
- xj_salaryVo.setDinnerSubsidies(xj_salaryVo.getDinnerSubsidies().add(salaryDetail.getDinnerSubsidies()));
- if(StringUtils.isNull(xj_salaryVo.getSubsidyOrBonus())){
- xj_salaryVo.setSubsidyOrBonus(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSubsidyOrBonus(xj_salaryVo.getSubsidyOrBonus().add(salaryDetail.getSubsidyOrBonus()));
- if(StringUtils.isNull(xj_salaryVo.getAbsenteeismSalary())){
- xj_salaryVo.setAbsenteeismSalary(new BigDecimal("0.00"));
- }
- xj_salaryVo.setAbsenteeismSalary(xj_salaryVo.getAbsenteeismSalary().add(salaryDetail.getAbsenteeismSalary()));
- if(StringUtils.isNull(xj_salaryVo.getAbsenteeismSubsidies())){
- xj_salaryVo.setAbsenteeismSubsidies(new BigDecimal("0.00"));
- }
- xj_salaryVo.setAbsenteeismSubsidies(xj_salaryVo.getAbsenteeismSubsidies().add(salaryDetail.getAbsenteeismSubsidies()));
- if(StringUtils.isNull(xj_salaryVo.getMealFee())){
- xj_salaryVo.setMealFee(new BigDecimal("0.00"));
- }
- xj_salaryVo.setMealFee(xj_salaryVo.getMealFee().add(salaryDetail.getMealFee()));
- if(StringUtils.isNull(xj_salaryVo.getDeductions())){
- xj_salaryVo.setDeductions(new BigDecimal("0.00"));
- }
- xj_salaryVo.setDeductions(xj_salaryVo.getDeductions().add(salaryDetail.getDeductions()));
- if(StringUtils.isNull(xj_salaryVo.getSalary())){
- xj_salaryVo.setSalary(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSalary(xj_salaryVo.getSalary().add(salaryDetail.getSalary()));
- if(StringUtils.isNull(xj_salaryVo.getPayInsurance())){
- xj_salaryVo.setPayInsurance(new BigDecimal("0.00"));
- }
- xj_salaryVo.setPayInsurance(xj_salaryVo.getPayInsurance().add(salaryDetail.getPayInsurance()));
- if(StringUtils.isNull(xj_salaryVo.getSalaryBeforeTax())){
- xj_salaryVo.setSalaryBeforeTax(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSalaryBeforeTax(xj_salaryVo.getSalaryBeforeTax().add(salaryDetail.getSalaryBeforeTax()));
- if(StringUtils.isNull(xj_salaryVo.getTotalWages())){
- xj_salaryVo.setTotalWages(new BigDecimal("0.00"));
- }
- xj_salaryVo.setTotalWages(xj_salaryVo.getTotalWages().add(salaryDetail.getTotalWages()));
- if(StringUtils.isNull(xj_salaryVo.getAnnualExemptionAmount())){
- xj_salaryVo.setAnnualExemptionAmount(new BigDecimal("0.00"));
- }
- xj_salaryVo.setAnnualExemptionAmount(xj_salaryVo.getAnnualExemptionAmount().add(salaryDetail.getAnnualExemptionAmount()));
- if(StringUtils.isNull(xj_salaryVo.getSpecialDeduction())){
- xj_salaryVo.setSpecialDeduction(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSpecialDeduction(xj_salaryVo.getSpecialDeduction().add(salaryDetail.getSpecialDeduction()));
- if(StringUtils.isNull(xj_salaryVo.getSlowDownTheDeduction())){
- xj_salaryVo.setSlowDownTheDeduction(new BigDecimal("0.00"));
- }
- xj_salaryVo.setSlowDownTheDeduction(xj_salaryVo.getSlowDownTheDeduction().add(salaryDetail.getSlowDownTheDeduction()));
- if(StringUtils.isNull(xj_salaryVo.getAggregatePersonalIncomeTax())){
- xj_salaryVo.setAggregatePersonalIncomeTax(new BigDecimal("0.00"));
- }
- xj_salaryVo.setAggregatePersonalIncomeTax(xj_salaryVo.getAggregatePersonalIncomeTax().add(salaryDetail.getAggregatePersonalIncomeTax()));
- if(StringUtils.isNull(xj_salaryVo.getTaxPayable())){
- xj_salaryVo.setTaxPayable(new BigDecimal("0.00"));
- }
- xj_salaryVo.setTaxPayable(xj_salaryVo.getTaxPayable().add(salaryDetail.getTaxPayable()));
- if(StringUtils.isNull(xj_salaryVo.getNetPayroll())){
- xj_salaryVo.setNetPayroll(new BigDecimal("0.00"));
- }
- xj_salaryVo.setNetPayroll(xj_salaryVo.getNetPayroll().add(salaryDetail.getNetPayroll()));
- BeanUtils.copyProperties(salaryDetail, salaryVo);
- salaryVo.setDeptName(deptService.selectDeptById(salaryDetail.getDeptId()).getDeptName());
- list0.add(salaryVo);
- }
- list.add(xj_salaryVo);
-// payRollZong.setBasicSalary(DataUtils.findDefaultValue(xj_salaryVo.getBasicSalary(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getBasicSalary(), DataUtils.DEFAULT_VALUE)));
-// payRollZong.setOvertimeSalary(DataUtils.findDefaultValue(xj_salaryVo.getOvertimeSalary(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getOvertimeSalary(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setLevelSubsidies(DataUtils.findDefaultValue(xj_salaryVo.getLevelSubsidies(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getLevelSubsidies(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setContractSubsidies(DataUtils.findDefaultValue(xj_salaryVo.getContractSubsidies(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getContractSubsidies(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setSenioritySalary(DataUtils.findDefaultValue(xj_salaryVo.getSenioritySalary(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getSenioritySalary(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setSocialSubsidies(DataUtils.findDefaultValue(xj_salaryVo.getSocialSubsidies(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getSocialSubsidies(), DataUtils.DEFAULT_VALUE)));
-// payRollZong.setFullSubsidies(DataUtils.findDefaultValue(xj_salaryVo.getFullSubsidies(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getFullSubsidies(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setNightSubsidies(DataUtils.findDefaultValue(xj_salaryVo.getNightSubsidies(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getNightSubsidies(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setDinnerSubsidies(DataUtils.findDefaultValue(xj_salaryVo.getDinnerSubsidies(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getDinnerSubsidies(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setSubsidyOrBonus(DataUtils.findDefaultValue(xj_salaryVo.getSubsidyOrBonus(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getSubsidyOrBonus(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setAbsenteeismSalary(DataUtils.findDefaultValue(xj_salaryVo.getAbsenteeismSalary(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getAbsenteeismSalary(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setAbsenteeismSubsidies(DataUtils.findDefaultValue(xj_salaryVo.getAbsenteeismSubsidies(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getAbsenteeismSubsidies(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setMealFee(DataUtils.findDefaultValue(xj_salaryVo.getMealFee(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getMealFee(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setDeductions(DataUtils.findDefaultValue(xj_salaryVo.getDeductions(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getDeductions(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setSalary(DataUtils.findDefaultValue(xj_salaryVo.getSalary(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getSalary(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setPayInsurance(DataUtils.findDefaultValue(xj_salaryVo.getPayInsurance(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getPayInsurance(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setSalaryBeforeTax(DataUtils.findDefaultValue(xj_salaryVo.getSalaryBeforeTax(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getSalaryBeforeTax(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setTotalWages(DataUtils.findDefaultValue(xj_salaryVo.getTotalWages(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getTotalWages(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setAnnualExemptionAmount(DataUtils.findDefaultValue(xj_salaryVo.getAnnualExemptionAmount(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getAnnualExemptionAmount(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setSpecialDeduction(DataUtils.findDefaultValue(xj_salaryVo.getSpecialDeduction(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getSpecialDeduction(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setSlowDownTheDeduction(DataUtils.findDefaultValue(xj_salaryVo.getSlowDownTheDeduction(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getSlowDownTheDeduction(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setAggregatePersonalIncomeTax(DataUtils.findDefaultValue(xj_salaryVo.getAggregatePersonalIncomeTax(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getAggregatePersonalIncomeTax(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setTaxPayable(DataUtils.findDefaultValue(xj_salaryVo.getTaxPayable(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getTaxPayable(), DataUtils.DEFAULT_VALUE)));
- payRollZong.setNetPayroll(DataUtils.findDefaultValue(xj_salaryVo.getNetPayroll(), DataUtils.DEFAULT_VALUE).add(DataUtils.findDefaultValue(payRollZong.getNetPayroll(), DataUtils.DEFAULT_VALUE)));
- }
- }
- list.add(payRollZong);
- //表尾
- String footer = "制表: "+"审核: "+"经理签字: "+"总经理签字: ";
- return util.exportExcel(list,lists,"总表",sheetNameList,title,footer);
- }
- /**
- * 导入销售提成
- */
-// @PreAuthorize("@ss.hasPermi('finance:financeDetail:importSalesCommissions')")
-// @Log(title = "导入销售提成", businessType = BusinessType.IMPORT)
-// @PostMapping("/importSalesCommissions")
-// public AjaxResult importSalesCommissions(MultipartFile file) throws Exception
-// {
-// ExcelUtil util = new ExcelUtil<>(RzSalaryVo.class);
-// List attendanceList = util.importExcel(file.getInputStream());
-// return success(rzSalaryDetailService.importSalesCommissions(attendanceList));
-// }
/*
* 导出工资详情列表
*/
diff --git a/evo-admin/src/main/java/com/evo/finance/domain/RzSalaryDetail.java b/evo-admin/src/main/java/com/evo/finance/domain/RzSalaryDetail.java
index b67e6c0..b5dbe63 100644
--- a/evo-admin/src/main/java/com/evo/finance/domain/RzSalaryDetail.java
+++ b/evo-admin/src/main/java/com/evo/finance/domain/RzSalaryDetail.java
@@ -203,10 +203,23 @@ public class RzSalaryDetail extends BaseEntity
*/
@TableField(exist = false)
private BigDecimal kjAmount;
+ //基本工资全薪
+ @TableField(exist = false)
+ private BigDecimal basicAmount;
/** 删除标识 */
private String delFlag;
+ private Integer type;
+
+ public Integer getType() {
+ return type;
+ }
+
+ public void setType(Integer type) {
+ this.type = type;
+ }
+
public BigDecimal getSalesCommission() {
return salesCommission;
}
@@ -635,6 +648,14 @@ public class RzSalaryDetail extends BaseEntity
this.bjAmount = bjAmount;
}
+ public BigDecimal getBasicAmount() {
+ return basicAmount;
+ }
+
+ public void setBasicAmount(BigDecimal basicAmount) {
+ this.basicAmount = basicAmount;
+ }
+
public BigDecimal getSocialSecurityDeduction() {
return socialSecurityDeduction;
}
diff --git a/evo-admin/src/main/java/com/evo/finance/mapper/RzSalaryDetailMapper.java b/evo-admin/src/main/java/com/evo/finance/mapper/RzSalaryDetailMapper.java
index 06366e7..88e5fae 100644
--- a/evo-admin/src/main/java/com/evo/finance/mapper/RzSalaryDetailMapper.java
+++ b/evo-admin/src/main/java/com/evo/finance/mapper/RzSalaryDetailMapper.java
@@ -78,4 +78,8 @@ public interface RzSalaryDetailMapper extends BaseMapper
public List selectSalaryDetailByWbFlag(@Param("wbFlag")String wbFlag,@Param("date")Date date);
+ /**
+ */
+ public Date selectMaxMonthBySysStallId(Long id);
+
}
diff --git a/evo-admin/src/main/java/com/evo/finance/processor/SalaryCalculationStrategyExchangeProcessor.java b/evo-admin/src/main/java/com/evo/finance/processor/SalaryCalculationStrategyExchangeProcessor.java
index 9de07e1..acfcf89 100644
--- a/evo-admin/src/main/java/com/evo/finance/processor/SalaryCalculationStrategyExchangeProcessor.java
+++ b/evo-admin/src/main/java/com/evo/finance/processor/SalaryCalculationStrategyExchangeProcessor.java
@@ -111,9 +111,10 @@ public interface SalaryCalculationStrategyExchangeProcessor {
rzSalaryDetail.setSalary(rzSalaryDetail.getMonthSalary().add(isMonth ? rzSalaryDetail.getOvertimeSalary() : DataUtils.DEFAULT_VALUE).add(rzSalaryDetail.getNightSubsidies()).add(rzSalaryDetail.getMiddleSubsidies()).add(rzSalaryDetail.getDinnerSubsidies())
.add(DataUtils.findDefaultValue(rzSalaryDetail.getFullSubsidies(), DataUtils.DEFAULT_VALUE)).add(rzSalaryDetail.getLevelSubsidies()).add(rzSalaryDetail.getContractSubsidies()).add(rzSalaryDetail.getSocialSubsidies())
.add(rzSalaryDetail.getSenioritySalary()).add(rzSalaryDetail.getSubsidyOrBonus()).add(rzSalaryDetail.getSalesCommission())
- .subtract(rzSalaryDetail.getAbsenteeismSubsidies()).subtract(rzSalaryDetail.getDeductions()).subtract(rzSalaryDetail.getAbsenteeismSalary()).add(DataUtils.findDefaultValue(rzSalaryDetail.getBjAmount(), DataUtils.DEFAULT_VALUE)));
+ .subtract(rzSalaryDetail.getAbsenteeismSubsidies()).subtract(rzSalaryDetail.getDeductions()).subtract(rzSalaryDetail.getAbsenteeismSalary()).add(DataUtils.findDefaultValue(rzSalaryDetail.getBjAmount(), DataUtils.DEFAULT_VALUE))
+ .add(DataUtils.findDefaultValue(rzSalaryDetail.getBasicAmount(), DataUtils.DEFAULT_VALUE)));
//.subtract(rzSalaryDetail.getMealFee())
- //如果当前人员不打卡 则需要扣减缺勤工资
+ //如果当前人员不打卡 则需要扣减缺勤工资setBasicAmount
// if("否".equals(sysStaff.getClockIn())){
// rzSalaryDetail.setSalary(rzSalaryDetail.getSalary());
// }
diff --git a/evo-admin/src/main/java/com/evo/finance/processor/impl/MonthlySalaryStrategyExchangeProcessor.java b/evo-admin/src/main/java/com/evo/finance/processor/impl/MonthlySalaryStrategyExchangeProcessor.java
index 0858e8a..2e35b2b 100644
--- a/evo-admin/src/main/java/com/evo/finance/processor/impl/MonthlySalaryStrategyExchangeProcessor.java
+++ b/evo-admin/src/main/java/com/evo/finance/processor/impl/MonthlySalaryStrategyExchangeProcessor.java
@@ -119,7 +119,6 @@ public class MonthlySalaryStrategyExchangeProcessor implements SalaryCalculation
BigDecimal gs = rzSalaryDetail.getBasicSalary().multiply(new BigDecimal(Constants.SUBSIDY_PERIOD)).divide(new BigDecimal(ParamUtils.getMonthWorkDayNum(DateUtils.getYear(rzSalaryDetail.getMonth()), DateUtils.getMonth(rzSalaryDetail.getMonth()))),2, RoundingMode.HALF_UP).multiply(new BigDecimal(total8Hour).divide(Constants.DAY_WORK_HOUR,2,RoundingMode.HALF_UP));
// rzSalaryDetail.setMonthSalary(rzSalaryDetail.getMonthSalary().add(gs));
rzSalaryDetail.setBjAmount(gs);
-
}
if(sysStaff.getName().equals("史国臣")){
System.out.println("11111");
@@ -154,6 +153,20 @@ public class MonthlySalaryStrategyExchangeProcessor implements SalaryCalculation
}
}
}
+ //获取所有的基本薪资
+ Long totalBasicHour = 0l;
+ for (Long type: ParamUtils.getBasicFullPaidLeave()) {
+ Long hour = rzLeaveDetailMapper.selectLeaveHourByUserIdAndDateAndType(sysStaff.getUserId(), rzSalaryDetail.getMonth(), type);
+ if(hour.compareTo(0l)> 0){
+ totalBasicHour = totalBasicHour+hour;
+ }
+ }
+ if(totalBasicHour.compareTo(0l)> 0){
+ //存在工伤假, 需要计算
+ BigDecimal basicAmount = rzSalaryDetail.getBasicSalary().divide(new BigDecimal(ParamUtils.getMonthWorkDayNum(DateUtils.getYear(rzSalaryDetail.getMonth()), DateUtils.getMonth(rzSalaryDetail.getMonth()))),2, RoundingMode.HALF_UP).multiply(new BigDecimal(totalBasicHour).divide(Constants.DAY_WORK_HOUR,2,RoundingMode.HALF_UP));
+ rzSalaryDetail.setBasicAmount(basicAmount);
+ }
+
// if(totalAllHour.compareTo(0l)> 0){
// //存在工伤假, 需要计算
// rzSalaryDetail.setMonthSalary(rzSalaryDetail.getMonthSalary().add(workHourPrice.multiply(new BigDecimal(totalAllHour))));
diff --git a/evo-admin/src/main/java/com/evo/finance/service/IRzSalaryDetailService.java b/evo-admin/src/main/java/com/evo/finance/service/IRzSalaryDetailService.java
index 4af9520..6b7d6c4 100644
--- a/evo-admin/src/main/java/com/evo/finance/service/IRzSalaryDetailService.java
+++ b/evo-admin/src/main/java/com/evo/finance/service/IRzSalaryDetailService.java
@@ -6,6 +6,7 @@ import java.util.List;
import com.evo.attendance.domain.vo.RzSalaryVo;
import com.evo.common.core.domain.AjaxResult;
import com.evo.finance.domain.RzSalaryDetail;
+import com.evo.system.domain.SysStaff;
import org.apache.ibatis.annotations.Param;
/**
@@ -69,4 +70,6 @@ public interface IRzSalaryDetailService
public List selectSalaryDetailByWbFlag(String wbFlag,Date date);
AjaxResult export(RzSalaryDetail rzSalaryDetail);
+
+ void dimission(SysStaff sysStaff);
}
diff --git a/evo-admin/src/main/java/com/evo/finance/service/impl/RzSalaryDetailServiceImpl.java b/evo-admin/src/main/java/com/evo/finance/service/impl/RzSalaryDetailServiceImpl.java
index a99e089..986ead4 100644
--- a/evo-admin/src/main/java/com/evo/finance/service/impl/RzSalaryDetailServiceImpl.java
+++ b/evo-admin/src/main/java/com/evo/finance/service/impl/RzSalaryDetailServiceImpl.java
@@ -142,23 +142,26 @@ public class RzSalaryDetailServiceImpl extends ServiceImpl overTimeMap = deptMapper.selectList(new LambdaQueryWrapper().eq(SysDept::getDelFlag, Constants.DELETE_FLAG_0).select(SysDept::getIsOverTime, SysDept::getDeptId)).stream().collect(Collectors.toMap(SysDept::getDeptId,d->"0".equals(d.getIsOverTime())));
List