农民工用工管理台账定时任务

This commit is contained in:
lSun 2025-08-12 13:39:47 +08:00
parent ccfc0e2925
commit 2d23f61feb
7 changed files with 642 additions and 5 deletions

View File

@ -0,0 +1,60 @@
package com.bonus.ahsbs.base.dao;
import com.bonus.ahsbs.base.entity.AttLedgerBean;
import com.bonus.ahsbs.base.entity.XbgAttReportData;
import com.bonus.ahsbs.base.entity.XbgAttReportDataDetail;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* packageName com.bonus.ahsbs.base.dao
*
* @author lsun
* @version 1.0.0
* @className AttLedgerDao (此处以class为例)
* @date 2025/8/8
* @description 农民工用工管理台账数据访问层
*/
@Repository(value = "AttLedgerDao")
public interface AttLedgerDao {
/**
* 获取所有项目下的人员信息
* @return
*/
List<AttLedgerBean> getPersonesByPro();
/**
* 获取考勤信息
* @return
*/
List<AttLedgerBean> getAttendanceInfo();
/**
* 批量插入xbg_att_report_data表数据
* @param reportDataList
*/
void batchInsertXbgAttReportData(List<XbgAttReportData> reportDataList);
/**
* 批量插入xbg_att_report_data_detail表数据
* @param detailList
*/
void batchInsertXbgAttReportDataDetail(List<XbgAttReportDataDetail> detailList);
/**
* 获取工程的出入场时间
* @return
*/
List<AttLedgerBean> getProjectTime();
/**
* 删除xbg_att_report_data表数据
*/
void deleteXbgAttReportData();
/**
* 删除xbg_att_report_data_detail表数据
*/
void deleteXbgAttReportDataDetail();
}

View File

@ -0,0 +1,58 @@
package com.bonus.ahsbs.base.entity;
import lombok.Data;
/**
* packageName com.bonus.ahsbs.base.entity
*
* @author lsun
* @version 1.0.0
* @className AttLedgerBean (此处以class为例)
* @date 2025/8/8
* @description 农民工用工管理台账
*/
@Data
public class AttLedgerBean {
private String proName;//工程名称
private String proType;//工程类型
private String dydj;//电压等级
private String proState;//工程状态
private String personName;//姓名
private String sex;//性别
private String teamName;//所属班组
private String postName;//岗位
private String idNumber;//身份证ID
private String subId;
private String subName;//分包单位
private String contactInfo;//联系方式
private String proId;
private String teamId;
private String nation;//民族
private String age;//年龄
private String enDate;//进场时间
private String exDate;//出场时间
private String checkNum;//出勤天数 字符串格式","隔开
private String contract;//合同约定工价
private String birthDate;
private String contractNum;//合同数
private String contractId;//合同Id
private String contractPath;//合同路径
private String einTime;//入场时间3
private String exitTime;//出场时间
private String startTime;//开始时间
private String endTime;//结果时间
private String postId; // 工种/岗位ID
private String month;//月份
private String achievementsMoney;//绩效范围
private String fristTime;//第一次人员入场时间
private String lastTime;//工程完工时间
private String currentDay;//考勤时间
}

View File

@ -0,0 +1,34 @@
package com.bonus.ahsbs.base.entity;
import lombok.Data;
/**
* packageName com.bonus.ahsbs.base.entity
*
* @author lsun
* @version 1.0.0
* @className XbgAttReportData (此处以class为例)
* @date 2025/8/8
* @description 考勤报表主表存储员工基本信息与组织归属
*/
@Data
public class XbgAttReportData {
private String id; // 主键ID
private String name; // 姓名
private String idCard; // 身份证号
private String gender; // 性别
private String ethnicity; // 民族
private String age; // 年龄
private String contactInfo; // 联系方式
private String proId; // 工程项目ID关联工程项目表
private String subId; // 分包单位ID
private String teamId; // 所在班组ID
private String postId; // 工种/岗位ID
private String contractId; // 合同ID
private String entryDate; // 进场日期
private String exitDate; // 出场日期
private String basicSalary; // 基本工资
private String performanceSalary; // 绩效工资
private String createTime; // 创建时间
private String updateTime; // 更新时间
}

View File

