|
|
@@ -2,16 +2,13 @@
|
|
|
package org.sky.scientific.service.impl;
|
|
|
|
|
|
import com.alibaba.excel.EasyExcel;
|
|
|
-import com.alibaba.excel.write.metadata.style.WriteCellStyle;
|
|
|
-import com.alibaba.excel.write.metadata.style.WriteFont;
|
|
|
-import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
|
|
|
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import jakarta.servlet.http.HttpServletResponse;
|
|
|
import lombok.AllArgsConstructor;
|
|
|
import lombok.SneakyThrows;
|
|
|
-import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
|
|
import org.sky.core.log.exception.ServiceException;
|
|
|
import org.sky.core.mp.base.BaseServiceImpl;
|
|
|
import org.sky.core.mp.support.Condition;
|
|
|
@@ -23,6 +20,8 @@ import org.sky.core.tool.utils.Func;
|
|
|
import org.sky.core.tool.utils.StringUtil;
|
|
|
import org.sky.scientific.excel.AttendanceMonthlyDataListener;
|
|
|
import org.sky.scientific.excel.AttendanceMonthlyExcel;
|
|
|
+import org.sky.scientific.excel.handle.AttendanceMergeStrategy;
|
|
|
+import org.sky.scientific.excel.handle.CustomCellStyleHandler;
|
|
|
import org.sky.scientific.mapper.AttendanceMapper;
|
|
|
import org.sky.scientific.mapper.SettingMapper;
|
|
|
import org.sky.scientific.mapper.TechnicianMapper;
|
|
|
@@ -41,6 +40,7 @@ import java.net.URLEncoder;
|
|
|
import java.time.LocalDate;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.util.*;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
/**
|
|
|
* 考勤管理 服务实现类
|
|
|
@@ -181,109 +181,94 @@ public class AttendanceServiceImpl extends BaseServiceImpl<AttendanceMapper, Att
|
|
|
Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);
|
|
|
|
|
|
List<AttendanceVO> list = this.selectAttendancePage(dto, Condition.getPage(query)).getRecords();
|
|
|
-
|
|
|
+ AtomicInteger index = new AtomicInteger(1);
|
|
|
+ for (AttendanceVO vo : list) {
|
|
|
+ vo.setXh(index.getAndIncrement());
|
|
|
+ }
|
|
|
Integer year = Integer.valueOf(dto.getYearAndMonth().substring(0, 4));
|
|
|
Integer month = Integer.valueOf(dto.getYearAndMonth().substring(5));
|
|
|
- // 1. 构建完整数据(包含标题、表头、数据)
|
|
|
- List<List<Object>> allData = buildCompleteData(list, year, month);
|
|
|
-
|
|
|
- // 2. 构建表头
|
|
|
- List<List<String>> head = Collections.emptyList();//buildDynamicHead(year, month);
|
|
|
-
|
|
|
- // 3. 设置样式
|
|
|
- WriteCellStyle headStyle = new WriteCellStyle();
|
|
|
- headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
|
|
|
- WriteFont headFont = new WriteFont();
|
|
|
- headFont.setBold(true);
|
|
|
- headFont.setFontHeightInPoints((short) 12);
|
|
|
- headStyle.setWriteFont(headFont);
|
|
|
-
|
|
|
- WriteCellStyle contentStyle = new WriteCellStyle();
|
|
|
- contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
|
|
|
|
|
|
- HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(headStyle, contentStyle);
|
|
|
+ // 构建数据
|
|
|
+ List<List<Object>> data = buildData(list, year, month);
|
|
|
|
|
|
- // 4. 导出Excel
|
|
|
+ // 导出Excel(包含合并单元格策略)
|
|
|
EasyExcel.write(response.getOutputStream())
|
|
|
- .head(head)
|
|
|
- .registerWriteHandler(styleStrategy)
|
|
|
+ .registerWriteHandler(new AttendanceMergeStrategy(year, month))
|
|
|
+ .registerWriteHandler(new CustomCellStyleHandler())
|
|
|
+ .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
|
|
.autoCloseStream(true)
|
|
|
.sheet("考勤信息")
|
|
|
- .doWrite(allData);
|
|
|
+ .doWrite(data);
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
|
throw new ServiceException("导出失败:" + e.getLocalizedMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 构建完整数据(包含标题行、表头行、数据行)
|
|
|
+ * 构建数据行
|
|
|
*/
|
|
|
- private List<List<Object>> buildCompleteData(List<AttendanceVO> attendanceList, Integer year, Integer month) {
|
|
|
- List<List<Object>> allData = new ArrayList<>();
|
|
|
+ private List<List<Object>> buildData(List<AttendanceVO> attendanceList, Integer year, Integer month) {
|
|
|
+ List<List<Object>> data = new ArrayList<>();
|
|
|
|
|
|
LocalDate startDate = LocalDate.of(year, month, 1);
|
|
|
int daysInMonth = startDate.lengthOfMonth();
|
|
|
- int totalColumns = 5 + daysInMonth + 2; // 5个固定列 + 天数列 + 2个统计列
|
|
|
+ DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
|
|
+ DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("E", Locale.CHINA);
|
|
|
+ DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("d");
|
|
|
|
|
|
- // 1. 标题行(第一行)
|
|
|
+ // 第一行:标题行
|
|
|
List<Object> titleRow = new ArrayList<>();
|
|
|
titleRow.add("每月考勤信息" + year + "年" + month + "月");
|
|
|
- // 填充剩余单元格为空,用于合并
|
|
|
- for (int i = 1; i < totalColumns; i++) {
|
|
|
+ for (int i = 1; i < daysInMonth + 7; i++) { // 7 = 5个固定列 + 2个统计列
|
|
|
titleRow.add("");
|
|
|
}
|
|
|
- allData.add(titleRow);
|
|
|
-
|
|
|
- // 2. 空行(第二行)
|
|
|
- allData.add(Collections.nCopies(totalColumns, ""));
|
|
|
-
|
|
|
- // 3. 表头行(第三行)
|
|
|
- List<Object> headerRow = new ArrayList<>();
|
|
|
- headerRow.add("序号");
|
|
|
- headerRow.add("姓名");
|
|
|
- headerRow.add("工号");
|
|
|
- headerRow.add("身份证号码");
|
|
|
- headerRow.add("科研人员类别");
|
|
|
-
|
|
|
- // 日期表头
|
|
|
- DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("dd");
|
|
|
- for (int day = 1; day <= daysInMonth; day++) {
|
|
|
- headerRow.add(String.valueOf(day));
|
|
|
+ data.add(titleRow);
|
|
|
+
|
|
|
+ // 第二行:纵向表头标题
|
|
|
+ List<Object> verticalHeaderRow = new ArrayList<>();
|
|
|
+ verticalHeaderRow.add("序号");
|
|
|
+ verticalHeaderRow.add("姓名");
|
|
|
+ verticalHeaderRow.add("工号");
|
|
|
+ verticalHeaderRow.add("身份证号码");
|
|
|
+ verticalHeaderRow.add("科研人员类别");
|
|
|
+ //第二行:周几行
|
|
|
+ for (int i = 1; i <= daysInMonth; i++) {
|
|
|
+ LocalDate date = LocalDate.of(year, month, i);
|
|
|
+ verticalHeaderRow.add(date.format(weekFormatter).substring(1));
|
|
|
}
|
|
|
-
|
|
|
- headerRow.add("本月出勤/小时");
|
|
|
- headerRow.add("本月出勤/天");
|
|
|
- allData.add(headerRow);
|
|
|
-
|
|
|
- // 4. 周几行(第四行)
|
|
|
- List<Object> weekRow = new ArrayList<>();
|
|
|
- weekRow.add(""); // 序号列
|
|
|
- weekRow.add(""); // 姓名列
|
|
|
- weekRow.add(""); // 工号列
|
|
|
- weekRow.add(""); // 身份证列
|
|
|
- weekRow.add(""); // 人员类型列
|
|
|
-
|
|
|
- // 周几数据
|
|
|
- DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("E", Locale.CHINA);
|
|
|
+ verticalHeaderRow.add("本月出勤/小时");
|
|
|
+ verticalHeaderRow.add("本月出勤/天");
|
|
|
+ data.add(verticalHeaderRow);
|
|
|
+
|
|
|
+ // 第三行:日期数字行
|
|
|
+ List<Object> dayRow = new ArrayList<>();
|
|
|
+ dayRow.add("");
|
|
|
+ dayRow.add("");
|
|
|
+ dayRow.add("");
|
|
|
+ dayRow.add("");
|
|
|
+ dayRow.add("");
|
|
|
for (int day = 1; day <= daysInMonth; day++) {
|
|
|
LocalDate date = LocalDate.of(year, month, day);
|
|
|
- weekRow.add(date.format(weekFormatter));
|
|
|
+ dayRow.add(date.format(dayFormatter));
|
|
|
}
|
|
|
+ dayRow.add("");
|
|
|
+ dayRow.add("");
|
|
|
+ data.add(dayRow);
|
|
|
|
|
|
- weekRow.add(""); // 小时统计列
|
|
|
- weekRow.add(""); // 天数统计列
|
|
|
- allData.add(weekRow);
|
|
|
-
|
|
|
- // 5. 数据行(从第五行开始)
|
|
|
- DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
|
|
+ // 第4行开始:数据行
|
|
|
for (AttendanceVO attendance : attendanceList) {
|
|
|
List<Object> dataRow = new ArrayList<>();
|
|
|
|
|
|
- // 基础信息
|
|
|
+ // 序号
|
|
|
dataRow.add(attendance.getXh());
|
|
|
+ // 姓名
|
|
|
dataRow.add(attendance.getName());
|
|
|
+ // 工号
|
|
|
dataRow.add(attendance.getNumber());
|
|
|
+ // 身份证号码
|
|
|
dataRow.add(attendance.getIdCard());
|
|
|
+ // 科研人员类别
|
|
|
dataRow.add(attendance.getPersonnelType());
|
|
|
|
|
|
// 每日考勤数据
|
|
|
@@ -304,10 +289,10 @@ public class AttendanceServiceImpl extends BaseServiceImpl<AttendanceMapper, Att
|
|
|
dataRow.add(attendance.getMonthlyHours());
|
|
|
dataRow.add(attendance.getMonthlyDays());
|
|
|
|
|
|
- allData.add(dataRow);
|
|
|
+ data.add(dataRow);
|
|
|
}
|
|
|
|
|
|
- return allData;
|
|
|
+ return data;
|
|
|
}
|
|
|
|
|
|
@Override
|