考勤改版调整

This commit is contained in:
andy 2026-03-16 11:55:27 +08:00
parent 6c1c95352d
commit 2002d2a45b
37 changed files with 1181 additions and 85 deletions

View File

@ -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));
}

View File

@ -1,9 +1,11 @@
package com.evo.attendance.service;
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;
@ -64,4 +66,8 @@ public interface IRzAttendanceService extends IService<RzAttendance>
public void sendAbnormalAttendance();
public void dingDing(String dingUserId, String bizId);
void addRzOverTime(String result, SysStaff sysStaff, DingShiftGroup dingsShiftGroup, String checkType, Date formatWorkDate, Date formatUserCheckTime, String timeResult, String remark );
public RzAttendance queryNowDayAttendanceByStatisticalIdAndDate(Long staffId, Date date);
}

View File

@ -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<RzSpecialAttendance>
{
/**
* 查询加班考勤记录
@ -51,4 +54,6 @@ public interface IRzSpecialAttendanceService
* @return 结果
*/
public int deleteRzSpecialAttendanceById(Long id);
public RzSpecialAttendance selectRzSpecialAttendanceByUserIdAndDate(Long staffId, Date date);
}

View File

@ -16,13 +16,17 @@ 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.*;
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.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;
@ -55,6 +59,8 @@ public class RzAttendanceServiceImpl extends ServiceImpl<RzAttendanceMapper, RzA
@Resource
private ISysStaffService sysStaffService;
@Resource
private IRzOverTimeService rzOverTimeService;
@Resource
private IEqImagesService eqImagesService;
@Resource
private RzAttendanceDetailService rzAttendanceDetailService;
@ -489,7 +495,7 @@ public class RzAttendanceServiceImpl extends ServiceImpl<RzAttendanceMapper, RzA
try {
for (SysStaff sysStaff : sysStaffService.selectSysStaffListAll()){
Date date = DateUtils.addDays(new Date(), -1);
RzAttendance beforeAttendance = getBaseMapper().queryNowDayAttendanceByStatisticalIdAndDate(sysStaff.getUserId(), date);
RzAttendance beforeAttendance = queryNowDayAttendanceByStatisticalIdAndDate(sysStaff.getUserId(), date);
if(beforeAttendance.getWorkStartTime() != null && beforeAttendance.getWorkEndTime() == null){
sendAbnormalAttendance(sysStaff, beforeAttendance.getWorkStartTime());
@ -524,7 +530,8 @@ public class RzAttendanceServiceImpl extends ServiceImpl<RzAttendanceMapper, RzA
String result = DingUtils.getListRecord(Collections.asList(dingUserId), DateUtils.parseDateToStr("yyyy-MM-dd", new Date())+" 00:00:00", DateUtils.parseDateToStr("yyyy-MM-dd", new Date())+" 23:59:59");
System.out.println("打卡结果===>"+result);
JSONObject resultJson = JSON.parseObject(result);
if(Integer.valueOf(0).equals(resultJson.getInteger("errcode"))) try {
if(Integer.valueOf(0).equals(resultJson.getInteger("errcode")))
try {
{
JSONArray jsonArray = resultJson.getJSONArray("recordresult");
for (int i = 0; i < jsonArray.size(); i++) {
@ -538,13 +545,33 @@ public class RzAttendanceServiceImpl extends ServiceImpl<RzAttendanceMapper, RzA
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);
/* DingShiftGroup dingsShiftGroup = dingsShiftGroupMapper.selectByGroupIdAndTypeAndCheckTime(groupId, checkType, formatPlanCheckTime);
if(dingsShiftGroup != null){
//用户打卡时间
Long userCheckTime = ob.getLong("userCheckTime");
//工作时间 (这里指的是哪一天的卡)
Long workDate = ob.getLong("workDate");
/***
*//***
* 打卡结果
* Normal正常
* Early早退
@ -552,7 +579,7 @@ public class RzAttendanceServiceImpl extends ServiceImpl<RzAttendanceMapper, RzA
* SeriousLate严重迟到
* Absenteeism旷工迟到
* NotSigned未打卡
*/
*//*
String timeResult = ob.getString("timeResult");
Date formatWorkDate = Date.from(Instant.ofEpochMilli(workDate));
@ -560,9 +587,9 @@ public class RzAttendanceServiceImpl extends ServiceImpl<RzAttendanceMapper, RzA
//通过打卡类型, classId, 和排班打卡时间, 获取工作时长
//通过班次id拿到工作时长
DingShift dingsShift = dingsShiftMapper.selectOne(new LambdaQueryWrapper<DingShift>().eq(DingShift::getDingId, dingsShiftGroup.getShiftId()));
DingShift dingsShift = dingsShiftMapper.selectByDingId(dingsShiftGroup.getShiftId());
//通过workDate 获取哪一天的卡
RzAttendance attendance = getBaseMapper().queryNowDayAttendanceByStatisticalIdAndDate(sysStaff.getUserId(),formatWorkDate);
RzAttendance attendance = queryNowDayAttendanceByStatisticalIdAndDate(sysStaff.getUserId(),formatWorkDate);
//生成打卡记录
rzAttendanceDetailService.addDetail(attendance, ("OnDuty".equals(checkType) ? "上班卡" : "下班卡"), "/", formatUserCheckTime, "钉钉打卡", "");
//上班
@ -599,12 +626,15 @@ public class RzAttendanceServiceImpl extends ServiceImpl<RzAttendanceMapper, RzA
//检查是否开启加班
if(dingsShift.getOvertimeWork()){
//如果开启了加班
Long hours = (userCheckTime - attendance.getWorkStartTime().getTime())/1000/60/60;
new BigDecimal(hours).subtract(new BigDecimal(dingsShift.getWorkHour())).subtract(BigDecimal.valueOf(1)).subtract(new BigDecimal(String.valueOf(DataUtils.findDefaultValue(dingsShift.getDeductionHour(), 0))));
// BigDecimal hours = new BigDecimal(((userCheckTime - attendance.getWorkStartTime().getTime())/1000/60/60)).subtract(new BigDecimal(dingsShift.getWorkHour())).subtract(BigDecimal.valueOf(1)).subtract(new BigDecimal(String.valueOf(DataUtils.findDefaultValue(dingsShift.getDeductionHour(), 0))));
//使用排班下班时间作为加班开始时间
rzOverTimeService.overTimeCard(sysStaff, DateUtils.parseDate(DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", new Date(planCheckTime)), "yyyy-MM-dd HH:mm:ss"));
//员工打卡时间作为加班下班时间
rzOverTimeService.overTimeOffDutyCard(sysStaff, formatUserCheckTime, "/");
}
}else{
log.info("未找到相关考勤班次数据, 疑似加班打卡=====>>>>> {}", result);
}
}*/
break;
}
}
@ -616,6 +646,64 @@ public class RzAttendanceServiceImpl extends ServiceImpl<RzAttendanceMapper, RzA
}
}
@Override
public void addRzOverTime(String result, SysStaff sysStaff, DingShiftGroup dingsShiftGroup, String checkType, Date formatWorkDate, Date formatUserCheckTime, String timeResult, String remark){
if(dingsShiftGroup != null){
//通过打卡类型, classId, 和排班打卡时间, 获取工作时长
//通过班次id拿到工作时长
DingShift dingsShift = dingsShiftMapper.selectByDingId(dingsShiftGroup.getShiftId());
//通过workDate 获取哪一天的卡
RzAttendance 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)){
attendance.setWorkSum(BigDecimal.valueOf(dingsShift.getWorkHour()));
}else{
//早退需要计算工时
Long hours = (attendance.getWorkEndTime().getTime() - attendance.getWorkStartTime().getTime())/1000/60/60;
attendance.setWorkSum(BigDecimal.valueOf(hours));
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);
}
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);
}
@Async
protected void sendAbnormalAttendance(SysStaff sysStaff, Date date){

View File

@ -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<RzSpecialAttendanceMapper, RzSpecialAttendance> 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);
}
}