@ -0,0 +1,22 @@
package com.bonus.ahsbs.base.entity;
import lombok.Data;
/**
* packageName com.bonus.ahsbs.base.entity
*
* @author lsun
* @version 1.0.0
* @className XbgAttReportDataDetail (此处以class为例)
* @date 2025/8/8
* @description 考勤月度明细表存储每个员工每月的出勤天数
*/
@Data
public class XbgAttReportDataDetail {
private String id; // 主键ID
private String reportDataId; // 关联 xbg_att_report_data 的ID
private String yearMonth; // 年月
private String attendanceDays; // 出勤天数
private String createTime; // 创建时间
private String updateTime; // 更新时间
}

View File

@ -0,0 +1,294 @@
package com.bonus.ahsbs.base.task;
import com.bonus.ahsbs.base.dao.AttLedgerDao;
import com.bonus.ahsbs.base.entity.AttLedgerBean;
import com.bonus.ahsbs.base.entity.XbgAttReportData;
import com.bonus.ahsbs.base.entity.XbgAttReportDataDetail;
import com.bonus.ahsbs.core.util.DateTimeHelper;
import com.bonus.ahsbs.base.util.IDUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* packageName com.bonus.ahsbs.base.task
*
* @author lsun
* @version 1.0.0
* @className TrainingTask (此处以class为例)
* @date 2025/8/8
* @description 农民工用工管理台账
*/
@Component
@Slf4j
@EnableScheduling
@EnableAsync
public class AttLedgerTask {
@Resource(name = "AttLedgerDao")
private AttLedgerDao dao;
@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
// @Scheduled(cron = "0 */3 * * * ?") // 每3秒执行一次
public void add() {
log.info("农民工用工管理台账定时任务开始执行");
try {
// Step 1: 获取基本信息
List<AttLedgerBean> basicInfoList = dao.getPersonesByPro();
// Step 2: 获取工程的出入场时间
// List<AttLedgerBean> projectTimeList = dao.getProjectTime();
// Step 3: 获取考勤信息
List<AttLedgerBean> attendanceInfoList = dao.getAttendanceInfo();
// Step 3: 合并基本信息和考勤信息
List<AttLedgerBean> mergedList = mergeInfo(basicInfoList, attendanceInfoList);
// Step 4: 插入 xbg_att_report_data
List<XbgAttReportData> insertedReportDataList = insertXbgAttReportData(mergedList);
// Step 5: 插入 xbg_att_report_data_detail
insertXbgAttReportDataDetail(mergedList, insertedReportDataList);
} catch (Exception e) {
log.error(e.toString(), e);
}
log.info("农民工用工管理台账定时任务结束执行");
}
private List<AttLedgerBean> mergeInfo(List<AttLedgerBean> basicInfoList, List<AttLedgerBean> attendanceInfoList) {
// 1. 将考勤信息按身份证号+工程ID+分包id+班组id+岗位id分组方便后续查找
Map<String, List<AttLedgerBean>> attendanceGroupByKey = attendanceInfoList.stream()
.collect(Collectors.groupingBy(bean ->
bean.getIdNumber() + "_" + bean.getProId() + "_" +
bean.getSubId() + "_" + bean.getTeamId() + "_" + bean.getPostId()));
// 2. 处理每条基本信息记录
for (AttLedgerBean basicInfo : basicInfoList) {
// 构造分组key用于查找考勤信息
String key = basicInfo.getIdNumber() + "_" + basicInfo.getProId() + "_" +
basicInfo.getSubId() + "_" + basicInfo.getTeamId() + "_" + basicInfo.getPostId();
List<AttLedgerBean> attendanceList = attendanceGroupByKey.get(key);
if (attendanceList != null && !attendanceList.isEmpty()) {
// 按月份统计考勤天数
Map<String, Integer> attendanceByMonth = new HashMap<>();
for (AttLedgerBean attendance : attendanceList) {
// 从create_date中提取年月
String currentDate = attendance.getCurrentDay();
if (currentDate != null && currentDate.length() >= 7) {
// 提取年月部分 YYYY-MM
String yearMonth = currentDate.substring(0, 7);
// 累加该月份的考勤天数
attendanceByMonth.merge(yearMonth, 1, Integer::sum);
}
}
// 构建考勤天数和月份字符串
StringBuilder checkNumBuilder = new StringBuilder();
StringBuilder monthBuilder = new StringBuilder();
int count = 0;
for (Map.Entry<String, Integer> monthEntry : attendanceByMonth.entrySet()) {
if (count > 0) {
checkNumBuilder.append(",");
monthBuilder.append(",");
}
monthBuilder.append(monthEntry.getKey());
checkNumBuilder.append(monthEntry.getValue());
count++;
}
basicInfo.setCheckNum(checkNumBuilder.toString());
basicInfo.setMonth(monthBuilder.toString());
// 更新工程ID等信息如果还没有设置
if (basicInfo.getProId() == null && attendanceList.size() > 0) {
AttLedgerBean attendance = attendanceList.get(0);
basicInfo.setProId(attendance.getProId());
basicInfo.setSubId(attendance.getSubId());
basicInfo.setTeamId(attendance.getTeamId());
basicInfo.setPostId(attendance.getPostId());
basicInfo.setContractId(attendance.getContractId());
}
}
}
// 3. 更新工程的入场和出场时间
for (AttLedgerBean basicInfo : basicInfoList) {
// 设置入场和出场日期字段用于报表主表
basicInfo.setEnDate(basicInfo.getEinTime() != null ? basicInfo.getEinTime().substring(0, 10) : null);
basicInfo.setExDate(basicInfo.getExitTime() != null ? basicInfo.getExitTime().substring(0, 10) : null);
}
return basicInfoList;
}
private List<XbgAttReportData> insertXbgAttReportData(List<AttLedgerBean> mergedList) {
List<XbgAttReportData> reportDataList = new ArrayList<>();
// 先为每个报告数据生成唯一ID
Map<AttLedgerBean, String> beanToIdMap = new HashMap<>();
for (AttLedgerBean bean : mergedList) {
XbgAttReportData reportData = new XbgAttReportData();
String id = IDUtils.createID();
reportData.setId(id);
beanToIdMap.put(bean, id);
reportData.setName(bean.getPersonName());
reportData.setIdCard(bean.getIdNumber());
reportData.setGender(bean.getSex());
reportData.setEthnicity(bean.getNation());
reportData.setAge(calculateAge(bean.getBirthDate())); // 使用计算的年龄
reportData.setContactInfo(bean.getContactInfo());
reportData.setProId(bean.getProId());
reportData.setSubId(bean.getSubId());
reportData.setTeamId(bean.getTeamId());
reportData.setPostId(bean.getPostId());
reportData.setContractId(bean.getContractId());
reportData.setEntryDate(bean.getEnDate());
reportData.setExitDate(bean.getExDate());
reportData.setBasicSalary(getBasicSalary(bean));
reportData.setPerformanceSalary(getPerformanceSalary(bean));
reportData.setCreateTime(DateTimeHelper.getNowDate());
reportData.setUpdateTime(DateTimeHelper.getNowDate());
reportDataList.add(reportData);
}
// 插入之前删除之前的数据
dao.deleteXbgAttReportData();
// 批量插入 xbg_att_report_data
dao.batchInsertXbgAttReportData(reportDataList);
return reportDataList;
}
/**
* 根据出生日期计算年龄
* @param birthDate 出生日期格式为 "yyyy年M月d日" "yyyy年M月d" "yyyy-MM-dd"
* @return 年龄字符串
*/
private String calculateAge(String birthDate) {
if (birthDate == null || birthDate.trim().isEmpty()) {
return null;
}
try {
LocalDate birthLocalDate;
// 尝试解析"yyyy年M月d日""yyyy年M月d"格式
if (birthDate.contains("") && birthDate.contains("")) {
// 检查是否包含""
String pattern = birthDate.contains("") ? "yyyy年M月d日" : "yyyy年M月d";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
birthLocalDate = LocalDate.parse(birthDate, formatter);
} else {
// 解析"yyyy-MM-dd"格式
birthLocalDate = LocalDate.parse(birthDate);
}
// 获取当前日期
LocalDate now = LocalDate.now();
// 计算年龄
int age = Period.between(birthLocalDate, now).getYears();
return String.valueOf(age);
} catch (Exception e) {
// 如果解析失败返回null
return null;
}
}
private void insertXbgAttReportDataDetail(List<AttLedgerBean> mergedList, List<XbgAttReportData> insertedReportDataList) {
// 创建从身份证号+工程ID+分包id+班组id+岗位id到reportData的映射处理重复键的情况
Map<String, XbgAttReportData> keyToReportDataMap = insertedReportDataList.stream()
.collect(Collectors.toMap(
reportData -> reportData.getIdCard() + "_" + reportData.getProId() + "_" +
reportData.getSubId() + "_" + reportData.getTeamId() + "_" + reportData.getPostId(),
Function.identity(),
(existing, replacement) -> existing)); // 当出现重复键时保留第一个值
List<XbgAttReportDataDetail> detailList = new ArrayList<>();
for (AttLedgerBean bean : mergedList) {
// 根据身份证号+工程ID+分包id+班组id+岗位id获取对应的reportData
String key = bean.getIdNumber() + "_" + bean.getProId() + "_" +
bean.getSubId() + "_" + bean.getTeamId() + "_" + bean.getPostId();
XbgAttReportData reportData = keyToReportDataMap.get(key);
if (reportData == null) {
continue; // 如果没有对应的主表记录则跳过
}
if (bean.getCheckNum() != null && !bean.getCheckNum().isEmpty()
&& bean.getMonth() != null && !bean.getMonth().isEmpty()) {
String[] checkNums = bean.getCheckNum().split(",");
String[] months = bean.getMonth().split(",");
// 确保考勤天数和月份数组长度一致
int length = Math.min(checkNums.length, months.length);
for (int i = 0; i < length; i++) {
XbgAttReportDataDetail detail = new XbgAttReportDataDetail();
detail.setReportDataId(reportData.getId()); // 使用正确的ID
detail.setYearMonth(months[i]);
detail.setAttendanceDays(checkNums[i]);
detail.setCreateTime(DateTimeHelper.getNowDate());
detail.setUpdateTime(DateTimeHelper.getNowDate());
detailList.add(detail);
}
}
}
// 插入之前删除之前数据
dao.deleteXbgAttReportDataDetail();
// 批量插入 xbg_att_report_data_detail
if (!detailList.isEmpty()) {
dao.batchInsertXbgAttReportDataDetail(detailList);
}
}
private String getBasicSalary(AttLedgerBean bean) {
// 自定义方法获取基本工资
if (bean.getContract() != null && !bean.getContract().isEmpty()) {
String[] contractParts = bean.getContract().split(",");
if (contractParts.length > 0) {
return contractParts[0];
}
}
return "0";
}
private String getPerformanceSalary(AttLedgerBean bean) {
// 自定义方法获取绩效工资
if (bean.getContract() != null && !bean.getContract().isEmpty()) {
String[] contractParts = bean.getContract().split(",");
if (contractParts.length > 1) {
return contractParts[1];
}
}
return "0";
}
private Long getReportDataId(AttLedgerBean bean) {
// 自定义方法获取 report_data_id这里只是一个示例实现
return System.currentTimeMillis(); // 实际应根据业务逻辑获取正确的ID
}
private String getYearMonth(int index) {
// 自定义方法获取年月这里只是一个示例实现
LocalDate date = LocalDate.now().minusMonths(index);
return date.format(DateTimeFormatter.ofPattern("yyyy-MM")); // 实际应根据业务逻辑获取正确的年月
}
}

