优化
This commit is contained in:
@ -6,6 +6,7 @@ import jakarta.validation.constraints.NotNull;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.dromara.common.core.domain.R;
|
import org.dromara.common.core.domain.R;
|
||||||
import org.dromara.common.excel.utils.ExcelUtil;
|
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.annotation.Log;
|
||||||
import org.dromara.common.log.enums.BusinessType;
|
import org.dromara.common.log.enums.BusinessType;
|
||||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||||
@ -25,6 +26,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 员工每日工资
|
* 员工每日工资
|
||||||
@ -87,10 +89,10 @@ public class SubUserSalaryDetailController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/import")
|
@PutMapping("/import")
|
||||||
public R<Void> importData(@RequestParam("file") MultipartFile file,
|
@RepeatSubmit(interval = 1, timeUnit = TimeUnit.MINUTES,message = "正在导入,请勿重复提交")
|
||||||
@RequestParam("month") String month) {
|
public R<Boolean> importData(@RequestParam("file") MultipartFile file) {
|
||||||
subUserSalaryDetailService.importData(file, month);
|
subUserSalaryDetailService.importData(file, null);
|
||||||
return R.ok();
|
return R.ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/detailList")
|
@GetMapping("/detailList")
|
||||||
|
|||||||
@ -16,7 +16,8 @@ public class DynamicSalaryData {
|
|||||||
private String idCard; // 身份证号
|
private String idCard; // 身份证号
|
||||||
private Double total; // 合计出勤天数
|
private Double total; // 合计出勤天数
|
||||||
private String isLeave; // 是否离场(未离场/已离场)
|
private String isLeave; // 是否离场(未离场/已离场)
|
||||||
private String signature; // 签字(可选)
|
private String signature;
|
||||||
|
private String month;// 签字(可选)
|
||||||
|
|
||||||
// 动态日期考勤:key=完整日期(如“2025-09-01”),value=考勤状态(0=未出勤,1=出勤)
|
// 动态日期考勤:key=完整日期(如“2025-09-01”),value=考勤状态(0=未出勤,1=出勤)
|
||||||
private Map<String, Double> dailyAttendance = new LinkedHashMap<>();
|
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.context.AnalysisContext;
|
||||||
import com.alibaba.excel.event.AnalysisEventListener;
|
import com.alibaba.excel.event.AnalysisEventListener;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
import org.dromara.common.utils.IdCardEncryptorUtil;
|
import org.dromara.common.utils.IdCardEncryptorUtil;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
@ -13,7 +14,7 @@ import java.util.Map;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class DynamicSalaryListener extends AnalysisEventListener<Map<Integer, String>> {
|
public class DynamicSalaryListener extends AnalysisEventListener<Map<Integer, String>> {
|
||||||
private final List<DynamicSalaryData> allAttendanceList;
|
private final List<DynamicSalaryData> allAttendanceList;
|
||||||
private final String month;
|
private String month;
|
||||||
private static final int DATE_COL_START_INDEX = 3; // 日期列起始索引(第4列)
|
private static final int DATE_COL_START_INDEX = 3; // 日期列起始索引(第4列)
|
||||||
private int dateColEndIndex;
|
private int dateColEndIndex;
|
||||||
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
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行)
|
// 写死表头行号(0-based,对应Excel第3行)
|
||||||
private static final int FIXED_HEAD_ROW_NUMBER = 2;
|
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.allAttendanceList = allAttendanceList;
|
||||||
this.month = month;
|
|
||||||
// 月份格式校验
|
|
||||||
try {
|
|
||||||
LocalDate.parse(month + "-01", dateFormatter);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IllegalArgumentException("月份格式错误!需传入yyyy-MM格式(如2025-09)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
|
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
|
||||||
// 仅处理固定行号的表头,且仅初始化1次
|
|
||||||
|
// 处理第一行标题,提取月份
|
||||||
int currentRowIndex = context.readRowHolder().getRowIndex();
|
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) {
|
if (currentRowIndex != FIXED_HEAD_ROW_NUMBER || isHeadInitialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -68,6 +84,10 @@ public class DynamicSalaryListener extends AnalysisEventListener<Map<Integer, St
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invoke(Map<Integer, String> rowData, AnalysisContext context) {
|
public void invoke(Map<Integer, String> rowData, AnalysisContext context) {
|
||||||
|
// 确保month已初始化
|
||||||
|
if (this.month == null || this.month.isEmpty()) {
|
||||||
|
throw new RuntimeException("月份信息未正确解析,请检查Excel标题格式");
|
||||||
|
}
|
||||||
// 【核心优化:强化无效行过滤】
|
// 【核心优化:强化无效行过滤】
|
||||||
// 1. 获取第1列(序号)和第2列(姓名)的值,用于判断是否为有效数据行
|
// 1. 获取第1列(序号)和第2列(姓名)的值,用于判断是否为有效数据行
|
||||||
String serialNumberStr = getCellValue(rowData, 0);
|
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.setTotal(parseDouble(getCellValue(rowData, dateColEndIndex + 1)));
|
||||||
attendance.setIsLeave(getCellValue(rowData, dateColEndIndex + 2));
|
attendance.setIsLeave(getCellValue(rowData, dateColEndIndex + 2));
|
||||||
attendance.setSignature(getCellValue(rowData, dateColEndIndex + 3));
|
attendance.setSignature(getCellValue(rowData, dateColEndIndex + 3));
|
||||||
|
attendance.setMonth(month);
|
||||||
// 4. 解析动态日期列
|
// 4. 解析动态日期列
|
||||||
for (int colIndex = DATE_COL_START_INDEX; colIndex <= dateColEndIndex; colIndex++) {
|
for (int colIndex = DATE_COL_START_INDEX; colIndex <= dateColEndIndex; colIndex++) {
|
||||||
int day = colIndex - DATE_COL_START_INDEX + 1;
|
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时调用,重置表头初始化状态(确保新Sheet重新解析表头)
|
* 切换Sheet时调用,重置表头初始化状态(确保新Sheet重新解析表头)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -27,7 +27,7 @@ public class SalaryExcelReader {
|
|||||||
*/
|
*/
|
||||||
public static List<DynamicSalaryData> readAllAttendanceByStream(InputStream inputStream, String month) {
|
public static List<DynamicSalaryData> readAllAttendanceByStream(InputStream inputStream, String month) {
|
||||||
List<DynamicSalaryData> allAttendanceList = new ArrayList<>();
|
List<DynamicSalaryData> allAttendanceList = new ArrayList<>();
|
||||||
DynamicSalaryListener listener = new DynamicSalaryListener(allAttendanceList, month);
|
DynamicSalaryListener listener = new DynamicSalaryListener(allAttendanceList);
|
||||||
|
|
||||||
try (
|
try (
|
||||||
// 1. 构建Excel读取器:读取源为 InputStream,全局设置表头行号
|
// 1. 构建Excel读取器:读取源为 InputStream,全局设置表头行号
|
||||||
@ -60,7 +60,7 @@ public class SalaryExcelReader {
|
|||||||
|
|
||||||
public static List<DynamicSalaryData> readAllAttendance(String excelFilePath, String month) {
|
public static List<DynamicSalaryData> readAllAttendance(String excelFilePath, String month) {
|
||||||
List<DynamicSalaryData> allAttendanceList = new ArrayList<>();
|
List<DynamicSalaryData> allAttendanceList = new ArrayList<>();
|
||||||
DynamicSalaryListener listener = new DynamicSalaryListener(allAttendanceList, month);
|
DynamicSalaryListener listener = new DynamicSalaryListener(allAttendanceList);
|
||||||
|
|
||||||
try (
|
try (
|
||||||
// 【核心修改1:在读取器初始化时就全局设置表头行号】
|
// 【核心修改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));
|
Map<String, Map<String, Double>> dataMap = dataList.stream().collect(Collectors.toMap(vo -> idCardEncryptorUtil.encrypt(vo.getIdCard()), DynamicSalaryData::getDailyAttendance));
|
||||||
Set<String> cards = dataMap.keySet();
|
Set<String> cards = dataMap.keySet();
|
||||||
|
|
||||||
|
month = dataList.getFirst().getMonth();
|
||||||
YearMonth parse = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM"));
|
YearMonth parse = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM"));
|
||||||
LocalDate start = parse.atDay(1);
|
LocalDate start = parse.atDay(1);
|
||||||
LocalDate end = parse.atEndOfMonth();
|
LocalDate end = parse.atEndOfMonth();
|
||||||
|
|||||||
@ -1252,7 +1252,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
|
|||||||
// ==================== 表头部分 ====================
|
// ==================== 表头部分 ====================
|
||||||
Row titleRow = sheet.createRow(0);
|
Row titleRow = sheet.createRow(0);
|
||||||
Cell titleCell = titleRow.createCell(0);
|
Cell titleCell = titleRow.createCell(0);
|
||||||
titleCell.setCellValue("建筑施工企业现场人员考勤表(" + clockDate + "月)");
|
titleCell.setCellValue("建筑施工企业现场人员考勤表(" + clockDate + ")");
|
||||||
titleCell.setCellStyle(createTitleCellStyle(workbook));
|
titleCell.setCellStyle(createTitleCellStyle(workbook));
|
||||||
|
|
||||||
// 合并标题行,横跨所有列
|
// 合并标题行,横跨所有列
|
||||||
|
|||||||
Reference in New Issue
Block a user