View File

@ -1,23 +1,21 @@
package com.evo.common.controller;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.evo.attendance.processor.impl.KQDeviceExchangeProcessor;
import com.evo.attendance.service.IRzAttendanceService;
import com.evo.common.core.controller.BaseController;
import com.evo.common.utils.StringUtils;
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.scheduling.annotation.Async;
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 javax.annotation.Resource;
import java.util.List;
import java.util.Map;
/**
@ -36,7 +34,10 @@ public class CallbackController extends BaseController {
@Autowired
IRzAttendanceService rzAttendanceService;
@Autowired
List<InstanceChangeExchangeProcessor> instanceChangeExchangeProcessorList;
@Autowired
List<DingCallBackExchangeProcessor> dingCallBackExchangeProcessorList;
@PostMapping("/ding/clock")
public Map<String, String> dingDingClock(@RequestBody Map<String, Object> params){
System.out.println(JSONObject.toJSONString(params));
@ -53,34 +54,52 @@ public class CallbackController extends BaseController {
// 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)) {
// 测试回调url的正确性
log.info("测试回调url的正确性");
} 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)) {
// 处理通讯录用户增加事件
}else if ("user_add_org".equals(eventType)) {
// 处理通讯录用户增加事件
log.info("发生了:" + eventType + "事件");
} else {
// 添加其他已注册的
log.info("发生了:" + 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<String, String> successMap = callbackCrypto.getEncryptedMap("success");

View File

@ -215,6 +215,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);

View File

@ -1,6 +1,5 @@
package com.evo.ding;
import com.aliyun.dingtalkoauth2_1_0.Client;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
import com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceResponseBody;
@ -38,17 +37,23 @@ public class DingUtils {
this.redisCache = redisCache;
}
private static Client createClient() throws Exception {
public static <T> 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";
return new Client(config);
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);
}
return null;
}
public static String getAccessToken() throws Exception {
String token = redisCache.getCacheObject(ACCESS_TOKEN_KEY);
if(StringUtils.isEmpty(token)){
Client client = createClient();
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);
@ -168,40 +173,58 @@ public class DingUtils {
return "";
}
public static com.aliyun.dingtalkworkflow_1_0.Client createClient1() throws Exception {
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
config.protocol = "https";
config.regionId = "central";
return new com.aliyun.dingtalkworkflow_1_0.Client(config);
}
/***
* 获取审批详情
* @param processInstanceId 审批示例id
*/
public static GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult getProcessInstanceInfo(String processInstanceId) throws Exception {
com.aliyun.dingtalkworkflow_1_0.Client client = DingUtils.createClient1();
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);
public static GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult getProcessInstanceInfo(String 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());
}
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 "";
}
}

