优化
This commit is contained in:
		| @ -1,9 +1,13 @@ | |||||||
| package org.dromara.contractor.mapper; | package org.dromara.contractor.mapper; | ||||||
|  |  | ||||||
|  | import org.apache.ibatis.annotations.Insert; | ||||||
|  | import org.apache.ibatis.annotations.Param; | ||||||
| import org.dromara.contractor.domain.SubUserSalaryDetail; | import org.dromara.contractor.domain.SubUserSalaryDetail; | ||||||
| import org.dromara.contractor.domain.vo.usersalarydetail.SubUserSalaryDetailVo; | import org.dromara.contractor.domain.vo.usersalarydetail.SubUserSalaryDetailVo; | ||||||
| import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; | import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 员工每日工资Mapper接口 |  * 员工每日工资Mapper接口 | ||||||
|  * |  * | ||||||
| @ -12,4 +16,15 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; | |||||||
|  */ |  */ | ||||||
| public interface SubUserSalaryDetailMapper extends BaseMapperPlus<SubUserSalaryDetail, SubUserSalaryDetailVo> { | public interface SubUserSalaryDetailMapper extends BaseMapperPlus<SubUserSalaryDetail, SubUserSalaryDetailVo> { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     @Insert({ | ||||||
|  |         "<script>", | ||||||
|  |         "INSERT INTO sub_user_salary_detail (project_id, team_id, user_id, user_name, report_date, work_hour, daily_salary, total_salary, create_by, update_by, remark) VALUES ", | ||||||
|  |         "<foreach collection='list' item='item' separator=','>", | ||||||
|  |         "(#{item.projectId}, #{item.teamId}, #{item.userId}, #{item.userName}, #{item.reportDate}, #{item.workHour}, #{item.dailySalary}, #{item.totalSalary}, #{userId}, #{userId}, #{item.remark})", | ||||||
|  |         "</foreach>", | ||||||
|  |         "</script>" | ||||||
|  |     }) | ||||||
|  |    Boolean insertAll(@Param("list")List<SubUserSalaryDetail> list, @Param("userId") Long userId); | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ import jakarta.annotation.Resource; | |||||||
| import jakarta.servlet.ServletException; | import jakarta.servlet.ServletException; | ||||||
| import jakarta.servlet.http.HttpServletResponse; | import jakarta.servlet.http.HttpServletResponse; | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.apache.pdfbox.io.IOUtils; | import org.apache.pdfbox.io.IOUtils; | ||||||
| import org.apache.poi.ss.usermodel.Sheet; | import org.apache.poi.ss.usermodel.Sheet; | ||||||
| import org.apache.poi.ss.usermodel.Workbook; | import org.apache.poi.ss.usermodel.Workbook; | ||||||
| @ -30,6 +31,7 @@ import org.dromara.common.core.utils.ObjectUtils; | |||||||
| import org.dromara.common.core.utils.StringUtils; | import org.dromara.common.core.utils.StringUtils; | ||||||
| import org.dromara.common.mybatis.core.page.PageQuery; | import org.dromara.common.mybatis.core.page.PageQuery; | ||||||
| import org.dromara.common.mybatis.core.page.TableDataInfo; | import org.dromara.common.mybatis.core.page.TableDataInfo; | ||||||
|  | import org.dromara.common.satoken.utils.LoginHelper; | ||||||
| import org.dromara.common.utils.IdCardEncryptorUtil; | import org.dromara.common.utils.IdCardEncryptorUtil; | ||||||
| import org.dromara.contractor.domain.SubConstructionUser; | import org.dromara.contractor.domain.SubConstructionUser; | ||||||
| import org.dromara.contractor.domain.SubUserSalaryDetail; | import org.dromara.contractor.domain.SubUserSalaryDetail; | ||||||
| @ -58,6 +60,7 @@ import org.springframework.beans.BeanUtils; | |||||||
| import org.springframework.context.annotation.Lazy; | import org.springframework.context.annotation.Lazy; | ||||||
| import org.springframework.scheduling.annotation.Async; | import org.springframework.scheduling.annotation.Async; | ||||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||||
|  | import org.springframework.transaction.annotation.Transactional; | ||||||
| import org.springframework.web.multipart.MultipartFile; | import org.springframework.web.multipart.MultipartFile; | ||||||
| import software.amazon.awssdk.utils.CollectionUtils; | import software.amazon.awssdk.utils.CollectionUtils; | ||||||
|  |  | ||||||
| @ -72,6 +75,7 @@ import java.time.LocalDate; | |||||||
| import java.time.YearMonth; | import java.time.YearMonth; | ||||||
| import java.time.format.DateTimeFormatter; | import java.time.format.DateTimeFormatter; | ||||||
| import java.util.*; | import java.util.*; | ||||||
|  | import java.util.concurrent.ConcurrentHashMap; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| import org.dromara.contractor.excel.SalaryExcelReader; | import org.dromara.contractor.excel.SalaryExcelReader; | ||||||
|  |  | ||||||
| @ -83,6 +87,7 @@ import org.dromara.contractor.excel.SalaryExcelReader; | |||||||
|  */ |  */ | ||||||
| @RequiredArgsConstructor | @RequiredArgsConstructor | ||||||
| @Service | @Service | ||||||
|  | @Slf4j | ||||||
| public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDetailMapper, SubUserSalaryDetail> | public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDetailMapper, SubUserSalaryDetail> | ||||||
|     implements ISubUserSalaryDetailService { |     implements ISubUserSalaryDetailService { | ||||||
|  |  | ||||||
| @ -525,19 +530,21 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet | |||||||
|  |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|  |     @Transactional(rollbackFor = Exception.class) | ||||||
|     public void importData(MultipartFile file, String month) { |     public void importData(MultipartFile file, String month) { | ||||||
|  |         long startTime = System.currentTimeMillis(); | ||||||
|  |  | ||||||
|         // 1. 校验文件合法性 |         // 1. 校验文件合法性 | ||||||
|         if (file.isEmpty()) { |         if (file.isEmpty()) { | ||||||
|             throw new IllegalArgumentException("上传的Excel文件不能为空!"); |             throw new IllegalArgumentException("上传的Excel文件不能为空!"); | ||||||
|         } |         } | ||||||
|         // 校验文件格式(仅允许xlsx/xls) |  | ||||||
|         String originalFilename = file.getOriginalFilename(); |         String originalFilename = file.getOriginalFilename(); | ||||||
|         if (originalFilename == null || !(originalFilename.endsWith(".xlsx"))) { |         if (originalFilename == null || !(originalFilename.endsWith(".xlsx"))) { | ||||||
|             throw new IllegalArgumentException("仅支持上传Excel文件(.xlsx 格式)!"); |             throw new IllegalArgumentException("仅支持上传Excel文件(.xlsx 格式)!"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         List<DynamicSalaryData> dataList; |         List<DynamicSalaryData> dataList; | ||||||
|         try { |         try { | ||||||
|             // 2. 将 MultipartFile 转为 InputStream,传给工具类解析 |  | ||||||
|             dataList = SalaryExcelReader.readAllAttendanceByStream(file.getInputStream(), month); |             dataList = SalaryExcelReader.readAllAttendanceByStream(file.getInputStream(), month); | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             throw new RuntimeException("Excel流解析失败:" + e.getMessage(), e); |             throw new RuntimeException("Excel流解析失败:" + e.getMessage(), e); | ||||||
| @ -546,42 +553,79 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet | |||||||
|             throw new ServiceException("未读取到数据"); |             throw new ServiceException("未读取到数据"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         Map<String, Map<String, Double>> dataMap =  dataList.stream().collect(Collectors.toMap(vo -> idCardEncryptorUtil.encrypt(vo.getIdCard()), DynamicSalaryData::getDailyAttendance)); |         // 2. 数据预处理 - 使用并行流提高处理速度 | ||||||
|         Set<String> cards = dataMap.keySet(); |         Map<String, Map<String, Double>> dataMap = dataList.parallelStream() | ||||||
|  |             .collect(Collectors.toMap( | ||||||
|  |                 vo -> idCardEncryptorUtil.encrypt(vo.getIdCard()), | ||||||
|  |                 DynamicSalaryData::getDailyAttendance | ||||||
|  |             )); | ||||||
|  |  | ||||||
|  |         Set<String> cards = dataMap.keySet(); | ||||||
|         month = dataList.getFirst().getMonth(); |         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(); | ||||||
|  |  | ||||||
|         //人员数据 |         // 3. 分批处理人员数据 - 避免一次性加载大量数据 | ||||||
|         List<SubConstructionUser> list = constructionUserService.list(Wrappers.<SubConstructionUser>lambdaQuery() |         List<SubConstructionUser> allUsers = new ArrayList<>(); | ||||||
|             .in(SubConstructionUser::getSfzNumber, cards)); |         int batchSize = 1000; // 批量查询大小 | ||||||
|  |  | ||||||
|         List<Long> userIds = list.stream().map(SubConstructionUser::getSysUserId).toList(); |         // 分批查询用户数据 | ||||||
|         //考勤数据 |         List<List<String>> cardBatches = new ArrayList<>(); | ||||||
|         List<BusAttendance> attendanceList = attendanceService |         List<String> cardList = new ArrayList<>(cards); | ||||||
|             .list(Wrappers.<BusAttendance>lambdaQuery() |         for (int i = 0; i < cardList.size(); i += batchSize) { | ||||||
|                 .in(BusAttendance::getUserId, userIds) |             int endIndex = Math.min(i + batchSize, cardList.size()); | ||||||
|  |             cardBatches.add(cardList.subList(i, endIndex)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (List<String> batchCards : cardBatches) { | ||||||
|  |             List<SubConstructionUser> batchUsers = constructionUserService.list( | ||||||
|  |                 Wrappers.<SubConstructionUser>lambdaQuery() | ||||||
|  |                     .in(SubConstructionUser::getSfzNumber, batchCards) | ||||||
|  |             ); | ||||||
|  |             allUsers.addAll(batchUsers); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (CollUtil.isEmpty(allUsers)) { | ||||||
|  |             throw new ServiceException("未找到匹配的人员信息"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 4. 分批处理考勤数据 - 避免内存溢出 | ||||||
|  |         List<Long> userIds = allUsers.stream() | ||||||
|  |             .map(SubConstructionUser::getSysUserId) | ||||||
|  |             .distinct() | ||||||
|  |             .collect(Collectors.toList()); | ||||||
|  |  | ||||||
|  |         Map<String, BigDecimal> attendanceSalaryMap = new ConcurrentHashMap<>(); | ||||||
|  |  | ||||||
|  |         // 分批查询考勤数据 | ||||||
|  |         for (int i = 0; i < userIds.size(); i += batchSize) { | ||||||
|  |             int endIndex = Math.min(i + batchSize, userIds.size()); | ||||||
|  |             List<Long> batchUserIds = userIds.subList(i, endIndex); | ||||||
|  |  | ||||||
|  |             List<BusAttendance> batchAttendanceList = attendanceService.list( | ||||||
|  |                 Wrappers.<BusAttendance>lambdaQuery() | ||||||
|  |                     .in(BusAttendance::getUserId, batchUserIds) | ||||||
|                     .between(BusAttendance::getClockDate, start, end) |                     .between(BusAttendance::getClockDate, start, end) | ||||||
|             ); |             ); | ||||||
|         // 将 attendanceList 转换为 Map<String, BigDecimal> 格式 |  | ||||||
|         Map<String, BigDecimal> attendanceSalaryMap = new HashMap<>(); |  | ||||||
|  |  | ||||||
|         // 按 userId+日期 分组,只保留每组的第一条记录的salary |             // 并行处理考勤数据 | ||||||
|         attendanceList.stream() |             batchAttendanceList.parallelStream() | ||||||
|                 .collect(Collectors.groupingBy( |                 .collect(Collectors.groupingBy( | ||||||
|                     attendance -> attendance.getUserId() + "_" + attendance.getClockDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), |                     attendance -> attendance.getUserId() + "_" + attendance.getClockDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), | ||||||
|                     Collectors.toList() |                     Collectors.toList() | ||||||
|                 )) |                 )) | ||||||
|                 .forEach((key, dateList) -> attendanceSalaryMap.put(key, dateList.getFirst().getSalary())); |                 .forEach((key, dateList) -> attendanceSalaryMap.put(key, dateList.getFirst().getSalary())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 5. 批量构建数据 - 使用并行处理 | ||||||
|  |         List<SubUserSalaryDetail> allAddList = Collections.synchronizedList(new ArrayList<>()); | ||||||
|  |  | ||||||
|         List<SubUserSalaryDetail> addList = new ArrayList<>(); |         // 并行处理用户数据构建 | ||||||
|         for(SubConstructionUser constructionUser : list){ |         allUsers.parallelStream().forEach(constructionUser -> { | ||||||
|             Map<String, Double> stringIntegerMap = dataMap.get(constructionUser.getSfzNumber()); |             Map<String, Double> userAttendanceMap = dataMap.get(constructionUser.getSfzNumber()); | ||||||
|             if(stringIntegerMap != null){ |             if(userAttendanceMap != null){ | ||||||
|                 for(Map.Entry<String, Double> entry : stringIntegerMap.entrySet()){ |                 userAttendanceMap.entrySet().parallelStream().forEach(entry -> { | ||||||
|                     String key = entry.getKey(); |                     String key = entry.getKey(); | ||||||
|                     Double value = entry.getValue(); |                     Double value = entry.getValue(); | ||||||
|  |  | ||||||
| @ -600,16 +644,35 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet | |||||||
|                     } |                     } | ||||||
|                     subUserSalaryDetail.setDailySalary(bigDecimal); |                     subUserSalaryDetail.setDailySalary(bigDecimal); | ||||||
|                     subUserSalaryDetail.setTotalSalary(bigDecimal.multiply(new BigDecimal(value.toString()))); |                     subUserSalaryDetail.setTotalSalary(bigDecimal.multiply(new BigDecimal(value.toString()))); | ||||||
|                     addList.add(subUserSalaryDetail); |  | ||||||
|  |                     allAddList.add(subUserSalaryDetail); | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // 6. 批量删除和插入 - 分批处理避免数据库压力 | ||||||
|  |         if (CollUtil.isNotEmpty(allAddList)) { | ||||||
|  |             // 批量删除现有数据 | ||||||
|  |             for (int i = 0; i < userIds.size(); i += batchSize) { | ||||||
|  |                 int endIndex = Math.min(i + batchSize, userIds.size()); | ||||||
|  |                 List<Long> deleteUserIds = userIds.subList(i, endIndex); | ||||||
|  |  | ||||||
|  |                 baseMapper.delete(Wrappers.<SubUserSalaryDetail>lambdaQuery() | ||||||
|  |                     .in(SubUserSalaryDetail::getUserId, deleteUserIds) | ||||||
|  |                     .between(SubUserSalaryDetail::getReportDate, start, end) | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // 批量插入新数据 - 使用自定义批量插入方法 | ||||||
|  |             for (int i = 0; i < allAddList.size(); i += batchSize) { | ||||||
|  |                 int endIndex = Math.min(i + batchSize, allAddList.size()); | ||||||
|  |                 List<SubUserSalaryDetail> insertBatch = allAddList.subList(i, endIndex); | ||||||
|  |                 baseMapper.insertAll(insertBatch,LoginHelper.getUserId()); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         } |         long endTime = System.currentTimeMillis(); | ||||||
|         baseMapper.delete(Wrappers.<SubUserSalaryDetail>lambdaQuery() |         log.info("工资数据导入完成,耗时: {} ms,处理数据: {} 条", (endTime - startTime), allAddList.size()); | ||||||
|             .in(SubUserSalaryDetail::getUserId, userIds) |  | ||||||
|             .between(SubUserSalaryDetail::getReportDate, start, end) |  | ||||||
|         ); |  | ||||||
|         baseMapper.insertBatch(addList); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -17,6 +17,7 @@ import lombok.extern.slf4j.Slf4j; | |||||||
| import org.apache.poi.hssf.usermodel.HSSFWorkbook; | import org.apache.poi.hssf.usermodel.HSSFWorkbook; | ||||||
| import org.apache.poi.ss.usermodel.*; | import org.apache.poi.ss.usermodel.*; | ||||||
| import org.apache.poi.ss.util.CellRangeAddress; | import org.apache.poi.ss.util.CellRangeAddress; | ||||||
|  | import org.apache.poi.xssf.usermodel.XSSFWorkbook; | ||||||
| import org.dromara.common.core.constant.DateConstant; | import org.dromara.common.core.constant.DateConstant; | ||||||
| import org.dromara.common.core.constant.HttpStatus; | import org.dromara.common.core.constant.HttpStatus; | ||||||
| import org.dromara.common.core.exception.ServiceException; | import org.dromara.common.core.exception.ServiceException; | ||||||
| @ -1041,7 +1042,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | |||||||
|             AttendanceUserDataDetailVo detailVo = new AttendanceUserDataDetailVo(); |             AttendanceUserDataDetailVo detailVo = new AttendanceUserDataDetailVo(); | ||||||
|             detailVo.setClockDate(key); |             detailVo.setClockDate(key); | ||||||
|             detailVo.setWeek(key.getDayOfWeek().getValue()); |             detailVo.setWeek(key.getDayOfWeek().getValue()); | ||||||
|             detailVo.setWorkDay(value.size()*0.5); |             detailVo.setWorkDay(value.size() * 0.5); | ||||||
|             workList.add(detailVo); |             workList.add(detailVo); | ||||||
|         } |         } | ||||||
|         vo.setWork(workList); |         vo.setWork(workList); | ||||||
| @ -1178,15 +1179,25 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void getExportList(AttendanceExportDto dto, HttpServletResponse response) { |     public void getExportList(AttendanceExportDto dto, HttpServletResponse response) { | ||||||
|  |         try (OutputStream outputStream = response.getOutputStream()) { | ||||||
|             BusProject project = projectService.getById(dto.getProjectId()); |             BusProject project = projectService.getById(dto.getProjectId()); | ||||||
|  |  | ||||||
|             if (project == null) { |             if (project == null) { | ||||||
|                 throw new ServiceException("项目不存在"); |                 throw new ServiceException("项目不存在"); | ||||||
|             } |             } | ||||||
|  |             String clockDate = dto.getClockDate(); | ||||||
|  |             YearMonth yearMonth = YearMonth.parse(clockDate, DateTimeFormatter.ofPattern("yyyy-MM")); | ||||||
|  |             LocalDate start = yearMonth.atDay(1); | ||||||
|  |             LocalDate end = yearMonth.atEndOfMonth(); | ||||||
|  |  | ||||||
|  |             LocalDateTime startTime = LocalDateTime.of(yearMonth.atDay(1), LocalTime.MIN); // 00:00:00 | ||||||
|  |             LocalDateTime endTime = LocalDateTime.of(yearMonth.atEndOfMonth(), LocalTime.MAX); | ||||||
|  |  | ||||||
|             List<SubConstructionUser> list = constructionUserService.list(Wrappers.lambdaQuery(SubConstructionUser.class) |             List<SubConstructionUser> list = constructionUserService.list(Wrappers.lambdaQuery(SubConstructionUser.class) | ||||||
|                 .eq(SubConstructionUser::getProjectId, dto.getProjectId()) |                 .eq(SubConstructionUser::getProjectId, dto.getProjectId()) | ||||||
|  |                 .isNotNull(SubConstructionUser::getEntryDate) | ||||||
|  |                 .and(wrapper -> wrapper.between(SubConstructionUser::getLeaveDate, startTime, endTime).or() | ||||||
|  |                     .isNull(SubConstructionUser::getLeaveDate)) | ||||||
|                 .eq(dto.getTeamId() != null, SubConstructionUser::getTeamId, dto.getTeamId()) |                 .eq(dto.getTeamId() != null, SubConstructionUser::getTeamId, dto.getTeamId()) | ||||||
|                 .eq(StrUtil.isNotBlank(dto.getTypeOfWork()), SubConstructionUser::getTypeOfWork, dto.getTypeOfWork()) |                 .eq(StrUtil.isNotBlank(dto.getTypeOfWork()), SubConstructionUser::getTypeOfWork, dto.getTypeOfWork()) | ||||||
|                 .like(StringUtils.isNotBlank(dto.getUserName()), SubConstructionUser::getUserName, dto.getUserName()) |                 .like(StringUtils.isNotBlank(dto.getUserName()), SubConstructionUser::getUserName, dto.getUserName()) | ||||||
| @ -1197,11 +1208,6 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             List<Long> userIds = list.stream().map(SubConstructionUser::getSysUserId).toList(); |             List<Long> userIds = list.stream().map(SubConstructionUser::getSysUserId).toList(); | ||||||
|         String clockDate = dto.getClockDate(); |  | ||||||
|  |  | ||||||
|         YearMonth yearMonth = YearMonth.parse(clockDate, DateTimeFormatter.ofPattern("yyyy-MM")); |  | ||||||
|         LocalDate start = yearMonth.atDay(1); |  | ||||||
|         LocalDate end = yearMonth.atEndOfMonth(); |  | ||||||
|  |  | ||||||
|             int daysInMonth = yearMonth.lengthOfMonth(); // 动态获取天数 |             int daysInMonth = yearMonth.lengthOfMonth(); // 动态获取天数 | ||||||
|  |  | ||||||
| @ -1213,7 +1219,8 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | |||||||
|             Map<Long, List<SubConstructionUser>> teamUserMap = list.stream() |             Map<Long, List<SubConstructionUser>> teamUserMap = list.stream() | ||||||
|                 .collect(Collectors.groupingBy(user -> |                 .collect(Collectors.groupingBy(user -> | ||||||
|                     user.getTeamId() != null ? user.getTeamId() : 0L)); |                     user.getTeamId() != null ? user.getTeamId() : 0L)); | ||||||
|         Workbook workbook = new HSSFWorkbook(); |  | ||||||
|  |             Workbook workbook = new XSSFWorkbook(); | ||||||
|  |  | ||||||
|             for (Map.Entry<Long, List<SubConstructionUser>> entry : teamUserMap.entrySet()) { |             for (Map.Entry<Long, List<SubConstructionUser>> entry : teamUserMap.entrySet()) { | ||||||
|                 Long teamId = entry.getKey(); |                 Long teamId = entry.getKey(); | ||||||
| @ -1302,7 +1309,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | |||||||
|                 int rowIndex = 3; |                 int rowIndex = 3; | ||||||
|                 for (SubConstructionUser user : users) { |                 for (SubConstructionUser user : users) { | ||||||
|                     Row row = sheet.createRow(rowIndex++); |                     Row row = sheet.createRow(rowIndex++); | ||||||
|                 writeDataRow(row, user, attendanceList, start, end, daysInMonth,borderStyle,numberBorderStyle); |                     writeDataRow(row, user, attendanceList, start, end, daysInMonth, borderStyle, numberBorderStyle); | ||||||
|  |  | ||||||
|                     // 设置数据行边框 |                     // 设置数据行边框 | ||||||
|                     for (int i = 0; i < totalColumns; i++) { |                     for (int i = 0; i < totalColumns; i++) { | ||||||
| @ -1398,7 +1405,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | |||||||
|  |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|         try (OutputStream outputStream = response.getOutputStream()) { |  | ||||||
|             workbook.write(outputStream); |             workbook.write(outputStream); | ||||||
|             workbook.close(); |             workbook.close(); | ||||||
|         } catch (IOException e) { |         } catch (IOException e) { | ||||||
| @ -1417,6 +1424,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | |||||||
|         } |         } | ||||||
|         return columnName.toString(); |         return columnName.toString(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private CellStyle createBorderStyle(Workbook workbook) { |     private CellStyle createBorderStyle(Workbook workbook) { | ||||||
|         CellStyle style = workbook.createCellStyle(); |         CellStyle style = workbook.createCellStyle(); | ||||||
|         style.setBorderTop(BorderStyle.THIN); |         style.setBorderTop(BorderStyle.THIN); | ||||||
| @ -1425,10 +1433,11 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | |||||||
|         style.setBorderRight(BorderStyle.THIN); |         style.setBorderRight(BorderStyle.THIN); | ||||||
|         return style; |         return style; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private CellStyle createNumberBorderStyle(Workbook workbook) { |     private CellStyle createNumberBorderStyle(Workbook workbook) { | ||||||
|         CellStyle style = workbook.createCellStyle(); |         CellStyle style = workbook.createCellStyle(); | ||||||
|         DataFormat format = workbook.createDataFormat(); |         DataFormat format = workbook.createDataFormat(); | ||||||
|         style.setDataFormat(format.getFormat("0")); // 强制显示 0 |         style.setDataFormat(format.getFormat("0.0")); // 显示一位小数 | ||||||
|         style.setBorderTop(BorderStyle.THIN); |         style.setBorderTop(BorderStyle.THIN); | ||||||
|         style.setBorderBottom(BorderStyle.THIN); |         style.setBorderBottom(BorderStyle.THIN); | ||||||
|         style.setBorderLeft(BorderStyle.THIN); |         style.setBorderLeft(BorderStyle.THIN); | ||||||
| @ -1478,8 +1487,8 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | |||||||
|         row.createCell(2 + daysInMonth + 3).setCellValue("签字"); |         row.createCell(2 + daysInMonth + 3).setCellValue("签字"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void writeDataRow(Row row, SubConstructionUser user, List<BusAttendance> attendanceList, LocalDate start, LocalDate end, int daysInMonth,CellStyle borderStyle,CellStyle numberBorderStyle) { |     private void writeDataRow(Row row, SubConstructionUser user, List<BusAttendance> attendanceList, LocalDate start, LocalDate end, int daysInMonth, CellStyle borderStyle, CellStyle numberBorderStyle) { | ||||||
|         int index = row.getRowNum()-2; |         int index = row.getRowNum() - 2; | ||||||
|         row.createCell(0).setCellValue(index); |         row.createCell(0).setCellValue(index); | ||||||
|         row.createCell(1).setCellValue(user.getUserName()); |         row.createCell(1).setCellValue(user.getUserName()); | ||||||
|         row.createCell(2).setCellValue(idCardEncryptorUtil.decrypt(user.getSfzNumber())); |         row.createCell(2).setCellValue(idCardEncryptorUtil.decrypt(user.getSfzNumber())); | ||||||
| @ -1502,7 +1511,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | |||||||
|         sumCell.setCellFormula(formula); |         sumCell.setCellFormula(formula); | ||||||
|         sumCell.setCellStyle(borderStyle); |         sumCell.setCellStyle(borderStyle); | ||||||
|  |  | ||||||
|         String leaveStatus = "0".equals(user.getExitStatus()) ? "未离场" : "已离场"; |         String leaveStatus = user.getLeaveDate() != null ? "已离场" : "未离场"; | ||||||
|         row.createCell(2 + daysInMonth + 2).setCellValue(leaveStatus); |         row.createCell(2 + daysInMonth + 2).setCellValue(leaveStatus); | ||||||
|  |  | ||||||
|         row.createCell(2 + daysInMonth + 3).setCellValue(""); |         row.createCell(2 + daysInMonth + 3).setCellValue(""); | ||||||
| @ -1541,7 +1550,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | |||||||
|  |  | ||||||
|         while (!current.isAfter(end)) { |         while (!current.isAfter(end)) { | ||||||
|             Double value = getAttendanceValue(userId, current, attendanceList); |             Double value = getAttendanceValue(userId, current, attendanceList); | ||||||
|             total+=value; |             total += value; | ||||||
|             current = current.plusDays(1); |             current = current.plusDays(1); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 zt
					zt