提交 59f20c9e authored 作者: 窦馨雨's avatar 窦馨雨

合并分支 'dxy' 到 'qa'

新增勤策飞书考勤明细同步 查看合并请求 !124
package com.sfa.job.domain.feishu.dao;
import com.sfa.job.pojo.feishu.response.FeishuLeaveInfoDTO;
import java.util.List;
/**
* Dao接口:定义所有核心方法,Service仅调用此处方法
*/
public interface FeishuLeaveInfoDao {
/**
* 解析飞书原始JSON为DTO列表(核心解析逻辑)
*/
List<FeishuLeaveInfoDTO> parseFeishuLeaveRawJson(String rawJson);
/**
* 同步飞书请假数据(核心新增/更新逻辑)
*/
String syncFeishuLeaveData(String syncDate);
/**
* 根据请假唯一ID查询DTO
*/
FeishuLeaveInfoDTO selectByLeaveRequestId(String leaveRequestId);
}
\ No newline at end of file
package com.sfa.job.domain.feishu.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName("feishu_leave_info")
public class FeishuLeaveInfo implements Serializable {
@TableField(exist = false)
private static final long serialVersionUID = 1L;
// 逻辑删除常量
// 未删除
@TableField(exist = false)
public static final Integer IS_DELETE_NO = 1;
// 已删除
@TableField(exist = false)
public static final Integer IS_DELETE_YES = 0;
@TableId(type = IdType.AUTO)
private Long id;
private String employmentId;
private String employmentName;
private String employmentNo;
private String endTime;
private String grantSource;
private String leaveDuration;
private Integer leaveDurationUnit;
private String leaveProcessId;
private String leaveRequestId;
private Integer leaveRequestStatus;
private String leaveTypeId;
private String leaveTypeName;
private String notes;
private String returnTime;
private String startTime;
private String submittedAt;
private String submittedBy;
private String timeZone;
private String leaveCorrectProcessId;
private String processApplyTime;
private String processId;
private String processStatus;
private Date syncCreateTime;
private Date syncUpdateTime;
@TableLogic(value = "1", delval = "0")
private Integer isDelete;
}
\ No newline at end of file
package com.sfa.job.domain.feishu.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sfa.job.domain.feishu.entity.FeishuLeaveInfo;
import org.springframework.stereotype.Repository;
@Repository
public interface FeishuLeaveInfoMapper extends BaseMapper<FeishuLeaveInfo> {
}
\ No newline at end of file
package com.sfa.job.domain.qince.dao;
import com.sfa.job.pojo.qince.response.QinceUserStatisticDTO;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 勤策考勤用户统计 DAO 接口(纯 DTO 操作,不暴露 Entity)
*
*/
public interface IQinceUserStatisticDao {
/**
* 构建已存在考勤记录的映射(qcUserId+attDate → DTO)
* @param userIdList 用户ID列表
* @param dateList 考勤日期列表(Date 类型)
* @return 唯一键与 DTO 的映射(纯 DTO,无 Entity 暴露)
*/
Map<String, QinceUserStatisticDTO> buildExistAttendanceMap(List<Long> userIdList, List<LocalDate> dateList);
/**
* 批量新增考勤记录(入参为 DTO 列表)
* @param dtoList 考勤 DTO 列表
* @return 新增成功条数
*/
int batchInsert(List<QinceUserStatisticDTO> dtoList);
/**
* 批量更新考勤记录(入参为 DTO 列表,需包含主键 ID)
* @param dtoList 考勤 DTO 列表
* @return 更新成功条数
*/
int batchUpdate(List<QinceUserStatisticDTO> dtoList);
}
\ No newline at end of file
package com.sfa.job.domain.qince.dao;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sfa.job.domain.qince.entity.QinceUserStatistic;
import com.sfa.job.domain.qince.mapper.QinceUserStatisticMapper;
import com.sfa.job.pojo.qince.response.QinceUserStatisticDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
/**
* 勤策考勤用户统计 DAO 实现类(纯 DTO 对外,内部转换 Entity 调用 Mapper)
*/
@Slf4j
@Service
@DS("bi")
public class QinceUserStatisticDaoImpl implements IQinceUserStatisticDao {
@Autowired
private QinceUserStatisticMapper qinceUserStatisticMapper;
// 配置项
private static final int BATCH_SIZE = 200;
// 线程安全的日期格式化器(适配 Date 类型,避免多线程问题)
private static final ThreadLocal<SimpleDateFormat> DATE_SDF = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
// ===================== 内部工具:DTO ↔ Entity 转换(隔离对外接口与持久化层) =====================
/**
* DTO 转换为 Entity(用于调用 Mapper 进行持久化)
*/
private QinceUserStatistic dtoToEntity(QinceUserStatisticDTO dto) {
if (dto == null) {
return null;
}
QinceUserStatistic entity = new QinceUserStatistic();
// BeanUtils 拷贝同名同类型字段(Date/Long/String 均兼容)
BeanUtils.copyProperties(dto, entity);
return entity;
}
/**
* Entity 转换为 DTO(用于隐藏 Entity,对外返回纯净 DTO)
*/
private QinceUserStatisticDTO entityToDto(QinceUserStatistic entity) {
if (entity == null) {
return null;
}
QinceUserStatisticDTO dto = new QinceUserStatisticDTO();
// BeanUtils 拷贝同名同类型字段
BeanUtils.copyProperties(entity, dto);
return dto;
}
/**
* DTO 列表 转换为 Entity 列表(批量操作适配)
*/
private List<QinceUserStatistic> dtoListToEntityList(List<QinceUserStatisticDTO> dtoList) {
if (CollectionUtils.isEmpty(dtoList)) {
return new ArrayList<>(0);
}
return dtoList.stream()
.map(this::dtoToEntity)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* Entity 列表 转换为 DTO 列表(批量操作适配)
*/
private List<QinceUserStatisticDTO> entityListToDtoList(List<QinceUserStatistic> entityList) {
if (CollectionUtils.isEmpty(entityList)) {
return new ArrayList<>(0);
}
return entityList.stream()
.map(this::entityToDto)
.collect(Collectors.toList());
}
// ===================== DAO 接口实现:纯 DTO 对外,内部转换调用 Mapper =====================
@Override
public Map<String, QinceUserStatisticDTO> buildExistAttendanceMap(List<Long> userIdList, List<LocalDate> dateList) {
if (CollectionUtils.isEmpty(userIdList) || CollectionUtils.isEmpty(dateList)) {
return new HashMap<>(0);
}
// 1. 构建 Mapper 查询条件(仅操作 Entity,按需查询字段提升性能)
LambdaQueryWrapper<QinceUserStatistic> queryWrapper = new LambdaQueryWrapper<QinceUserStatistic>()
.in(QinceUserStatistic::getQcUserId, userIdList)
.in(QinceUserStatistic::getAttDate, dateList)
.select(QinceUserStatistic::getId, QinceUserStatistic::getQcUserId,
QinceUserStatistic::getAttDate);
// 2. Mapper 操作 Entity,查询数据库(唯一直接操作 Entity 的地方)
List<QinceUserStatistic> existEntityList = qinceUserStatisticMapper.selectList(queryWrapper);
// 3. 转换 Entity 为 DTO,构建唯一键映射(对外隐藏 Entity,返回纯 DTO)
Map<String, QinceUserStatisticDTO> existDtoMap = new HashMap<>(existEntityList.size());
for (QinceUserStatistic entity : existEntityList) {
QinceUserStatisticDTO dto = entityToDto(entity);
String dateStr = DATE_SDF.get().format(entity.getAttDate());
String uniqueKey = entity.getQcUserId() + "_" + dateStr;
existDtoMap.put(uniqueKey, dto);
}
return existDtoMap;
}
@Override
public int batchInsert(List<QinceUserStatisticDTO> dtoList) {
int count = 0;
if (CollectionUtils.isEmpty(dtoList)) {
return count;
}
// 1. 分批处理,避免 SQL 语句过长(保护数据库,防止超出最大允许长度)
for (int i = 0; i < dtoList.size(); i += BATCH_SIZE) {
int end = Math.min(i + BATCH_SIZE, dtoList.size());
List<QinceUserStatisticDTO> batchDtoList = dtoList.subList(i, end);
// 2. 内部转换:DTO 列表 → Entity 列表(仅 Mapper 能处理 Entity)
List<QinceUserStatistic> batchEntityList = dtoListToEntityList(batchDtoList);
// 3. Mapper 执行批量插入(当前为循环单条,可优化为 MyBatis 批量 SQL)
for (QinceUserStatistic entity : batchEntityList) {
qinceUserStatisticMapper.insert(entity);
count++;
}
}
log.info("DAO层批量新增{}条考勤记录(DTO → Entity 转换后持久化)", count);
return count;
}
@Override
public int batchUpdate(List<QinceUserStatisticDTO> dtoList) {
int count = 0;
if (CollectionUtils.isEmpty(dtoList)) {
return count;
}
// 分批处理,提升执行效率
for (int i = 0; i < dtoList.size(); i += BATCH_SIZE) {
int end = Math.min(i + BATCH_SIZE, dtoList.size());
List<QinceUserStatisticDTO> batchDtoList = dtoList.subList(i, end);
// 内部转换:DTO 列表 → Entity 列表(需包含主键 ID,否则更新失败)
List<QinceUserStatistic> batchEntityList = dtoListToEntityList(batchDtoList);
// Mapper 执行批量更新(基于主键 ID,当前为循环单条,可优化为 MyBatis 批量 SQL)
for (QinceUserStatistic entity : batchEntityList) {
if (entity.getId() != null) {
qinceUserStatisticMapper.updateById(entity);
count++;
} else {
log.warn("考勤记录无主键 ID,跳过更新:qcUserId={}", entity.getQcUserId());
}
}
}
log.info("DAO层批量更新{}条考勤记录(DTO → Entity 转换后持久化)", count);
return count;
}
}
\ No newline at end of file
package com.sfa.job.domain.qince.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@TableName("qince_user_statistic")
public class QinceUserStatistic implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID 唯一标识
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 部门名称
*/
@TableField("dept_name")
private String deptName;
/**
* 勤策用户ID
*/
@TableField("qc_user_id")
private Long qcUserId;
/**
* 性别
*/
@TableField("sex")
private String sex;
/**
* 用户名称
*/
@TableField("user_name")
private String userName;
/**
* 所属考勤组
*/
@TableField(value = "`group`")
private String group;
/**
* 用户工号
*/
@TableField("employee_code")
private String employeeCode;
/**
* 定位是否虚假模拟位置。0:非虚假模拟位置,1:虚假模拟位置
*/
@TableField("check_in_attd_lie_locate")
private Integer checkInAttdLieLocate;
/**
* 上班打卡位置
*/
@TableField("check_in_attd_address")
private String checkInAttdAddress;
/**
* 上班是否脱岗。0:正常,1:脱岗
*/
@TableField("check_in_attd_lc_error")
private Integer checkInAttdLcError;
/**
* 上班考勤状态。0:正常,1:迟到,2:异常
*/
@TableField("check_in_attd_status")
private Integer checkInAttdStatus;
/**
* 上班打卡时间
*/
@TableField("check_in_attd_time")
private LocalDateTime checkInAttdTime;
/**
* 考勤日期
*/
@TableField("att_date")
private LocalDate attDate;
/**
* 工作时长
*/
@TableField("work_time")
private Double workTime;
/**
* 备注信息
*/
@TableField("remarks")
private String remarks;
/**
* 下班定位是否虚假模拟位置。0:非虚假模拟位置,1:虚假模拟位置
*/
@TableField("check_out_attd_lie_locate")
private Integer checkOutAttdLieLocate;
/**
* 下班打卡位置
*/
@TableField("check_out_attd_address")
private String checkOutAttdAddress;
/**
* 下班是否脱岗。0:正常,1:脱岗
*/
@TableField("check_out_attd_lc_error")
private Integer checkOutAttdLcError;
/**
* 下班考勤状态。0:正常,1:迟到,2:异常
*/
@TableField("check_out_attd_status")
private Integer checkOutAttdStatus;
/**
* 下班考勤时间
*/
@TableField("check_out_attd_time")
private LocalDateTime checkOutAttdTime;
/**
* 删除标记。0:删除 1:正常
*/
@TableField(value = "is_delete")
private Integer isDelete = 1;
/**
* 创建时间
*/
@TableField(value = "create_time")
private LocalDateTime createTime;
/**
* 修改时间
*/
@TableField(value = "modify_time")
private LocalDateTime modifyTime;
}
\ No newline at end of file
package com.sfa.job.domain.qince.mapper;
import com.sfa.job.domain.qince.entity.QinceUserStatistic;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author 45810
* @description 针对表【qince_user_statistic(勤策考勤明细表- 每天同步三次)】的数据库操作Mapper
* @createDate 2026-01-28 15:38:34
* @Entity com.sfa.job.domain.qince.qince.QinceUserStatistic
*/
public interface QinceUserStatisticMapper extends BaseMapper<QinceUserStatistic> {
}
package com.sfa.job.pojo.feishu.response;
import lombok.Data;
import java.util.Date;
@Data
public class FeishuLeaveInfoDTO {
private Long id;
// 逻辑删除常量(优化)
// 未删除
public static final Integer IS_DELETE_NO = 1;
// 已删除
public static final Integer IS_DELETE_YES = 0;
// 业务字段
private String employmentId;
private String employmentName;
private String employmentNo;
private String endTime;
private String grantSource;
private String leaveDuration;
private Integer leaveDurationUnit;
private String leaveProcessId;
private String leaveRequestId;
private Integer leaveRequestStatus;
private String leaveTypeId;
private String leaveTypeName;
private String notes;
private String returnTime;
private String startTime;
private String submittedAt;
private String submittedBy;
private String timeZone;
private String leaveCorrectProcessId;
// 嵌套更正流程字段
private String processApplyTime;
private String processId;
private String processStatus;
// 系统字段
private Date syncCreateTime;
private Date syncUpdateTime;
private Integer isDelete;
}
\ No newline at end of file
package com.sfa.job.pojo.qince.response;
import lombok.Data;
import org.springframework.cglib.core.Local;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
/**
* 勤策考勤明细表 DTO 类
* 对应表:qince_user_statistic
* 表注释:勤策考勤明细表- 每天同步三次
*
* @author douxinyu
* @date 2026-01-28
*/
@Data
public class QinceUserStatisticDTO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID 唯一标识
*/
private Long id;
/**
* 部门名称
*/
private String deptName;
/**
* 勤策用户ID
*/
private Long qcUserId;
/**
* 性别
*/
private String sex;
/**
* 用户名称
*/
private String userName;
/**
* 所属考勤组
*/
private String group;
/**
* 用户工号
*/
private String employeeCode;
/**
* 定位是否虚假模拟位置。0:非虚假模拟位置,1:虚假模拟位置
*/
private Integer checkInAttdLieLocate;
/**
* 上班打卡位置
*/
private String checkInAttdAddress;
/**
* 上班是否脱岗。0:正常,1:脱岗
*/
private Integer checkInAttdLcError;
/**
* 上班考勤状态。0:正常,1:迟到,2:异常
*/
private Integer checkInAttdStatus;
/**
* 上班打卡时间
*/
private LocalDateTime checkInAttdTime;
/**
* 考勤日期
*/
private LocalDate attDate;
/**
* 工作时长
*/
private Double workTime;
/**
* 备注信息
*/
private String remarks;
/**
* 下班定位是否虚假模拟位置。0:非虚假模拟位置,1:虚假模拟位置
*/
private Integer checkOutAttdLieLocate;
/**
* 下班打卡位置
*/
private String checkOutAttdAddress;
/**
* 下班是否脱岗。0:正常,1:脱岗
*/
private Integer checkOutAttdLcError;
/**
* 下班考勤状态。0:正常,1:迟到,2:异常
*/
private Integer checkOutAttdStatus;
/**
* 下班考勤时间
*/
private LocalDateTime checkOutAttdTime;
/**
* 删除标记。0:删除 1:正常
*/
private Integer isDelete = 1;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 修改时间
*/
private LocalDateTime modifyTime;
}
\ No newline at end of file
package com.sfa.job.service.feishu;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.sfa.job.domain.feishu.dao.FeishuLeaveInfoDao;
import com.sfa.job.pojo.feishu.response.FeishuLeaveInfoDTO;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* Service实现:仅简单调用Dao方法,无任何核心业务逻辑
*/
@Service
@DS("bi")
public class FeishuLeaveInfoServiceImpl implements IFeishuLeaveInfoService {
@Resource
private FeishuLeaveInfoDao feishuLeaveInfoDao;
@Override
public String syncFeishuLeaveData(String syncDate) {
return feishuLeaveInfoDao.syncFeishuLeaveData(syncDate);
}
@Override
public FeishuLeaveInfoDTO getByLeaveRequestId(String leaveRequestId) {
return feishuLeaveInfoDao.selectByLeaveRequestId(leaveRequestId);
}
}
\ No newline at end of file
package com.sfa.job.service.feishu;
import com.sfa.job.pojo.feishu.response.FeishuLeaveInfoDTO;
/**
* Service接口:仅定义调用方法,无核心逻辑
*/
public interface IFeishuLeaveInfoService {
/**
* 同步飞书请假数据(调用Dao,无核心逻辑)
*/
String syncFeishuLeaveData(String syncDate);
/**
* 根据请假唯一ID查询(调用Dao)
*/
FeishuLeaveInfoDTO getByLeaveRequestId(String leaveRequestId);
}
\ No newline at end of file
package com.sfa.job.service.qince;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
/**
* @Author: DouXinYu
* @Date: 2026-01-28 10:54
* @Description: 勤策用户考勤明细服务类
*/
public interface IQinceUserStatisticService {
JSONObject getUserStatistic(String startDate, String endDate, Integer page, Integer size) throws Exception;
/**
* 查询并保存前一天的勤策考勤数据(新增/修改判断)
* @return 处理的记录数
* @throws Exception 异常
*/
int queryAndSaveYesterdayAttendance()throws Exception;
/**
* 查询并保存当天的勤策考勤数据(新增/修改判断)
* @return 处理的记录数
* @throws Exception 异常
*/
int queryAndSaveTodayAttendance()throws Exception;
}
......@@ -51,12 +51,14 @@ public class QinCeUtils {
// 修改人员
public static final String MODIFY_USER = "/api/employee/v3/modifyEmployee/";
// 商品信息查询
public static final String QUERY_PRODUCT_INFO = "/api/product/v1/queryProduct/";
// 商品价格更新
public static final String MODIFY_PRODUCT_PRICE = "/api/pd/v2/modifyProduct/";
// 获取考勤明细统计数据接口
public static final String GET_USER_STATISTIC = "/api/attStatistics/v1/getUserStatistic/";
public String builderUrl(String sidepath, Map<String, Object> params) {
String msgId = UUID.randomUUID().toString();
......@@ -196,4 +198,43 @@ public class QinCeUtils {
return false;
}
}
/**
* 构建获取考勤明细的请求参数
* @param startDate 开始日期(格式:yyyy-MM-dd)
* @param endDate 结束日期(格式:yyyy-MM-dd)
* @param page 当前第几页(非必填,默认1)
* @param size 当前页记录条数(非必填)
* @return 符合接口要求的参数Map
*/
public Map<String, Object> getUserStatisticParams(String startDate, String endDate, Integer page, Integer size) {
Map<String, Object> params = new HashMap<>();
// 必传参数:开始日期、结束日期
params.put("startDate", startDate);
params.put("endDate", endDate);
// 非必传参数:页码、每页条数(为空则不设置)
if (page != null) {
params.put("page", page);
}
if (size != null) {
params.put("size", size);
}
return params;
}
/**
* 调用勤策获取考勤明细统计数据接口
* @param startDate 开始日期(yyyy-MM-dd)
* @param endDate 结束日期(yyyy-MM-dd)
* @param page 页码(可为null,默认1)
* @param size 每页条数(可为null)
* @return 考勤明细数据JSON数组
* @throws Exception 接口调用异常(如网络错误、返回码非0等)
*/
public JSONObject getUserStatistic(String startDate, String endDate, Integer page, Integer size) throws Exception {
Map<String, Object> params = getUserStatisticParams(startDate, endDate, page, size);
String url = builderUrl(GET_USER_STATISTIC, params);
log.info("调用勤策考勤明细接口,URL:{},参数:{}", url, params);
return postQC(url, params);
}
}
package com.sfa.job.xxljob.feishu;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.sfa.job.service.feishu.IFeishuLeaveInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* 飞书请假数据XXL-JOB定时任务类(适配实际Service:syncFeishuLeaveData(String syncDate))
* 任务:每天00:02同步前一天的请假数据
*/
@Slf4j
@Component
public class FeiShuLeaveXxlJob {
@Autowired
private IFeishuLeaveInfoService feishuLeaveInfoService;
// 日期格式化器(与勤策保持一致,适配 syncDate 入参:yyyy-MM-dd)
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
/**
* 飞书请假数据同步任务(XXL-JOB核心方法)
* 调度配置:每天02:00执行(Cron表达式:0 0 2 * * ?)
*/
@XxlJob("feiShuLeaveSyncJob")
public void feiShuLeaveSyncJob() {
// 任务日志记录(XXL-JOB控制台可见)
XxlJobHelper.log("===== 开始执行飞书请假数据同步任务(同步前一天数据) =====");
// 获取前一天日期(格式:yyyy-MM-dd),适配Service入参要求
LocalDate yesterday = LocalDate.now().minusDays(1);
String syncDate = yesterday.format(DATE_FORMATTER);
XxlJobHelper.log("===== 本次同步目标日期:{} =====", syncDate);
try {
// 调用你实际的飞书请假同步方法(入参:前一天日期字符串)
String syncResult = feishuLeaveInfoService.syncFeishuLeaveData(syncDate);
// 任务结果日志(适配Service返回的字符串结果)
XxlJobHelper.log("===== 飞书请假数据同步任务执行完成,同步结果:{} =====", syncResult);
// 标记任务执行成功(XXL-JOB状态回调)
XxlJobHelper.handleSuccess("飞书请假数据同步成功,同步结果:" + syncResult);
} catch (Exception e) {
// 异常处理与日志记录
log.error("===== 飞书请假数据同步任务执行失败 =====", e);
XxlJobHelper.log("===== 飞书请假数据同步任务执行失败,异常信息:{} =====", e.getMessage());
// 标记任务执行失败(XXL-JOB状态回调)
XxlJobHelper.handleFail("飞书请假数据同步失败,异常信息:" + e.getMessage());
}
}
}
\ No newline at end of file
package com.sfa.job.xxljob.qince;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.sfa.job.service.qince.IQinceUserStatisticService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 勤策考勤数据XXL-JOB定时任务类
*/
@Slf4j
@Component
public class QinCeAttendanceXxlJob {
@Autowired
private IQinceUserStatisticService qinceUserStatisticService;
/**
* 勤策考勤数据同步任务(XXL-JOB核心方法)
* 调度配置:每天23:00执行(Cron表达式:0 0 23 * * ?)
*/
@XxlJob("qinCeAttendanceSyncTodayJob")
public void qinCeAttendanceSyncTodayJob() {
XxlJobHelper.log("===== 开始执行勤策考勤数据同步任务(同步当天数据) =====");
try {
// 调用已实现的同步当天考勤数据方法
int syncCount = qinceUserStatisticService.queryAndSaveTodayAttendance();
// 任务结果日志
XxlJobHelper.log("===== 勤策考勤数据同步任务执行完成,总计同步/更新{}条记录 =====", syncCount);
// 标记任务执行成功(XXL-JOB状态回调)
XxlJobHelper.handleSuccess("勤策考勤数据同步成功,总计同步/更新" + syncCount + "条记录");
} catch (Exception e) {
log.error("===== 勤策考勤数据同步任务执行失败 =====", e);
XxlJobHelper.log("===== 勤策考勤数据同步任务执行失败,异常信息:{} =====", e.getMessage());
XxlJobHelper.handleFail("勤策考勤数据同步失败,异常信息:" + e.getMessage());
}
}
/**
* 勤策考勤数据同步任务(XXL-JOB核心方法)
* 调度配置:每天00:02执行(Cron表达式:0 2 0 * * ?)
*/
@XxlJob("qinCeAttendanceSyncYesterdayJob")
public void qinCeAttendanceSyncYesterdayJob() {
XxlJobHelper.log("===== 开始执行勤策考勤数据同步任务(同步前一天数据) =====");
try {
// 调用已实现的同步前一天考勤数据方法
int syncCount = qinceUserStatisticService.queryAndSaveYesterdayAttendance();
// 任务结果日志
XxlJobHelper.log("===== 勤策考勤数据同步任务执行完成,总计同步/更新{}条记录 =====", syncCount);
// 标记任务执行成功(XXL-JOB状态回调)
XxlJobHelper.handleSuccess("勤策考勤数据同步成功,总计同步/更新" + syncCount + "条记录");
} catch (Exception e) {
// 异常处理与日志记录
log.error("===== 勤策考勤数据同步任务执行失败 =====", e);
XxlJobHelper.log("===== 勤策考勤数据同步任务执行失败,异常信息:{} =====", e.getMessage());
// 标记任务执行失败(XXL-JOB状态回调)
XxlJobHelper.handleFail("勤策考勤数据同步失败,异常信息:" + e.getMessage());
}
}
}
\ No newline at end of file
<?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.sfa.job.domain.feishu.mapper.FeishuLeaveInfoMapper">
<!-- 基础结果集映射:匹配实体类字段(驼峰)与数据库表字段(下划线) -->
<resultMap id="BaseResultMap" type="com.sfa.job.domain.feishu.entity.FeishuLeaveInfo">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="employment_id" property="employmentId" jdbcType="VARCHAR"/>
<result column="employment_name" property="employmentName" jdbcType="VARCHAR"/>
<result column="employment_no" property="employmentNo" jdbcType="VARCHAR"/>
<result column="end_time" property="endTime" jdbcType="VARCHAR" />
<result column="grant_source" property="grantSource" jdbcType="VARCHAR"/>
<result column="leave_duration" property="leaveDuration" jdbcType="VARCHAR"/>
<result column="leave_duration_unit" property="leaveDurationUnit" jdbcType="INTEGER"/>
<result column="leave_process_id" property="leaveProcessId" jdbcType="VARCHAR" />
<result column="leave_request_id" property="leaveRequestId" jdbcType="VARCHAR" />
<result column="leave_request_status" property="leaveRequestStatus" jdbcType="INTEGER"/>
<result column="leave_type_id" property="leaveTypeId" jdbcType="VARCHAR" />
<result column="leave_type_name" property="leaveTypeName" jdbcType="VARCHAR" />
<result column="notes" property="notes" jdbcType="VARCHAR" />
<result column="return_time" property="returnTime" jdbcType="VARCHAR" />
<result column="start_time" property="startTime" jdbcType="VARCHAR" />
<result column="submitted_at" property="submittedAt" jdbcType="VARCHAR" />
<result column="submitted_by" property="submittedBy" jdbcType="VARCHAR" />
<result column="time_zone" property="timeZone" jdbcType="VARCHAR" />
<result column="leave_correct_process_id" property="leaveCorrectProcessId" jdbcType="VARCHAR" />
<result column="process_apply_time" property="processApplyTime" jdbcType="VARCHAR" />
<result column="process_id" property="processId" jdbcType="VARCHAR" />
<result column="process_status" property="processStatus" jdbcType="VARCHAR" />
<result column="sync_create_time" property="syncCreateTime" jdbcType="TIMESTAMP" />
<result column="sync_update_time" property="syncUpdateTime" jdbcType="TIMESTAMP" />
<result column="is_delete" property="isDelete" jdbcType="TINYINT" />
</resultMap>
<sql id="Base_Column_List">
employment_id, employment_name, employment_no, end_time, grant_source,
leave_duration, leave_duration_unit, leave_process_id, leave_request_id,
leave_request_status, leave_type_id, leave_type_name, notes, return_time,
start_time, submitted_at, submitted_by, time_zone, leave_correct_process_id,
process_apply_time, process_id, process_status, sync_create_time, sync_update_time,
is_delete
</sql>
</mapper>
\ No newline at end of file
<?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.sfa.job.domain.qince.mapper.QinceUserStatisticMapper">
<resultMap id="BaseResultMap" type="com.sfa.job.domain.qince.entity.QinceUserStatistic">
<id property="id" column="id" />
<result property="deptName" column="dept_name" />
<result property="qcUserId" column="qc_user_id" />
<result property="sex" column="sex" />
<result property="userName" column="user_name" />
<result property="group" column="group" />
<result property="employeeCode" column="employee_code" />
<result property="checkInAttdLieLocate" column="check_in_attd_lie_locate" />
<result property="checkInAttdAddress" column="check_in_attd_address" />
<result property="checkInAttdLcError" column="check_in_attd_lc_error" />
<result property="checkInAttdStatus" column="check_in_attd_status" />
<result property="checkInAttdTime" column="check_in_attd_time" />
<result property="attDate" column="att_date" />
<result property="workTime" column="work_time" />
<result property="remarks" column="remarks" />
<result property="checkOutAttdLieLocate" column="check_out_attd_lie_locate" />
<result property="checkOutAttdAddress" column="check_out_attd_address" />
<result property="checkOutAttdLcError" column="check_out_attd_lc_error" />
<result property="checkOutAttdStatus" column="check_out_attd_status" />
<result property="checkOutAttdTime" column="check_out_attd_time" />
<result property="isDelete" column="is_delete" />
<result property="createTime" column="create_time" />
<result property="modifyTime" column="modify_time" />
</resultMap>
<sql id="Base_Column_List">
id,dept_name,qc_user_id,sex,user_name,group,
employee_code,check_in_attd_lie_locate,check_in_attd_address,check_in_attd_lc_error,check_in_attd_status,
check_in_attd_time,att_date,work_time,remarks,check_out_attd_lie_locate,
check_out_attd_address,check_out_attd_lc_error,check_out_attd_status,check_out_attd_time,is_delete,
create_time,modify_time
</sql>
</mapper>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论