This commit is contained in:
zt
2025-09-22 22:57:43 +08:00
parent d72f763545
commit b754e3ffc0
6 changed files with 45 additions and 19 deletions

View File

@ -6,6 +6,7 @@ import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
@ -25,6 +26,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 员工每日工资
@ -87,10 +89,10 @@ public class SubUserSalaryDetailController extends BaseController {
}
@PutMapping("/import")
public R<Void> importData(@RequestParam("file") MultipartFile file,
@RequestParam("month") String month) {
subUserSalaryDetailService.importData(file, month);
return R.ok();
@RepeatSubmit(interval = 1, timeUnit = TimeUnit.MINUTES,message = "正在导入,请勿重复提交")
public R<Boolean> importData(@RequestParam("file") MultipartFile file) {
subUserSalaryDetailService.importData(file, null);
return R.ok(true);
}
@GetMapping("/detailList")

View File

@ -16,7 +16,8 @@ public class DynamicSalaryData {
private String idCard; // 身份证号
private Double total; // 合计出勤天数
private String isLeave; // 是否离场(未离场/已离场)
private String signature; // 签字(可选)
private String signature;
private String month;// 签字(可选)
// 动态日期考勤key=完整日期如“2025-09-01”value=考勤状态0=未出勤1=出勤)
private Map<String, Double> dailyAttendance = new LinkedHashMap<>();

View File

@ -3,6 +3,7 @@ package org.dromara.contractor.excel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.utils.IdCardEncryptorUtil;
import java.time.LocalDate;
@ -13,7 +14,7 @@ import java.util.Map;
@Slf4j
public class DynamicSalaryListener extends AnalysisEventListener<Map<Integer, String>> {
private final List<DynamicSalaryData> allAttendanceList;
private final String month;
private String month;
private static final int DATE_COL_START_INDEX = 3; // 日期列起始索引第4列
private int dateColEndIndex;
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@ -21,22 +22,37 @@ public class DynamicSalaryListener extends AnalysisEventListener<Map<Integer, St
// 写死表头行号0-based对应Excel第3行
private static final int FIXED_HEAD_ROW_NUMBER = 2;
public DynamicSalaryListener(List<DynamicSalaryData> allAttendanceList, String month) {
public DynamicSalaryListener(List<DynamicSalaryData> allAttendanceList) {
this.allAttendanceList = allAttendanceList;
this.month = month;
// 月份格式校验
try {
LocalDate.parse(month + "-01", dateFormatter);
} catch (Exception e) {
throw new IllegalArgumentException("月份格式错误需传入yyyy-MM格式如2025-09");
}
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
// 仅处理固定行号的表头且仅初始化1次
// 处理第一行标题,提取月份
int currentRowIndex = context.readRowHolder().getRowIndex();
if (currentRowIndex == 0) { // 第一行标题
// 从标题中提取月份,如"建筑施工企业现场人员工资表2025年09月"
String title = getCellValue(headMap, 0);
if (title != null && title.contains("") && title.contains("")) {
try {
// 提取年月部分,如"2025-09"
// 转换为yyyy-MM格式如"2025-09"
this.month = title.substring(title.indexOf("") + 1, title.indexOf(""));
// 校验格式
LocalDate.parse(this.month + "-01", dateFormatter);
log.info("从标题行解析到月份:{}", this.month);
} catch (Exception e) {
throw new ServiceException("未解析到年月");
}
}
return;
}
// 仅处理固定行号的表头且仅初始化1次
if (currentRowIndex != FIXED_HEAD_ROW_NUMBER || isHeadInitialized) {
return;
}
@ -68,6 +84,10 @@ public class DynamicSalaryListener extends AnalysisEventListener<Map<Integer, St
@Override
public void invoke(Map<Integer, String> rowData, AnalysisContext context) {
// 确保month已初始化
if (this.month == null || this.month.isEmpty()) {
throw new RuntimeException("月份信息未正确解析请检查Excel标题格式");
}
// 【核心优化:强化无效行过滤】
// 1. 获取第1列序号和第2列姓名的值用于判断是否为有效数据行
String serialNumberStr = getCellValue(rowData, 0);
@ -99,7 +119,7 @@ public class DynamicSalaryListener extends AnalysisEventListener<Map<Integer, St
attendance.setTotal(parseDouble(getCellValue(rowData, dateColEndIndex + 1)));
attendance.setIsLeave(getCellValue(rowData, dateColEndIndex + 2));
attendance.setSignature(getCellValue(rowData, dateColEndIndex + 3));
attendance.setMonth(month);
// 4. 解析动态日期列
for (int colIndex = DATE_COL_START_INDEX; colIndex <= dateColEndIndex; colIndex++) {
int day = colIndex - DATE_COL_START_INDEX + 1;
@ -120,6 +140,7 @@ public class DynamicSalaryListener extends AnalysisEventListener<Map<Integer, St
// ------------------- 新增工具方法:判断整行是否为空 -------------------
/**
* 判断行数据是否所有列均为空(过滤空行)
*/
@ -167,6 +188,7 @@ public class DynamicSalaryListener extends AnalysisEventListener<Map<Integer, St
}
// ------------------- 重置表头初始化状态用于多Sheet遍历 -------------------
/**
* 切换Sheet时调用重置表头初始化状态确保新Sheet重新解析表头
*/

View File

@ -27,7 +27,7 @@ public class SalaryExcelReader {
*/
public static List<DynamicSalaryData> readAllAttendanceByStream(InputStream inputStream, String month) {
List<DynamicSalaryData> allAttendanceList = new ArrayList<>();
DynamicSalaryListener listener = new DynamicSalaryListener(allAttendanceList, month);
DynamicSalaryListener listener = new DynamicSalaryListener(allAttendanceList);
try (
// 1. 构建Excel读取器读取源为 InputStream全局设置表头行号
@ -60,7 +60,7 @@ public class SalaryExcelReader {
public static List<DynamicSalaryData> readAllAttendance(String excelFilePath, String month) {
List<DynamicSalaryData> allAttendanceList = new ArrayList<>();
DynamicSalaryListener listener = new DynamicSalaryListener(allAttendanceList, month);
DynamicSalaryListener listener = new DynamicSalaryListener(allAttendanceList);
try (
// 【核心修改1在读取器初始化时就全局设置表头行号】

View File

@ -549,6 +549,7 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
Map<String, Map<String, Double>> dataMap = dataList.stream().collect(Collectors.toMap(vo -> idCardEncryptorUtil.encrypt(vo.getIdCard()), DynamicSalaryData::getDailyAttendance));
Set<String> cards = dataMap.keySet();
month = dataList.getFirst().getMonth();
YearMonth parse = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM"));
LocalDate start = parse.atDay(1);
LocalDate end = parse.atEndOfMonth();

View File

@ -1252,7 +1252,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
// ==================== 表头部分 ====================
Row titleRow = sheet.createRow(0);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue("建筑施工企业现场人员考勤表(" + clockDate + "");
titleCell.setCellValue("建筑施工企业现场人员考勤表(" + clockDate + "");
titleCell.setCellStyle(createTitleCellStyle(workbook));
// 合并标题行,横跨所有列