View File

@ -1,5 +1,6 @@
package com.evo.ding.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
@ -16,6 +17,7 @@ import java.io.Serializable;
public class DingShift implements Serializable {
/** 主键 */
@TableId
private Long id;
/** 班次名称 */
public String name;

View File

@ -2,6 +2,8 @@ package com.evo.ding.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evo.ding.domain.DingShift;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* DingsShiftGroupMapper
@ -13,5 +15,5 @@ import com.evo.ding.domain.DingShift;
*/
public interface DingShiftMapper extends BaseMapper<DingShift> {
public DingShift selectByDingId(@Param("dingId") String dingId);
}

View File

@ -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<String, Object> 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;
}
}

View File

@ -0,0 +1,46 @@
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.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";
boolean accept(String approveType, String result, String status);
void exchange(GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult json);
default JSONObject getExtValue(GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult json, String componentType) {
List<GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultFormComponentValues> 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());
}
}

View File

@ -0,0 +1,89 @@
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.core.domain.entity.SysDictData;
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.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(GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult json) {
try {
SysStaff sysStaff = getSysStaffByDingUserId(json.getOriginatorUserId());
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);
rzLeaveDetail.setLeaveHour(extValueJson.getInteger("durationInHour"));
rzLeaveDetail.setRemarks("钉钉"+tag);
getIRzLeaveDetailService().insertRzLeaveDetail(rzLeaveDetail);
}
} catch (Exception e) {
log.error("执行钉钉 -[请假审批]-回调出现异常, 异常原因为:{}", e.getMessage());
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,118 @@
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 APPROVE_TYPE_OVERTIME.equals(approveType.toUpperCase()) && APPROVE_STATUS.equals(status.toUpperCase()) && APPROVE_RESULT.equals(result.toUpperCase());
}
@Override
@Transactional
public void exchange(GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult result) {
try {
SysStaff sysStaff = getSysStaffByDingUserId(result.getOriginatorUserId());
List<GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultFormComponentValues> 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();
}
}
}

