三表一册数据来源

This commit is contained in:
方亮 2025-09-19 17:54:23 +08:00
parent 78b58fa7d6
commit b0a89422bb
6 changed files with 525 additions and 0 deletions

View File

@ -0,0 +1,25 @@
package com.bonus.job.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MapBeanVo {
private String key;
private String value;
private Integer id;
private String name;
public MapBeanVo(String key, String value){
this.value = value;
this.key = key;
}
}

View File

@ -0,0 +1,47 @@
package com.bonus.job.domain;
import lombok.Data;
@Data
public class ThreeTableOneRosterPo {
private Integer proId;
private String attMonth;
private Integer subId;
private String subName;
private Integer teamId;
private String teamName;
private Integer workerId;
private String name;
private String sex;
private Integer postId;
private String postName;
private String idNumber;
private String address;
private String phone;
private String proName;
//1.花名册
private String firstEinTime;
private String lastExitTime;
//2.农民工实名制工资信息报审表
private String bankName;
private String bankCardCode;
private String bankIdentifierCode;
private Integer contractId;
private Double priceWage;
private String payment;
private String payDay;
private String remark;
//3.考勤明细表
private String einDay;
private String attDay;
private String isRepair;
private String attendanceDay;
private Integer checkDay;
private Integer repairNum;
private Integer attendanceNum;
//4.工资明细表
private Double payMoney;
private Double deductMoney;
private Double actualMoney;
}

View File

@ -0,0 +1,29 @@
package com.bonus.job.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WorkerAttDayVo {
/**
* 日期
*/
private String day;
/**
* 是否在场
*/
private String isEin;
/**
* 是否打卡
*/
private String isAtt;
/**
* 是否为补卡
*/
private String isRepair;
}

View File

@ -1,7 +1,10 @@
package com.bonus.job.mapper;
import com.bonus.job.domain.BmWorkerBlackJob;
import com.bonus.job.domain.MapBeanVo;
import com.bonus.job.domain.PmWorkerJob;
import com.bonus.job.domain.ThreeTableOneRosterPo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -33,4 +36,45 @@ public interface WorkerJobMapper {
int insertWorkerBlack(List<BmWorkerBlackJob> list);
void updateWorkerEinDayRecordBlackStatus(List<BmWorkerBlackJob> list);
/**
* 查询所有有人在场的工程
* @param lastMonth
* @return
*/
List<MapBeanVo> getAllPro(String lastMonth);
/**
* 人员花名册
* @param attMonth
* @param startTime
* @param endTime
* @param proId
* @return
*/
List<ThreeTableOneRosterPo> getWorkerRosterData(@Param("attMonth") String attMonth, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("proId") Integer proId);
/**
* 农民工实名制工资信息报审表
* @param attMonth
* @param proId
* @return
*/
List<ThreeTableOneRosterPo> getWorkerApplyData(@Param("attMonth") String attMonth, @Param("proId") Integer proId);
/**
* 农民工实名制工资信息考勤表
* @param attMonth
* @param proId
* @return
*/
List<ThreeTableOneRosterPo> getWorkerAttData(@Param("attMonth") String attMonth, @Param("proId") Integer proId);
/**
* 农民工实名制工资信息支付表
* @param attMonth
* @param proId
* @return
*/
List<ThreeTableOneRosterPo> getWorkerPayData(@Param("attMonth") String attMonth, @Param("proId") Integer proId);
}

View File

