优化
This commit is contained in:
@ -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")
|
||||
|
||||
@ -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<>();
|
||||
|
||||
@ -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重新解析表头)
|
||||
*/
|
||||
|
||||
@ -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:在读取器初始化时就全局设置表头行号】
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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));
|
||||
|
||||
// 合并标题行,横跨所有列
|
||||
|
||||
Reference in New Issue
Block a user