View File

@ -0,0 +1,27 @@
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_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";
boolean accept(String eventType);
void exchange(JSONObject json);
}

View File

@ -0,0 +1,48 @@
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<AttendanceApproveStatusChangeExchangeProcessor> 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);
for (AttendanceApproveStatusChangeExchangeProcessor processor : approveStatusChangeExchangeProcessorList) {
if (processor.accept(json.getString("approveType"), result.getResult(), result.getStatus())) {
processor.exchange(result);
}
}
}
}

View File

@ -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"));
}
}
}
}

View File

@ -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<InstanceChangeExchangeProcessor> 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);
}
}
}
}

View File

@ -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);
}

View File

@ -0,0 +1,77 @@
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.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<GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultFormComponentValues> 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"), checkType, formatPlanCheckTime);
Date date= DateUtils.parseDate(bkTime, "yyyy-MM-dd HH:mm");
//生成补卡记录信息
getIRzAttendanceService().addRzOverTime(json.toString(), sysStaff, dingsShiftGroup, checkType, date, date, "/", "钉钉补卡-"+bkYi);
}
}
} catch (ParseException e) {
log.info("补卡出现错误, 为在本地找到班次数据, 错误信息为:{}, 回调信息为: {}", e.getMessage(), json.toString());
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,17 @@
package com.evo.ding.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.evo.ding.domain.DingShiftGroup;
/**
* DingShiftGroupService
*
* @author andy.shi
* @ClassName:DingShiftGroupService
* @date: 2026年03月11日 15:48
* @remark: 开发人员联系方式 1042025947@qq.com/微信同步
*/
public interface DingShiftGroupService extends IService<DingShiftGroup> {
DingShiftGroup selectByGroupIdAndTypeAndCheckTime(String groupId, String type, String checkTime);
}

View File

@ -0,0 +1,17 @@
package com.evo.ding.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.evo.ding.domain.DingShift;
/**
* DingsShiftService
*
* @author andy.shi
* @ClassName:DingsShiftService
* @date: 2026年03月11日 15:55
* @remark: 开发人员联系方式 1042025947@qq.com/微信同步
*/
public interface DingShiftService extends IService<DingShift> {
public DingShift selectByDingId(String dingId);
}

View File

@ -0,0 +1,23 @@
package com.evo.ding.service.impl;
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;
/**
* DingShiftGroupServiceImpl
*
* @author andy.shi
* @ClassName:DingShiftGroupServiceImpl
* @date: 2026年03月11日 15:49
* @remark: 开发人员联系方式 1042025947@qq.com/微信同步
*/
@Service
public class DingShiftGroupServiceImpl extends ServiceImpl<DingShiftGroupMapper, DingShiftGroup> implements DingShiftGroupService {
@Override
public DingShiftGroup selectByGroupIdAndTypeAndCheckTime(String groupId, String type, String checkTime) {
return getBaseMapper().selectByGroupIdAndTypeAndCheckTime(groupId, type, checkTime);
}
}

View File

@ -0,0 +1,26 @@
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;
/**
* DingShiftGroupServiceImpl
*
* @author andy.shi
* @ClassName:DingShiftGroupServiceImpl
* @date: 2026年03月11日 15:49
* @remark: 开发人员联系方式 1042025947@qq.com/微信同步
*/
@Service
public class DingShiftServiceImpl extends ServiceImpl<DingShiftMapper, DingShift> implements DingShiftService {
@Override
public DingShift selectByDingId(String dingId) {
return getBaseMapper().selectByDingId(dingId);
}
}

View File

@ -51,4 +51,10 @@ public interface RzLeaveMapper extends BaseMapper<RzLeave>
* @return
*/
public RzLeave queryRzLeaveByDateAndUserId(@Param("userId") Long userId, @Param("date") Date date);
/**
* 根据员工ID和时间查询请假统计
* @return
*/
public RzLeave queryRzLeaveByUserId(@Param("userId") Long userId);
}

