Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
W
wangxiaolu-sfa-module-job
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
sfa
wangxiaolu-sfa-module-job
Commits
59f20c9e
提交
59f20c9e
authored
2月 04, 2026
作者:
窦馨雨
浏览文件
操作
浏览文件
下载
差异文件
合并分支 'dxy' 到 'qa'
新增勤策飞书考勤明细同步 查看合并请求
!124
上级
6dd435e7
84fe610e
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
19 个修改的文件
包含
988 行增加
和
1 行删除
+988
-1
FeishuLeaveInfoDao.java
...ava/com/sfa/job/domain/feishu/dao/FeishuLeaveInfoDao.java
+26
-0
FeishuLeaveInfoDaoImpl.java
...com/sfa/job/domain/feishu/dao/FeishuLeaveInfoDaoImpl.java
+0
-0
FeishuLeaveInfo.java
...ava/com/sfa/job/domain/feishu/entity/FeishuLeaveInfo.java
+55
-0
FeishuLeaveInfoMapper.java
...m/sfa/job/domain/feishu/mapper/FeishuLeaveInfoMapper.java
+11
-0
IQinceUserStatisticDao.java
.../com/sfa/job/domain/qince/dao/IQinceUserStatisticDao.java
+38
-0
QinceUserStatisticDaoImpl.java
...m/sfa/job/domain/qince/dao/QinceUserStatisticDaoImpl.java
+171
-0
QinceUserStatistic.java
...a/com/sfa/job/domain/qince/entity/QinceUserStatistic.java
+152
-0
QinceUserStatisticMapper.java
...sfa/job/domain/qince/mapper/QinceUserStatisticMapper.java
+18
-0
FeishuLeaveInfoDTO.java
.../com/sfa/job/pojo/feishu/response/FeishuLeaveInfoDTO.java
+46
-0
QinceUserStatisticDTO.java
...om/sfa/job/pojo/qince/response/QinceUserStatisticDTO.java
+140
-0
FeishuLeaveInfoServiceImpl.java
...om/sfa/job/service/feishu/FeishuLeaveInfoServiceImpl.java
+31
-0
IFeishuLeaveInfoService.java
...a/com/sfa/job/service/feishu/IFeishuLeaveInfoService.java
+19
-0
IQinceUserStatisticService.java
...com/sfa/job/service/qince/IQinceUserStatisticService.java
+28
-0
QinceUserStatisticServiceImpl.java
...job/service/qince/impl/QinceUserStatisticServiceImpl.java
+0
-0
QinCeUtils.java
src/main/java/com/sfa/job/util/QinCeUtils.java
+42
-1
FeiShuLeaveXxlJob.java
...ain/java/com/sfa/job/xxljob/feishu/FeiShuLeaveXxlJob.java
+58
-0
QinCeAttendanceXxlJob.java
.../java/com/sfa/job/xxljob/qince/QinCeAttendanceXxlJob.java
+68
-0
FeishuLeaveInfoMapper.xml
src/main/resources/mapper/feishu/FeishuLeaveInfoMapper.xml
+45
-0
QinceUserStatisticMapper.xml
src/main/resources/mapper/qince/QinceUserStatisticMapper.xml
+40
-0
没有找到文件。
src/main/java/com/sfa/job/domain/feishu/dao/FeishuLeaveInfoDao.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/java/com/sfa/job/domain/feishu/dao/FeishuLeaveInfoDaoImpl.java
0 → 100644
浏览文件 @
59f20c9e
差异被折叠。
点击展开。
src/main/java/com/sfa/job/domain/feishu/entity/FeishuLeaveInfo.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/java/com/sfa/job/domain/feishu/mapper/FeishuLeaveInfoMapper.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/java/com/sfa/job/domain/qince/dao/IQinceUserStatisticDao.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/java/com/sfa/job/domain/qince/dao/QinceUserStatisticDaoImpl.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/java/com/sfa/job/domain/qince/entity/QinceUserStatistic.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/java/com/sfa/job/domain/qince/mapper/QinceUserStatisticMapper.java
0 → 100644
浏览文件 @
59f20c9e
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
>
{
}
src/main/java/com/sfa/job/pojo/feishu/response/FeishuLeaveInfoDTO.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/java/com/sfa/job/pojo/qince/response/QinceUserStatisticDTO.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/java/com/sfa/job/service/feishu/FeishuLeaveInfoServiceImpl.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/java/com/sfa/job/service/feishu/IFeishuLeaveInfoService.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/java/com/sfa/job/service/qince/IQinceUserStatisticService.java
0 → 100644
浏览文件 @
59f20c9e
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
;
}
src/main/java/com/sfa/job/service/qince/impl/QinceUserStatisticServiceImpl.java
0 → 100644
浏览文件 @
59f20c9e
差异被折叠。
点击展开。
src/main/java/com/sfa/job/util/QinCeUtils.java
浏览文件 @
59f20c9e
...
@@ -51,12 +51,14 @@ public class QinCeUtils {
...
@@ -51,12 +51,14 @@ public class QinCeUtils {
// 修改人员
// 修改人员
public
static
final
String
MODIFY_USER
=
"/api/employee/v3/modifyEmployee/"
;
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
QUERY_PRODUCT_INFO
=
"/api/product/v1/queryProduct/"
;
// 商品价格更新
// 商品价格更新
public
static
final
String
MODIFY_PRODUCT_PRICE
=
"/api/pd/v2/modifyProduct/"
;
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
)
{
public
String
builderUrl
(
String
sidepath
,
Map
<
String
,
Object
>
params
)
{
String
msgId
=
UUID
.
randomUUID
().
toString
();
String
msgId
=
UUID
.
randomUUID
().
toString
();
...
@@ -196,4 +198,43 @@ public class QinCeUtils {
...
@@ -196,4 +198,43 @@ public class QinCeUtils {
return
false
;
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
);
}
}
}
src/main/java/com/sfa/job/xxljob/feishu/FeiShuLeaveXxlJob.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/java/com/sfa/job/xxljob/qince/QinCeAttendanceXxlJob.java
0 → 100644
浏览文件 @
59f20c9e
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
src/main/resources/mapper/feishu/FeishuLeaveInfoMapper.xml
0 → 100644
浏览文件 @
59f20c9e
<?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
src/main/resources/mapper/qince/QinceUserStatisticMapper.xml
0 → 100644
浏览文件 @
59f20c9e
<?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
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论