@ -0,0 +1,239 @@
package com.bonus.job.task;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.bonus.job.domain.MapBeanVo;
import com.bonus.job.domain.ThreeTableOneRosterPo;
import com.bonus.job.domain.WorkerAttDayVo;
import com.bonus.job.mapper.WorkerJobMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* 三表一册定时任务调度
*
* @author bonus
*/
@Component("threeTableOneRosterTask")
public class ThreeTableOneRosterTask {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Resource
private WorkerJobMapper mapper;
/**
* 三表一册生成
* 每月1号生成上月数据
*/
public void createThreeTableOneRosterData(){
try{
// 获取当前年月
YearMonth current = YearMonth.now();
// 获取上一个月
YearMonth lastMonth = current.minusMonths(1);
// 本月第一天
LocalDate firstDayOfThisMonth = YearMonth.now().atDay(1);
// 上个月第一天先得到本月第一天再减一个月
LocalDate firstDayOfLastMonth = YearMonth.now().minusMonths(1).atDay(1);
String startTime = firstDayOfLastMonth.format(FORMATTER)+" 00:00:00";
String endTime = firstDayOfThisMonth.format(FORMATTER)+" 00:00:00";
//查询所有有人在场的工程
List<MapBeanVo> listPro = mapper.getAllPro(lastMonth.toString());
listPro.forEach(mapBeanVo -> {
//每个工程去循环生成
//1.花名册
List<ThreeTableOneRosterPo> listRoster = mapper.getWorkerRosterData(lastMonth.toString(),startTime,endTime,mapBeanVo.getId());
//2.农民工实名制工资信息报审表
List<ThreeTableOneRosterPo> listApply = mapper.getWorkerApplyData(lastMonth.toString(),mapBeanVo.getId());
//3.考勤明细表
List<ThreeTableOneRosterPo> listAtt = mapper.getWorkerAttData(lastMonth.toString(),mapBeanVo.getId());
List<ThreeTableOneRosterPo> listAttDeal = dealAttData(listAtt,lastMonth);
//4.农民工资支付表
// 数据串联生成工资支付数据
// 通过workerId,proId将listApplylistAttDeal数据填充到listRoster
// 先将listApply按workerId和proId分组
Map<String, ThreeTableOneRosterPo> applyMap = listApply.stream()
.collect(Collectors.toMap(
item -> item.getWorkerId() + "_" + item.getProId(),
item -> item,
(existing, replacement) -> existing
));
// 将listAttDeal按workerId和proId分组
Map<String, ThreeTableOneRosterPo> attDealMap = listAttDeal.stream()
.collect(Collectors.toMap(
item -> item.getWorkerId() + "_" + item.getProId(),
item -> item,
(existing, replacement) -> existing
));
// 遍历listRoster填充数据
listRoster.forEach(roster -> {
String key = roster.getWorkerId() + "_" + roster.getProId();
// 从listApply中查找匹配的数据
ThreeTableOneRosterPo applyData = applyMap.get(key);
if (applyData != null) {
// 填充apply相关字段
roster.setBankName(applyData.getBankName());
roster.setBankCardCode(applyData.getBankCardCode());
roster.setBankIdentifierCode(applyData.getBankIdentifierCode());
roster.setContractId(applyData.getContractId());
roster.setPriceWage(applyData.getPriceWage());
roster.setPayment(applyData.getPayment());
}
// 从listAttDeal中查找匹配的数据
ThreeTableOneRosterPo attDealData = attDealMap.get(key);
if (attDealData != null) {
// 填充attDeal相关字段
roster.setAttendanceDay(attDealData.getAttendanceDay());
roster.setAttendanceNum(attDealData.getAttendanceNum());
roster.setRepairNum(attDealData.getRepairNum());
roster.setCheckDay(attDealData.getCheckDay());
// 根据实际需要填充其他字段
}
// 计算工资使用精度计算如果有null返回0
double calculatePayMoney = Optional.ofNullable(roster.getPriceWage()).orElse(0.0) *
Optional.ofNullable(roster.getAttendanceNum()).orElse(0);
roster.setPayMoney(calculatePayMoney);
roster.setDeductMoney(0.0);
roster.setActualMoney(calculatePayMoney);
});
});
//1.花名册
}catch (Exception e){
logger.error("人员入场更新表失败,{}",e.getMessage());
}
}
private List<ThreeTableOneRosterPo> dealAttData(List<ThreeTableOneRosterPo> listAtt, YearMonth lastMonth) {
// 按照 workerId 分组
Map<Integer, List<ThreeTableOneRosterPo>> groupedByWorkerId = listAtt.stream()
.collect(Collectors.groupingBy(ThreeTableOneRosterPo::getWorkerId));
// 获取上个月的所有日期
List<MapBeanVo> daysOfMonth = generateDaysOfMonth(lastMonth);
// 处理每个员工的考勤数据
// 处理每个员工的考勤数据
// 获取第一条数据作为基础数据
// 处理考勤日数据
// 将处理后的考勤日数据设置到基础数据中
return groupedByWorkerId.entrySet().stream()
.map(entry -> {
Integer workerId = entry.getKey();
List<ThreeTableOneRosterPo> workerAttendance = entry.getValue();
// 获取第一条数据作为基础数据
ThreeTableOneRosterPo baseData = workerAttendance.get(0);
// 处理考勤日数据
List<WorkerAttDayVo> attendanceDays = processWorkerAttendance(workerId, workerAttendance, daysOfMonth);
//计算出勤天数
int attNum = 0;
int repairNum = 0;
for (WorkerAttDayVo attendanceDay : attendanceDays) {
if("1".equals(attendanceDay.getIsAtt())){
attNum++;
if("1".equals(attendanceDay.getIsRepair())){
repairNum++;
}
}
}
baseData.setAttendanceNum(attNum);
baseData.setRepairNum(repairNum);
baseData.setCheckDay(attNum-repairNum);
JSONArray o = (JSONArray)JSON.toJSON(attendanceDays);
// 将处理后的考勤日数据设置到基础数据中
baseData.setAttendanceDay(JSON.toJSONString(o));
return baseData;
})
.collect(Collectors.toList());
}
/**
* 生成指定年月的所有日期列表
* @param yearMonth 指定的年月
* @return 日期列表包含 yyyy-MM-dd dd 两个字段的 MapBeanVo
*/
private List<MapBeanVo> generateDaysOfMonth(YearMonth yearMonth) {
List<MapBeanVo> days = new ArrayList<>();
LocalDate firstDay = yearMonth.atDay(1);
LocalDate lastDay = yearMonth.atEndOfMonth();
LocalDate currentDate = firstDay;
while (!currentDate.isAfter(lastDay)) {
MapBeanVo dayInfo = new MapBeanVo();
dayInfo.setValue(currentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
dayInfo.setKey(String.format("%02d", currentDate.getDayOfMonth()));
days.add(dayInfo);
currentDate = currentDate.plusDays(1);
}
return days;
}
/**
* 处理单个员工的考勤数据
* @param workerId 员工ID
* @param attendanceRecords 该员工的考勤记录
* @param daysOfMonth 月份中的所有日期
* @return 处理后的考勤数据列表
*/
private List<WorkerAttDayVo> processWorkerAttendance(Integer workerId,
List<ThreeTableOneRosterPo> attendanceRecords, List<MapBeanVo> daysOfMonth) {
List<WorkerAttDayVo> result = new ArrayList<>();
// 将该员工的考勤记录按日期分组便于快速查找
// 将该员工的考勤记录按日期分组便于快速查找
Map<String, ThreeTableOneRosterPo> einByDate = attendanceRecords.stream()
.collect(Collectors.toMap(
ThreeTableOneRosterPo::getEinDay,
record -> record,
(existing, replacement) -> existing));
// 遍历月份中的每一天
for (MapBeanVo dayInfo : daysOfMonth) {
String dateStr = dayInfo.getValue();
String dayStr = dayInfo.getKey();
WorkerAttDayVo dayRecord = new WorkerAttDayVo();
dayRecord.setDay(dayStr);
// 查找该日期是否有考勤记录
ThreeTableOneRosterPo einForDate = einByDate.get(dateStr);
if (einForDate != null) {
// 存在考勤记录
dayRecord.setIsEin("1"); // 在场
// 检查是否有打卡记录
if (einForDate.getAttDay() != null) {
dayRecord.setIsAtt("1"); // 打卡
}
// 检查是否有补卡记录
if ("1".equals(einForDate.getIsRepair())) {
dayRecord.setIsRepair("1"); // 补卡
}
} else {
// 无考勤记录
dayRecord.setIsEin("0"); // 不在场
dayRecord.setIsAtt("0"); // 未打卡
dayRecord.setIsRepair("0"); // 未补卡
}
result.add(dayRecord);
}
return result;
}
}

View File

@ -94,4 +94,145 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{item.id}
</foreach>
</update>
<select id="getAllPro" resultType="com.bonus.job.domain.MapBeanVo">
SELECT
distinct
pro_id as id
FROM
bm_worker_ein_day_record
where att_month = #{lastMonth}
order by pro_id
</select>
<select id="getWorkerRosterData" resultType="com.bonus.job.domain.ThreeTableOneRosterPo">
SELECT
d.pro_id,
d.pro_name,
d.att_month,
d.sub_id,
d.sub_name,
d.team_id,
d.team_name,
d.worker_id,
pw.`name`,
pw.sex,
d.post_id,
d.post_name,
d.id_number,
pw.address,
pw.phone,
'remark' as remark,
-- 正确的最早入场时间所有与8月相关的入场中最早的
MIN(p.ein_time) AS first_ein_time,
-- 获取最后一次“相关入场”的出场时间(即使为 null
(
SELECT p2.exit_time
FROM bm_worker_ein_pro_record p2
WHERE p2.worker_id = d.worker_id
AND p2.pro_id = d.pro_id
AND (
-- 情况1入场在8月内
(p2.ein_time >= #{startTime} AND p2.ein_time &lt; #{endTime})
OR
-- 情况2入场在8月前但8月仍在场
(p2.ein_time &lt; #{startTime} AND p2.exit_time >= #{startTime})
)
ORDER BY p2.ein_time DESC
LIMIT 1
) AS last_exit_time
FROM
bm_worker_ein_day_record d
LEFT JOIN pm_worker pw ON pw.id = d.worker_id
LEFT JOIN
bm_worker_ein_pro_record p
ON d.worker_id = p.worker_id
AND d.pro_id = p.pro_id
AND (
-- 同样条件只关联与8月出勤相关的出入记录
(p.ein_time >= #{startTime} AND p.ein_time &lt; #{endTime})
OR
(p.ein_time &lt; #{startTime} AND p.exit_time >= #{startTime})
)
WHERE
d.pro_id = #{proId}
AND d.att_month = #{attMonth}
GROUP BY
d.worker_id;
</select>
<select id="getWorkerApplyData" resultType="com.bonus.job.domain.ThreeTableOneRosterPo">
SELECT
d.pro_id,
d.att_month,
d.sub_id,
d.sub_name,
d.team_id,
d.team_name,
d.worker_id,
pw.`name`,
d.id_number,
bwwc.bank_name,
bwwc.bank_card_code,
bwwc.bank_identifier_code,
MAX(d.contract_id) AS contract_id,
bwc.day_rate as price_wage,
'按月支付' AS payment,
'每月20日前' AS pay_day,
'备注' AS remark,
pw.phone
FROM
bm_worker_ein_day_record d
LEFT JOIN pm_worker pw ON pw.id = d.worker_id
LEFT JOIN bm_worker_wage_card bwwc ON bwwc.worker_id = pw.id
LEFT JOIN bm_worker_contract bwc ON (
bwc.id = (
SELECT
MAX(d2.contract_id)
FROM
bm_worker_ein_day_record d2
WHERE
d2.worker_id = d.worker_id
AND d2.pro_id = #{proId}
AND d2.att_month = #{attMonth}
AND d2.contract_id IS NOT NULL
)
)
WHERE
d.pro_id = #{proId}
AND d.att_month = #{attMonth}
GROUP BY
d.worker_id;
</select>
<select id="getWorkerAttData" resultType="com.bonus.job.domain.ThreeTableOneRosterPo">
SELECT
d.pro_id,
d.pro_name,
d.att_month,
d.sub_id,
d.sub_name,
d.team_id,
d.team_name,
d.post_id,
d.post_name,
d.worker_id,
pw.`name`,
d.id_number,
d.ein_day,
bap.att_day,
bap.is_repair
FROM
bm_worker_ein_day_record d
LEFT JOIN pm_worker pw ON pw.id = d.worker_id
LEFT JOIN bm_att_person bap ON bap.att_day = d.ein_day and bap.pro_id = d.pro_id and bap.worker_id = d.worker_id
WHERE
d.pro_id = #{proId}
AND d.att_month = #{attMonth}
</select>
<select id="getWorkerPayData" resultType="com.bonus.job.domain.ThreeTableOneRosterPo">
</select>
</mapper>