View File

@ -59,4 +59,12 @@ public interface IRzLeaveService
public List<RzLeaveDetail> listLeaveDetails(RzLeave rzLeave);
List<RzLeave> selectRzLeaveListByDate(RzLeave rzLeave);
/**
* 根据员工
*
* @param userId 员工id
* @return 请假管理
*/
public RzLeave selectRzLeaveByUserId(Long userId);
}

View File

@ -1,5 +1,6 @@
package com.evo.personnelMatters.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.evo.common.core.domain.AjaxResult;
import com.evo.personnelMatters.domain.RzOverTimeDetail;
@ -11,7 +12,7 @@ import java.util.List;
* @author chenyj
* @date 2024-09-09
*/
public interface IRzOverTimeDetailService
public interface IRzOverTimeDetailService extends IService<RzOverTimeDetail>
{
/**
* 查询加班详情

View File

@ -1,9 +1,13 @@
package com.evo.personnelMatters.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.evo.common.core.domain.AjaxResult;
import com.evo.personnelMatters.domain.RzOverTime;
import com.evo.personnelMatters.domain.RzOverTimeDetail;
import com.evo.system.domain.SysStaff;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
/**
@ -12,7 +16,7 @@ import java.util.List;
* @author chenyj
* @date 2024-09-03
*/
public interface IRzOverTimeService
public interface IRzOverTimeService extends IService<RzOverTime>
{
/**
* 查询加班管理
@ -60,4 +64,10 @@ public interface IRzOverTimeService
* @return
*/
public List<RzOverTimeDetail> selectRzOverTimeDetailListByUserIdAndMonth(RzOverTime rzOverTime);
public RzOverTime selectRzOverTimeByNameAndMonth(Long userId, Date overTimeMonth);
public String overTimeCard(SysStaff sysStaff, Date overTimeMonth);
public String overTimeOffDutyCard(SysStaff sysStaff , Date date, String sn);
}

View File

@ -2,6 +2,7 @@ package com.evo.personnelMatters.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evo.common.annotation.DataScope;
import com.evo.common.constant.Constants;
import com.evo.common.core.domain.AjaxResult;
@ -29,7 +30,7 @@ import java.util.List;
* @date 2024-08-03
*/
@Service
public class RzLeaveServiceImpl implements IRzLeaveService
public class RzLeaveServiceImpl extends ServiceImpl<RzLeaveMapper, RzLeave> implements IRzLeaveService
{
@Resource
private RzLeaveMapper rzLeaveMapper;
@ -160,4 +161,9 @@ public class RzLeaveServiceImpl implements IRzLeaveService
wa.like(StringUtils.isNotBlank(rzLeave.getDeptName()),RzLeave::getDeptName,rzLeave.getDeptName());
return rzLeaveMapper.selectList(wa);
}
@Override
public RzLeave selectRzLeaveByUserId(Long userId) {
return getBaseMapper().queryRzLeaveByUserId(userId);
}
}

View File