View File

@ -7,6 +7,8 @@ package com.bonus.ahsbs.base.util;
*/
public class IDUtils {
private static byte[] lock = new byte[0];
private static long lastTimestamp = 0;
private static long sequence = 0;
/**位数默认是8位*/
private final static long w = 100000000;
@ -14,10 +16,34 @@ public class IDUtils {
public static String createID() {
long r = 0;
synchronized (lock) {
r = (long) ((Math.random() + 1) * w);
long timestamp = System.currentTimeMillis();
// 如果同一毫秒内生成多个ID增加序列号
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) % 1000;
// 如果序列号达到上限则等待下一毫秒
if (sequence == 0) {
timestamp = waitNextMillis(timestamp);
}
} else {
sequence = 0;
}
lastTimestamp = timestamp;
// 使用时间戳加上序列号生成唯一ID
return String.valueOf(timestamp * 1000 + sequence);
}
return System.currentTimeMillis() + String.valueOf(r).substring(1);
}
}
/**
* 等待下一毫秒
* @param lastTimestamp 上一毫秒时间戳
* @return 下一毫秒时间戳
*/
private static long waitNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}

View File

@ -0,0 +1,143 @@
<?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.bonus.ahsbs.base.dao.AttLedgerDao">
<update id="deleteXbgAttReportData">
update xbg_att_report_data
set is_active = 0
</update>
<update id="deleteXbgAttReportDataDetail">
update xbg_att_report_data_detail
set is_active = 0
</update>
<select id="getPersonesByPro" resultType="com.bonus.ahsbs.base.entity.AttLedgerBean">
SELECT bp.id AS proId,
bwah.ID_NUMBER as idNumber,
bwah.SUB_ID as subId,
bwah.team_id as teamId,
bwah.EXIT_TIME as exitTime,
bwah.EIN_TIME as einTime,
bw.NAME AS personName,
bw.SEX,
bw.ethnic AS nation,
bw.phone AS contactInfo,
sub.SUB_NAME AS subname,
bt.NAME AS teamName,
sdd.`name` AS postname,
DATE_FORMAT(bwah.EIN_TIME, '%Y-%m-%d') AS enDate,
DATE_FORMAT(bwah.EXIT_TIME, '%Y-%m-%d') AS exdate,
bwc.wageCRITERION AS contract,
concat(bwc.min_achievements_money, '-', bwc.max_achievements_money) AS achievementsMoney,
bw.birthday AS birthDate,
bwah.POST_ID as postId,
bwc.id as contractId
FROM (SELECT * FROM bm_project WHERE is_active = '1') bp
LEFT JOIN (SELECT * FROM bm_worker_attend_history ORDER BY EIN_TIME DESC) bwah
ON bp.id = bwah.PROJECT_ID
AND bwah.is_active = '1'
LEFT JOIN bm_worker bw ON bwah.ID_NUMBER = bw.ID_NUMBER
LEFT JOIN bm_subcontractor sub ON bwah.SUB_ID = sub.id
LEFT JOIN bm_sub_team bt ON bwah.TEAM_ID = bt.id
LEFT JOIN sys_dic_detail sdd ON bwah.POST_ID = sdd.id
LEFT JOIN (SELECT *
FROM (SELECT * FROM bm_worker_contract WHERE is_active = '1' ORDER BY upload_time DESC) aa
GROUP BY idcard) bwc ON bwah.id_number = bwc.idcard
WHERE bp.is_active = '1'
AND bw.IS_ACTIVE = '1'
GROUP BY bwah.PROJECT_ID,
bwah.SUB_ID,
bwah.TEAM_ID,
bwah.EXIT_TIME,
bwah.ID_NUMBER
</select>
<select id="getAttendanceInfo" resultType="com.bonus.ahsbs.base.entity.AttLedgerBean">
select id,
id_card AS idNumber,
pro_id as proId,
sub_id AS subId,
team_id AS teamId,
post_id AS postId,
create_date AS currentDay,
contract_id AS contractId
from xbg_user_attendance
where audit_status IN ('0', '1', '2')
and data_type in ('1', '2', '3')
</select>
<select id="getProjectTime" resultType="com.bonus.ahsbs.base.entity.AttLedgerBean">
SELECT bp.ID as proId,
min(bwr.EIN_TIME) as einTime,
min(bwr.EIN_TIME) as fristTime,
(case bp.`status` when '完工' then max(bwr.EXIT_TIME) else curdate() END) as exitTime,
(case bp.`status` when '完工' then max(bwr.EXIT_TIME) else '未完工' END) as lastTime
FROM bm_worker_attend_history bwah
LEFT JOIN bm_project bp ON bp.ID = bwah.PROJECT_ID
LEFT JOIN bm_worker_record bwr ON bwah.ID_NUMBER = bwr.ID_NUMBER
LEFT JOIN bm_sub_team bt on bwr.TEAM_ID = bt.id
LEFT JOIN bm_subcontractor sub on bwah.sub_id = sub.id
LEFT JOIN bm_worker bw on bwr.ID_NUMBER = bw.ID_NUMBER
WHERE bwr.IS_ACTIVE = '1'
and bwah.is_active = '1'
and bp.IS_ACTIVE = '1'
GROUP BY bp.ID
</select>
<insert id="batchInsertXbgAttReportData">
INSERT INTO xbg_att_report_data (
id,
name,
id_card,
gender,
ethnicity,
age,
contact_info,
pro_id,
sub_id,
team_id,
post_id,
contract_id,
entry_date,
exit_date,
basic_salary,
performance_salary
) VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.id},
#{item.name},
#{item.idCard},
#{item.gender},
#{item.ethnicity},
#{item.age},
#{item.contactInfo},
#{item.proId},
#{item.subId},
#{item.teamId},
#{item.postId},
#{item.contractId},
#{item.entryDate},
#{item.exitDate},
#{item.basicSalary},
#{item.performanceSalary}
)
</foreach>
</insert>
<insert id="batchInsertXbgAttReportDataDetail">
INSERT INTO xbg_att_report_data_detail (
report_data_id,
`year_month`,
attendance_days
) VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.reportDataId},
#{item.yearMonth},
#{item.attendanceDays}
)
</foreach>
</insert>
</mapper>