@ -1,5 +1,6 @@
package com.evo.personnelMatters.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evo.attendance.domain.RzAttendance;
import com.evo.attendance.domain.RzAttendanceDetail;
import com.evo.attendance.mapper.RzAttendanceDetailMapper;
@ -34,7 +35,7 @@ import java.util.List;
* @date 2024-09-09
*/
@Service
public class RzOverTimeDetailServiceImpl implements IRzOverTimeDetailService
public class RzOverTimeDetailServiceImpl extends ServiceImpl<RzOverTimeDetailMapper, RzOverTimeDetail> implements IRzOverTimeDetailService
{
@Resource
private RzOverTimeDetailMapper rzOverTimeDetailMapper;

View File

@ -1,9 +1,12 @@
package com.evo.personnelMatters.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evo.attendance.processor.KqUtils;
import com.evo.attendance.processor.PunchTheClockStrategyExchangeProcessor;
import com.evo.common.annotation.DataScope;
import com.evo.common.constant.Constants;
import com.evo.common.core.domain.AjaxResult;
import com.evo.common.utils.DataUtils;
import com.evo.common.utils.DateUtils;
import com.evo.common.utils.SecurityUtils;
import com.evo.common.utils.StringUtils;
@ -15,10 +18,12 @@ import com.evo.personnelMatters.service.IRzOverTimeService;
import com.evo.system.domain.SysStaff;
import com.evo.system.mapper.SysDeptMapper;
import com.evo.system.mapper.SysStaffMapper;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
@ -151,5 +156,77 @@ public class RzOverTimeServiceImpl extends ServiceImpl<RzOverTimeMapper, RzOverT
return res_list;
}
@Override
public RzOverTime selectRzOverTimeByNameAndMonth(Long userId, Date overTimeMonth) {
return getBaseMapper().selectRzOverTimeByNameAndMonth(userId, overTimeMonth);
}
@Override
public String overTimeCard(SysStaff sysStaff, Date overTimeMonth) {
//判断打卡月是否统计过加班
RzOverTime rzOverTime = getBaseMapper().selectRzOverTimeByNameAndMonth(sysStaff.getUserId(),overTimeMonth);
if(rzOverTime == null){
rzOverTime = new RzOverTime();
rzOverTime.setUserId(sysStaff.getUserId());
rzOverTime.setOverHours(new BigDecimal("0.0"));
rzOverTime.setDeptId(sysStaff.getDeptId());
rzOverTime.setName(sysStaff.getName());
rzOverTime.setOverTimeMonth(overTimeMonth);
if(getBaseMapper().insert(rzOverTime) < 1){
return PunchTheClockStrategyExchangeProcessor.initStaticMessage(1, "打卡失败");
}
}
RzOverTimeDetail rzOverTimeDetail = new RzOverTimeDetail();
rzOverTimeDetail.setOverTimeId(rzOverTime.getId());
rzOverTimeDetail.setOverTimeStart(overTimeMonth);
rzOverTimeDetail.setName(sysStaff.getName());
rzOverTimeDetail.setDelFlag(Constants.DELETE_FLAG_0);
rzOverTimeDetail.setCreateBy("admin");
rzOverTimeDetail.setCreateTime(DateUtils.getNowDate());
return (rzOverTimeDetailMapper.insert(rzOverTimeDetail) < 1) ? PunchTheClockStrategyExchangeProcessor.initStaticMessage(1, "打卡失败") : PunchTheClockStrategyExchangeProcessor.initStaticMessage(0, "打卡成功");
}
@Override
public String overTimeOffDutyCard(SysStaff sysStaff, Date date, String sn) {
//修改加班打卡记录 根据员工ID和时间查找 统计数据
RzOverTime rzOverTime = getBaseMapper().selectRzOverTimeByNameAndMonth(sysStaff.getUserId(),date);
//如果没有加班汇总, 生成一条新的
if(ObjectUtils.isEmpty(rzOverTime)){
rzOverTime = new RzOverTime();
rzOverTime.setUserId(sysStaff.getUserId());
rzOverTime.setDeptId(sysStaff.getDeptId());
rzOverTime.setName(sysStaff.getName());
rzOverTime.setOverTimeMonth(date);
rzOverTime.setOverHours(DataUtils.DEFAULT_VALUE);
rzOverTime.setDelFlag(Constants.DELETE_FLAG_0);
rzOverTime.setCreateBy("admin");
rzOverTime.setCreateTime(new Date());
getBaseMapper().insert(rzOverTime);
}
//查找加班详情 加班统计ID和加班开始时间
RzOverTimeDetail rzOverTimeDetail = rzOverTimeDetailMapper.queryRzOverTimeDetailByDateAndOverId(rzOverTime.getId(),date);
if(ObjectUtils.isEmpty(rzOverTimeDetail)){
//特殊情况, 会存在过12点的情况, 这时候需要核查下前一天没有下班的加班数据
rzOverTimeDetail = rzOverTimeDetailMapper.queryRzOverTimeDetailByDateAndOverIdAndOverTimeEndIsNull(rzOverTime.getId(), DateUtils.addDays(date,-1));
//如果前一天也为空
if(ObjectUtils.isEmpty(rzOverTimeDetail)){
return PunchTheClockStrategyExchangeProcessor.initStaticMessage(1, "未找到当天的加班信息, 请补卡");
}
}
rzOverTimeDetail.setOverTimeEnd(date);
//计算加班时长 分钟
KqUtils.calculateOverTimeHours(rzOverTimeDetail, sysStaff.getUserId(), sn);
if(rzOverTimeDetailMapper.updateRzOverTimeDetail(rzOverTimeDetail) < 1){
return PunchTheClockStrategyExchangeProcessor.initStaticMessage(1, "打卡失败");
}
//加班修改统计
rzOverTime.setOverHours(rzOverTime.getOverHours().add(rzOverTimeDetail.getOverTimeHours()));
if(getBaseMapper().updateRzOverTime(rzOverTime) < 1){
return PunchTheClockStrategyExchangeProcessor.initStaticMessage(1, "打卡失败");
}
return PunchTheClockStrategyExchangeProcessor.initStaticMessage(0, "打卡成功");
}
}

View File

@ -8,8 +8,7 @@ import com.evo.common.core.domain.entity.SysDictData;
*
* @author evo
*/
public interface ISysDictDataService
{
public interface ISysDictDataService {
/**
* 根据条件分页查询字典数据
*
@ -66,4 +65,11 @@ public interface ISysDictDataService
* @return 结果
*/
public int updateDictData(SysDictData dictData);
/**
* 根据条件分页查询字典数据
*
* @return 字典数据集合信息
*/
public List<SysDictData> selectDictDataListByType(String type);
}

View File

@ -108,13 +108,18 @@ public interface ISysStaffService extends IService<SysStaff>
* @param userId 重新入职员工Id
*/
AjaxResult reEmployment(Long userId);
/***
* 数据导出
* @param response
* @param sysStaff
*/
void exportInfo(HttpServletResponse response, SysStaff sysStaff);
/**
* 根据钉钉id 查询员工信息
*
* @param dingUserId 钉钉userId
* @return 员工管理
*/
public SysStaff selectSysStaffByDingUserId(String dingUserId);
}

View File

@ -117,4 +117,9 @@ public class SysDictDataServiceImpl extends ServiceImpl<SysDictDataMapper, SysDi
}
return row;
}
@Override
public List<SysDictData> selectDictDataListByType(String type) {
return getBaseMapper().selectDictDataByType(type);
}
}

View File

@ -838,4 +838,9 @@ public class SysStaffServiceImpl extends ServiceImpl<SysStaffMapper, SysStaff> i
ExcelUtil1<SysStaffVo> util = new ExcelUtil1<SysStaffVo>(SysStaffVo.class);
util.exportExcel(response, res_list, "员工信息");
}
@Override
public SysStaff selectSysStaffByDingUserId(String dingUserId) {
return getBaseMapper().selectSysStaffByDingUserId(dingUserId);
}
}

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.evo.ding.mapper.DingShiftMapper">
<resultMap type="DingShift" id="DingShiftResult">
<result property="id" column="id" />
<result property="name" column="name" />
<result property="dingId" column="ding_Id" />
<result property="workHour" column="work_Hour" />
<result property="overtimeWork" column="overtime_Work" />
<result property="deductionHour" column="deduction_Hour" />
</resultMap>
<sql id="selectVo">
select id, name, ding_Id, work_Hour, overtime_Work, deduction_Hour from ding_shift
</sql>
<select id="selectByDingId" resultMap="DingShiftResult">
<include refid="selectVo"/>
where ding_id = #{dingId}
</select>
<!-- <select id="selectEqButtonList" parameterType="EqButton" resultMap="EqButtonResult">-->
<!-- <include refid="selectEqButtonVo"/>-->
<!-- <where>-->
<!-- del_flag = '0'-->
<!-- <if test="name != null and name != ''"> and name like concat('%', #{name}, '%')</if>-->
<!-- </where>-->
<!-- order by num asc-->
<!-- </select>-->
<!-- <select id="selectEqButtonById" parameterType="Long" resultMap="EqButtonResult">-->
<!-- <include refid="selectEqButtonVo"/>-->
<!-- where id = #{id}-->
<!-- </select>-->
<!-- <insert id="insertEqButton" parameterType="EqButton" useGeneratedKeys="true" keyProperty="id">-->
<!-- insert into eq_button-->
<!-- <trim prefix="(" suffix=")" suffixOverrides=",">-->
<!-- <if test="num != null">num,</if>-->
<!-- <if test="name != null">name,</if>-->
<!-- <if test="image != null">image,</if>-->
<!-- <if test="delFlag != null">del_flag,</if>-->
<!-- <if test="remarks != null">remarks,</if>-->
<!-- <if test="createBy != null">create_by,</if>-->
<!-- <if test="createTime != null">create_time,</if>-->
<!-- <if test="updateBy != null">update_by,</if>-->
<!-- <if test="updateTime != null">update_time,</if>-->
<!-- </trim>-->
<!-- <trim prefix="values (" suffix=")" suffixOverrides=",">-->
<!-- <if test="num != null">#{num},</if>-->
<!-- <if test="name != null">#{name},</if>-->
<!-- <if test="image != null">#{image},</if>-->
<!-- <if test="delFlag != null">#{delFlag},</if>-->
<!-- <if test="remarks != null">#{remarks},</if>-->
<!-- <if test="createBy != null">#{createBy},</if>-->
<!-- <if test="createTime != null">#{createTime},</if>-->
<!-- <if test="updateBy != null">#{updateBy},</if>-->
<!-- <if test="updateTime != null">#{updateTime},</if>-->
<!-- </trim>-->
<!-- </insert>-->
<!-- <update id="updateEqButton" parameterType="EqButton">-->
<!-- update eq_button-->
<!-- <trim prefix="SET" suffixOverrides=",">-->
<!-- <if test="num != null">num = #{num},</if>-->
<!-- <if test="name != null">name = #{name},</if>-->
<!-- <if test="image != null">image = #{image},</if>-->
<!-- <if test="delFlag != null">del_flag = #{delFlag},</if>-->
<!-- <if test="remarks != null">remarks = #{remarks},</if>-->
<!-- <if test="createBy != null">create_by = #{createBy},</if>-->
<!-- <if test="createTime != null">create_time = #{createTime},</if>-->
<!-- <if test="updateBy != null">update_by = #{updateBy},</if>-->
<!-- <if test="updateTime != null">update_time = #{updateTime},</if>-->
<!-- </trim>-->
<!-- where id = #{id}-->
<!-- </update>-->
<!-- &lt;!&ndash; 根据名称查询信息 &ndash;&gt;-->
<!-- <select id="selectEqButtonByName" parameterType="String" resultMap="EqButtonResult">-->
<!-- <include refid="selectEqButtonVo"/>-->
<!-- where del_flag = '0' and name = #{name}-->
<!-- </select>-->
</mapper>

View File

@ -116,4 +116,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where del_flag = '0' and DATE_FORMAT( leave_date, '%Y%m' ) = DATE_FORMAT( #{date} , '%Y%m' ) and user_id = #{userId}
</select>
<select id="queryRzLeaveByUserId" resultMap="RzLeaveResult">
<include refid="selectRzLeaveVo"/>
where del_flag = '0' and user_id = #{userId} order by create_time desc limit 1
</select>
</mapper>