AttendanceStatisticsServiceImpl.java 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. package com.xjrsoft.module.attendance.service.impl;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  4. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  5. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  6. import com.github.yulichang.base.MPJBaseServiceImpl;
  7. import com.github.yulichang.wrapper.MPJLambdaWrapper;
  8. import com.google.gson.JsonArray;
  9. import com.google.gson.JsonElement;
  10. import com.google.gson.JsonObject;
  11. import com.google.gson.JsonParser;
  12. import com.xjrsoft.common.enums.DeleteMark;
  13. import com.xjrsoft.common.exception.MyException;
  14. import com.xjrsoft.common.mybatis.SqlRunnerAdapter;
  15. import com.xjrsoft.common.utils.VoToColumnUtil;
  16. import com.xjrsoft.module.attendance.dto.AddAttendanceStatisticsDto;
  17. import com.xjrsoft.module.attendance.dto.AttendanceStatisticsPageDto;
  18. import com.xjrsoft.module.attendance.dto.AttendanceStatisticsRecordDto;
  19. import com.xjrsoft.module.attendance.entity.AttendanceRuleDetails;
  20. import com.xjrsoft.module.attendance.entity.AttendanceStatistics;
  21. import com.xjrsoft.module.attendance.entity.AttendanceStatisticsRecord;
  22. import com.xjrsoft.module.attendance.entity.AttendanceUserRelation;
  23. import com.xjrsoft.module.attendance.entity.TeacherAttendanceRecord;
  24. import com.xjrsoft.module.attendance.mapper.AttendanceStatisticsMapper;
  25. import com.xjrsoft.module.attendance.service.IAttendanceRuleCategoryService;
  26. import com.xjrsoft.module.attendance.service.IAttendanceStatisticsRecordService;
  27. import com.xjrsoft.module.attendance.service.IAttendanceStatisticsService;
  28. import com.xjrsoft.module.attendance.service.ITeacherAttendanceRecordService;
  29. import com.xjrsoft.module.attendance.vo.AttendanceRuleDetailsUserVo;
  30. import com.xjrsoft.module.attendance.vo.AttendanceStatisticsPageVo;
  31. import com.xjrsoft.module.attendance.vo.AttendanceStatisticsRecordVo;
  32. import com.xjrsoft.module.hikvision.util.OutInRecordUtil;
  33. import com.xjrsoft.module.holiday.entity.HolidayDate;
  34. import com.xjrsoft.module.holiday.service.IHolidayDateService;
  35. import com.xjrsoft.module.job.AttendanceRecordTask;
  36. import com.xjrsoft.module.organization.entity.User;
  37. import com.xjrsoft.module.organization.service.IUserService;
  38. import com.xjrsoft.module.organization.vo.UserIdDeptNameVo;
  39. import com.xjrsoft.module.outint.service.ICarOutInRecordService;
  40. import com.xjrsoft.module.outint.service.ITeacherOutInRecordService;
  41. import com.xjrsoft.module.teacher.entity.BaseTeacher;
  42. import com.xjrsoft.module.teacher.mapper.FaceImportMapper;
  43. import com.xjrsoft.module.teacher.service.IWfTeacherleaveService;
  44. import lombok.AllArgsConstructor;
  45. import me.zhyd.oauth.log.Log;
  46. import org.apache.poi.ss.usermodel.Cell;
  47. import org.apache.poi.ss.usermodel.CellStyle;
  48. import org.apache.poi.ss.usermodel.Font;
  49. import org.apache.poi.ss.usermodel.HorizontalAlignment;
  50. import org.apache.poi.ss.usermodel.IndexedColors;
  51. import org.apache.poi.ss.usermodel.Row;
  52. import org.apache.poi.ss.usermodel.Sheet;
  53. import org.apache.poi.ss.usermodel.VerticalAlignment;
  54. import org.apache.poi.ss.usermodel.Workbook;
  55. import org.apache.poi.ss.util.CellRangeAddress;
  56. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  57. import org.springframework.stereotype.Service;
  58. import org.springframework.transaction.annotation.Transactional;
  59. import java.io.ByteArrayOutputStream;
  60. import java.io.IOException;
  61. import java.text.ParseException;
  62. import java.time.LocalDate;
  63. import java.time.LocalDateTime;
  64. import java.time.LocalTime;
  65. import java.time.format.DateTimeFormatter;
  66. import java.time.temporal.ChronoUnit;
  67. import java.util.ArrayList;
  68. import java.util.Arrays;
  69. import java.util.Date;
  70. import java.util.HashMap;
  71. import java.util.List;
  72. import java.util.Map;
  73. import java.util.Objects;
  74. import java.util.stream.Collectors;
  75. /**
  76. * @title: 考勤统计
  77. * @Author dzx
  78. * @Date: 2024-10-19
  79. * @Version 1.0
  80. */
  81. @Service
  82. @AllArgsConstructor
  83. public class AttendanceStatisticsServiceImpl extends MPJBaseServiceImpl<AttendanceStatisticsMapper, AttendanceStatistics> implements IAttendanceStatisticsService {
  84. private final AttendanceStatisticsMapper statisticsMapper;
  85. private final IAttendanceStatisticsRecordService statisticsRecordService;
  86. private final ITeacherAttendanceRecordService recordService;
  87. private final IUserService userService;
  88. private final IAttendanceRuleCategoryService ruleCategoryService;
  89. private final IHolidayDateService holidayDateService;
  90. private final FaceImportMapper faceImportMapper;
  91. private final IWfTeacherleaveService wfTeacherleaveService;
  92. private final ITeacherOutInRecordService outInRecordService;
  93. private final ICarOutInRecordService carOutInRecordService;
  94. @Override
  95. @Transactional(rollbackFor = Exception.class)
  96. public Boolean add(AddAttendanceStatisticsDto dto) {
  97. AttendanceStatistics statistics = BeanUtil.toBean(dto, AttendanceStatistics.class);
  98. statistics.setCreateDate(new Date());
  99. statistics.setStatus(0);
  100. statisticsMapper.insert(statistics);
  101. //1、同步出入记录的数据
  102. //2、固化考勤数据
  103. refreshRecord(statistics.getId());
  104. return true;
  105. }
  106. @Override
  107. @Transactional(rollbackFor = Exception.class)
  108. public Boolean update(AttendanceStatistics attendanceStatistics) {
  109. statisticsMapper.updateById(attendanceStatistics);
  110. //********************************* AttendanceStatisticsRecord 增删改 开始 *******************************************/
  111. {
  112. // 查出所有子级的id
  113. List<AttendanceStatisticsRecord> attendanceStatisticsRecordList = statisticsRecordService.list(Wrappers.lambdaQuery(AttendanceStatisticsRecord.class).eq(AttendanceStatisticsRecord::getAttendanceStatisticsId, attendanceStatistics.getId()).select(AttendanceStatisticsRecord::getId));
  114. List<Long> attendanceStatisticsRecordIds = attendanceStatisticsRecordList.stream().map(AttendanceStatisticsRecord::getId).collect(Collectors.toList());
  115. //原有子表单 没有被删除的主键
  116. List<Long> attendanceStatisticsRecordOldIds = attendanceStatistics.getAttendanceStatisticsRecordList().stream().map(AttendanceStatisticsRecord::getId).filter(Objects::nonNull).collect(Collectors.toList());
  117. //找到需要删除的id
  118. List<Long> attendanceStatisticsRecordRemoveIds = attendanceStatisticsRecordIds.stream().filter(item -> !attendanceStatisticsRecordOldIds.contains(item)).collect(Collectors.toList());
  119. for (AttendanceStatisticsRecord attendanceStatisticsRecord : attendanceStatistics.getAttendanceStatisticsRecordList()) {
  120. //如果不等于空则修改
  121. if (attendanceStatisticsRecord.getId() != null) {
  122. statisticsRecordService.updateById(attendanceStatisticsRecord);
  123. }
  124. //如果等于空 则新增
  125. else {
  126. //已经不存在的id 删除
  127. attendanceStatisticsRecord.setAttendanceStatisticsId(attendanceStatistics.getId());
  128. statisticsRecordService.save(attendanceStatisticsRecord);
  129. }
  130. }
  131. //已经不存在的id 删除
  132. if (attendanceStatisticsRecordRemoveIds.size() > 0) {
  133. statisticsRecordService.removeBatchByIds(attendanceStatisticsRecordRemoveIds);
  134. }
  135. }
  136. //********************************* AttendanceStatisticsRecord 增删改 结束 *******************************************/
  137. return true;
  138. }
  139. @Override
  140. @Transactional(rollbackFor = Exception.class)
  141. public Boolean delete(List<Long> ids) {
  142. statisticsMapper.deleteBatchIds(ids);
  143. statisticsRecordService.remove(Wrappers.lambdaQuery(AttendanceStatisticsRecord.class).in(AttendanceStatisticsRecord::getAttendanceStatisticsId, ids));
  144. return true;
  145. }
  146. @Override
  147. public Page<AttendanceStatisticsPageVo> getPage(Page<AttendanceStatisticsPageVo> page, AttendanceStatisticsPageDto dto) {
  148. return this.baseMapper.getPage(page, dto);
  149. }
  150. @Override
  151. public List<AttendanceStatisticsRecordVo> getRecordList(AttendanceStatisticsRecordDto dto) {
  152. return this.baseMapper.getRecordList(dto);
  153. }
  154. /**
  155. * 1、修改状态为统计中
  156. * 2、删除以前的数据
  157. * 3、重新计算数据并入库
  158. * 4、将状态改为统计完成
  159. */
  160. @Override
  161. @Transactional(rollbackFor = Exception.class)
  162. public Boolean refreshRecord(Long id) {
  163. String remarks = null;
  164. int personCount = 0;
  165. int attendanceDays = 0;
  166. try {
  167. //1、修改状态为统计中
  168. AttendanceStatistics statistics = this.getById(id);
  169. statistics.setStatus(0);
  170. statistics.setModifyDate(new Date());
  171. this.updateById(statistics);
  172. List<LocalDate> dateList2 = getDatesBetween(statistics.getStartDate(), statistics.getEndDate());
  173. LocalDateTime now = LocalDateTime.now();
  174. if (dateList2.contains(now.toLocalDate()) && (statistics.getTimePeriod() == 1 && now.toLocalTime().isBefore(LocalTime.MIDNIGHT))) {
  175. DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  176. String sql = "DELETE FROM teacher_attendance_record WHERE attendance_date = '" + now.toLocalDate().format(dtf) + "'" +
  177. " AND time_interval = " + statistics.getTimePeriod();
  178. SqlRunnerAdapter.db().delete(sql);
  179. OutInRecordUtil outInRecordUtil = new OutInRecordUtil();
  180. LocalDateTime startDateTime = now.withHour(0).withMinute(0).withSecond(0).withNano(0);
  181. LocalDateTime endDateTime = startDateTime.plusDays(1).plusSeconds(-1);
  182. Map<Long, AttendanceRuleDetailsUserVo> teacherRules = ruleCategoryService.getAllTeacherTodyRule(now.getDayOfWeek().name());
  183. try {
  184. //教师
  185. outInRecordUtil.getTeacherRecords(faceImportMapper, startDateTime, endDateTime, teacherRules);
  186. //拉取车辆数据
  187. outInRecordUtil.GetVehicleRecord(faceImportMapper, startDateTime, endDateTime);
  188. }catch (Exception e){
  189. Log.error(e.getMessage());
  190. }
  191. AttendanceRecordTask recordTask = new AttendanceRecordTask();
  192. recordTask.teacherAttendanceRecord(startDateTime, statistics.getTimePeriod(),
  193. userService,
  194. ruleCategoryService,
  195. holidayDateService,
  196. wfTeacherleaveService,
  197. outInRecordService,
  198. carOutInRecordService,
  199. recordService
  200. );
  201. }
  202. //2、删除以前的数据
  203. statisticsRecordService.remove(
  204. new QueryWrapper<AttendanceStatisticsRecord>().lambda()
  205. .eq(AttendanceStatisticsRecord::getAttendanceStatisticsId, id)
  206. );
  207. /**
  208. * 3、重新计算数据并入库
  209. */
  210. //3.1、根据考勤规则和统计的时间段查询这个规则下面所涉及到的老师
  211. List<User> userList = userService.list(
  212. new MPJLambdaWrapper<User>()
  213. .select(User::getId)
  214. .select(User.class, x -> VoToColumnUtil.fieldsToColumns(User.class).contains(x.getProperty()))
  215. .innerJoin(AttendanceUserRelation.class, AttendanceUserRelation::getUserId, User::getId)
  216. .innerJoin(BaseTeacher.class, BaseTeacher::getUserId, User::getId)
  217. .eq(AttendanceUserRelation::getAttendanceRuleCategoryId, statistics.getAttendanceRuleCategoryId())
  218. .eq(AttendanceUserRelation::getDeleteMark, DeleteMark.NODELETE.getCode())
  219. );
  220. if (userList.isEmpty()) {
  221. throw new MyException("该规则下无考勤人员");
  222. }
  223. List<Long> userIds = userList.stream().map(User::getId).collect(Collectors.toList());
  224. //查询固化的考勤数据
  225. List<TeacherAttendanceRecord> attendanceRecords = recordService.list(
  226. new QueryWrapper<TeacherAttendanceRecord>().lambda()
  227. .eq(TeacherAttendanceRecord::getTimeInterval, statistics.getTimePeriod())
  228. .in(TeacherAttendanceRecord::getUserId, userIds)
  229. .between(TeacherAttendanceRecord::getAttendanceDate, statistics.getStartDate(), statistics.getEndDate())
  230. .eq(TeacherAttendanceRecord::getDeleteMark, DeleteMark.NODELETE.getCode())
  231. );
  232. //3.2、准备相关需要的数据
  233. DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
  234. DateTimeFormatter timeDtf = DateTimeFormatter.ofPattern("HH:mm");
  235. Map<String, String> weekCnMap = initWeekCn();
  236. Map<String, String> weekEnMap = initWeekEn();
  237. List<UserIdDeptNameVo> teacherDeptName = userService.getTeacherDeptName();
  238. Map<Long, UserIdDeptNameVo> userDeptMap = teacherDeptName.stream().collect(Collectors.toMap(UserIdDeptNameVo::getId, x -> x));
  239. //3.3、查询这个考勤规则下面的所有细则并转换成map
  240. List<AttendanceRuleDetails> ruleList = ruleCategoryService.getRules(statistics.getAttendanceRuleCategoryId());
  241. Map<String, AttendanceRuleDetails> ruleMap = ruleList.stream().collect(Collectors.toMap(AttendanceRuleDetails::getDateType, x -> x));
  242. //3.4、计算出所有的天数
  243. List<LocalDate> dateList = getDatesBetween(statistics.getStartDate(), statistics.getEndDate(), ruleMap, weekEnMap);
  244. attendanceDays = dateList.size();
  245. //3.5、循环计算,并存入insertList,方便后续批量入库
  246. List<AttendanceStatisticsRecord> insertList = new ArrayList<>();
  247. List<String> leaveTypeList = new ArrayList<>();
  248. leaveTypeList.add("事假");
  249. leaveTypeList.add("公假");
  250. leaveTypeList.add("病假");
  251. leaveTypeList.add("会议活动");
  252. for (User user : userList) {
  253. AttendanceStatisticsRecord record = new AttendanceStatisticsRecord();
  254. record.setUserId(user.getId());
  255. record.setAttendanceStatisticsId(statistics.getId());
  256. Long normalCount = 0L;
  257. Long leaveCount = 0L;
  258. Long absenteeCount = 0L;
  259. JsonArray daysData = new JsonArray();
  260. int sortCode = 1;
  261. for (LocalDate localDate : dateList) {
  262. String dayOfWeekName = localDate.getDayOfWeek().name();
  263. AttendanceRuleDetails ruleDetails = ruleMap.get(weekEnMap.get(dayOfWeekName));
  264. if (ruleDetails != null && (ruleDetails.getIsAttendance() == null || ruleDetails.getIsAttendance() == 0)) {
  265. continue;
  266. }
  267. JsonObject daysJson = new JsonObject();
  268. daysJson.addProperty("date", localDate.format(dtf));
  269. daysJson.addProperty("week", weekCnMap.get(dayOfWeekName));
  270. String content = "";
  271. if (statistics.getTimePeriod() == 1) {
  272. content += "(" + ruleDetails.getAmStartTime().toLocalTime().format(timeDtf) + ")上班 ";
  273. } else if (statistics.getTimePeriod() == 2) {
  274. content += "(" + ruleDetails.getPmStartTime().toLocalTime().format(timeDtf) + ")上班 ";
  275. }
  276. List<TeacherAttendanceRecord> collect = attendanceRecords.stream()
  277. .filter(el -> el.getAttendanceDate().equals(localDate) && el.getUserId().equals(user.getId())
  278. && !"不考勤".equals(el.getAttendanceStatus())
  279. )
  280. .collect(Collectors.toList());
  281. for (TeacherAttendanceRecord attendanceRecord : collect) {
  282. if ("到校".equals(attendanceRecord.getAttendanceStatus())) {
  283. normalCount++;
  284. content += "正常(" + attendanceRecord.getRecordTime().toLocalTime().format(timeDtf) + ")";
  285. } else if (leaveTypeList.contains(attendanceRecord.getAttendanceStatus())) {
  286. leaveCount++;
  287. content += "请假";
  288. } else {
  289. absenteeCount++;
  290. content += "缺勤";
  291. }
  292. }
  293. daysJson.addProperty("content", content);
  294. daysJson.addProperty("sortCode", sortCode);
  295. daysData.add(daysJson);
  296. sortCode++;
  297. }
  298. record.setNormalCount(normalCount);
  299. record.setLeaveCount(leaveCount);
  300. record.setAbsenteeCount(absenteeCount);
  301. record.setDaysData(daysData.toString());
  302. UserIdDeptNameVo deptNameVo = userDeptMap.get(record.getUserId());
  303. if (deptNameVo != null) {
  304. record.setDeptIds(deptNameVo.getDeptIds());
  305. record.setDeptName(deptNameVo.getDeptName());
  306. }
  307. insertList.add(record);
  308. }
  309. //3.6、插入数据
  310. if (!insertList.isEmpty()) {
  311. statisticsRecordService.saveBatch(insertList);
  312. personCount = insertList.size();
  313. }
  314. return true;
  315. } catch (Exception e) {
  316. Log.error(e.getMessage(), e);
  317. if (e.getClass().equals(MyException.class)) {
  318. throw new MyException(e.getMessage());
  319. }else if(e.getClass().equals(ParseException.class) || e.getClass().equals(ClassCastException.class)){
  320. remarks = "未能拉取海康数据,统计可能不准确";
  321. } else {
  322. throw new MyException("刷新出错,请联系管理员");
  323. }
  324. }finally {
  325. //4、将状态改为统计完成
  326. AttendanceStatistics statistics = this.getById(id);
  327. statistics.setStatus(1);
  328. statistics.setModifyDate(new Date());
  329. statistics.setPersonCount(personCount);
  330. statistics.setAttendanceDays(attendanceDays);
  331. statistics.setRemarks(remarks);
  332. this.updateById(statistics);
  333. return true;
  334. }
  335. }
  336. private List<LocalDate> getDatesBetween(LocalDate startDate, LocalDate endDate, Map<String, AttendanceRuleDetails> ruleMap, Map<String, String> weekEnMap) {
  337. List<LocalDate> dates = new ArrayList<>();
  338. List<Integer> list = Arrays.asList(1, 3);
  339. List<HolidayDate> holidayDates = holidayDateService.list(
  340. new QueryWrapper<HolidayDate>().lambda()
  341. .in(HolidayDate::getStatus, list)
  342. );
  343. List<String> holidayDateList = holidayDates.stream().map(HolidayDate::getDate).collect(Collectors.toList());
  344. List<HolidayDate> workHolidayDates = holidayDateService.list(
  345. new QueryWrapper<HolidayDate>().lambda()
  346. .eq(HolidayDate::getStatus, 2)
  347. );
  348. List<String> workHolidayDateListg = workHolidayDates.stream().map(HolidayDate::getDate).collect(Collectors.toList());
  349. DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  350. List<String> weekList = Arrays.asList("SUNDAY", "SATURDAY");
  351. long numOfDaysBetween = ChronoUnit.DAYS.between(startDate, endDate) + 1; // +1 包含结束日期
  352. for (long i = 0; i < numOfDaysBetween; i++) {
  353. LocalDate localDate = startDate.plusDays(i);
  354. AttendanceRuleDetails holidays = ruleMap.get("holidays");
  355. Integer holidaysIsAttendance = holidays.getIsAttendance();
  356. if (
  357. (
  358. (!workHolidayDateListg.contains(localDate.format(formatter)) && weekList.contains(localDate.getDayOfWeek().name()))
  359. || holidayDateList.contains(localDate.format(formatter)))
  360. && (holidaysIsAttendance == null || holidaysIsAttendance == 0)
  361. ) {
  362. continue;
  363. }
  364. AttendanceRuleDetails ruleDetails = ruleMap.get(weekEnMap.get(localDate.getDayOfWeek().name()));
  365. if (ruleDetails != null && ruleDetails.getIsAttendance() != null && ruleDetails.getIsAttendance() == 1) {
  366. dates.add(localDate);
  367. }
  368. }
  369. return dates;
  370. }
  371. private List<LocalDate> getDatesBetween(LocalDate startDate, LocalDate endDate) {
  372. List<LocalDate> dates = new ArrayList<>();
  373. long numOfDaysBetween = ChronoUnit.DAYS.between(startDate, endDate) + 1; // +1 包含结束日期
  374. for (long i = 0; i < numOfDaysBetween; i++) {
  375. LocalDate localDate = startDate.plusDays(i);
  376. dates.add(localDate);
  377. }
  378. return dates;
  379. }
  380. private Map<String, String> initWeekCn() {
  381. Map<String, String> result = new HashMap<>();
  382. result.put("MONDAY", "周一");
  383. result.put("TUESDAY", "周二");
  384. result.put("WEDNESDAY", "周三");
  385. result.put("THURSDAY", "周四");
  386. result.put("FRIDAY", "周五");
  387. result.put("SATURDAY", "周六");
  388. result.put("SUNDAY", "周日");
  389. return result;
  390. }
  391. private Map<String, String> initWeekEn() {
  392. Map<String, String> result = new HashMap<>();
  393. result.put("MONDAY", "monday");
  394. result.put("TUESDAY", "tuesday");
  395. result.put("WEDNESDAY", "wednesday");
  396. result.put("THURSDAY", "thursday");
  397. result.put("FRIDAY", "friday");
  398. result.put("SATURDAY", "saturday");
  399. result.put("SUNDAY", "sunday");
  400. return result;
  401. }
  402. @Override
  403. public byte[] recordExport(Long id) throws IOException {
  404. AttendanceStatistics statistics = this.getById(id);
  405. List<AttendanceStatisticsRecordVo> recordList = this.getRecordList(new AttendanceStatisticsRecordDto() {{
  406. setId(id);
  407. }});
  408. List<ArrayList<String>> dataList = new ArrayList<>();
  409. int sortCode = 1;
  410. JsonParser parser = new JsonParser();
  411. List<String> dateList = new ArrayList<>();
  412. List<String> weekList = new ArrayList<>();
  413. for (AttendanceStatisticsRecordVo recordVo : recordList) {
  414. ArrayList<String> data = new ArrayList<>();
  415. data.add(sortCode + "");
  416. data.add(recordVo.getName());
  417. data.add(recordVo.getUserName());
  418. data.add(recordVo.getDeptName());
  419. data.add(statistics.getAttendanceDays() + "");
  420. data.add(recordVo.getNormalCount() + "");
  421. data.add(recordVo.getLeaveCount() + "");
  422. data.add(recordVo.getAbsenteeCount() + "");
  423. JsonArray daysData = parser.parse(recordVo.getDaysData()).getAsJsonArray();
  424. for (JsonElement daysDatum : daysData) {
  425. JsonObject daysJson = daysDatum.getAsJsonObject();
  426. data.add(daysJson.get("content").getAsString());
  427. if (!dateList.contains(daysJson.get("date").getAsString())) {
  428. dateList.add(daysJson.get("date").getAsString());
  429. }
  430. if (!weekList.contains(daysJson.get("week").getAsString())) {
  431. weekList.add(daysJson.get("week").getAsString());
  432. }
  433. }
  434. dataList.add(data);
  435. sortCode++;
  436. }
  437. int allColumn = 8 + statistics.getAttendanceDays();//总列数
  438. Workbook workbook = new XSSFWorkbook();
  439. // 创建一个工作表(sheet)
  440. String sheetName = "数据";
  441. Sheet sheet = workbook.createSheet(sheetName);
  442. // 第一行表头
  443. createFirstTitle(workbook, sheet, allColumn - 1);
  444. // 第二行表头
  445. createSecondTitle(workbook, sheet, statistics, 7);
  446. // 第三行表头
  447. createThirdTitle(workbook, sheet, weekList);
  448. // 第四行表头
  449. createFourthTitle(workbook, sheet, 8, dateList);
  450. //生成数据
  451. int dataRowNumber = 4;
  452. for (ArrayList<String> rowData : dataList) {
  453. Row dataRow = sheet.createRow(dataRowNumber);
  454. for (int i = 0; i < rowData.size(); i++) {
  455. if (i > 7) {
  456. sheet.autoSizeColumn(i);
  457. }
  458. String content = rowData.get(i);
  459. Font font = workbook.createFont();
  460. font.setBold(false);// 设置为粗体
  461. font.setFontName("宋体");
  462. if (content != null && content.contains("缺勤")) {
  463. font.setColor(IndexedColors.RED.getIndex()); // 设置字体颜色为红色
  464. } else if (content != null && content.contains("正常")) {
  465. font.setColor(IndexedColors.BLUE.getIndex()); // 设置字体颜色为蓝色
  466. } else if (content != null && content.contains("请假")) {
  467. font.setColor(IndexedColors.BROWN.getIndex()); // 设置字体颜色为橙色
  468. }
  469. font.setFontHeightInPoints((short) 12);
  470. CellStyle cellStyle = workbook.createCellStyle();
  471. cellStyle.setFont(font); // 将字体应用到样式
  472. cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
  473. if (i > 3 && i < 8) {
  474. cellStyle.setAlignment(HorizontalAlignment.CENTER);
  475. }
  476. Cell row1cell2 = dataRow.createCell(i);
  477. row1cell2.setCellValue(content);
  478. row1cell2.setCellStyle(cellStyle);
  479. }
  480. dataRowNumber++;
  481. }
  482. //写入文件
  483. ByteArrayOutputStream bot = new ByteArrayOutputStream();
  484. workbook.write(bot);
  485. return bot.toByteArray();
  486. }
  487. /**
  488. * 创建第一行表头
  489. *
  490. * @param mergeCoulmn 合并后面多少列(不包含自己)
  491. */
  492. void createFirstTitle(Workbook workbook, Sheet sheet, int mergeCoulmn) {
  493. int rowNumber = 0;
  494. Font font = workbook.createFont();
  495. font.setBold(true);// 设置为粗体
  496. font.setFontName("宋体");
  497. //font.setColor(IndexedColors.RED.getIndex()); // 设置字体颜色为红色
  498. font.setFontHeightInPoints((short) 24);
  499. CellStyle cellStyle = workbook.createCellStyle();
  500. cellStyle.setFont(font); // 将字体应用到样式
  501. cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
  502. cellStyle.setAlignment(HorizontalAlignment.CENTER);
  503. Row row = sheet.createRow(rowNumber);
  504. //合并第一行的列
  505. sheet.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber, 0, mergeCoulmn));
  506. //3、处理表头
  507. String title1 = "坐班考勤 - 汇总统计";
  508. // 创建单元格并设置值
  509. Cell cell = row.createCell(0);
  510. cell.setCellValue(title1);
  511. cell.setCellStyle(cellStyle);
  512. }
  513. /**
  514. * 生成第二行表头
  515. *
  516. * @param mergeCoulmn 合并后面多少列(不包含自己)
  517. */
  518. void createSecondTitle(Workbook workbook, Sheet sheet, AttendanceStatistics statistics, int mergeCoulmn) {
  519. int rowNumber = 1;
  520. Font font = workbook.createFont();
  521. font.setFontName("宋体");
  522. font.setFontHeightInPoints((short) 12);
  523. CellStyle cellStyle = workbook.createCellStyle();
  524. cellStyle.setFont(font); // 将字体应用到样式
  525. cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
  526. cellStyle.setAlignment(HorizontalAlignment.CENTER);
  527. Row row1 = sheet.createRow(rowNumber);
  528. Cell row1cell1 = row1.createCell(0);
  529. DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  530. String content = "查询的考勤日期范围:" + statistics.getStartDate().format(dtf) + "至" + statistics.getEndDate() + ";";
  531. if (statistics.getTimePeriod() == 1) {
  532. content += "时间段:上午";
  533. } else if (statistics.getTimePeriod() == 2) {
  534. content += "时间段:下午";
  535. }
  536. row1cell1.setCellValue(content);
  537. row1cell1.setCellStyle(cellStyle);
  538. sheet.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber, 0, mergeCoulmn));
  539. }
  540. /**
  541. * 生成第三行表头
  542. * 表头:序号、姓名、工号、部门、正常考勤次数、请假次数、缺勤次数,后续周几
  543. *
  544. * @param weekList 周几
  545. */
  546. void createThirdTitle(Workbook workbook, Sheet sheet, List<String> weekList) {
  547. int rowNumber = 2;
  548. Font font = workbook.createFont();
  549. font.setFontName("宋体");
  550. font.setFontHeightInPoints((short) 12);
  551. CellStyle cellStyle = workbook.createCellStyle();
  552. cellStyle.setFont(font); // 将字体应用到样式
  553. cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
  554. cellStyle.setAlignment(HorizontalAlignment.CENTER);
  555. Row row1 = sheet.createRow(rowNumber);
  556. Cell row1cell1 = row1.createCell(0);
  557. row1cell1.setCellValue("序号");
  558. row1cell1.setCellStyle(cellStyle);
  559. sheet.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber + 1, 0, 0));
  560. Cell row1cell2 = row1.createCell(1);
  561. row1cell2.setCellValue("姓名");
  562. row1cell2.setCellStyle(cellStyle);
  563. sheet.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber + 1, 1, 1));
  564. Cell row1cell3 = row1.createCell(2);
  565. row1cell3.setCellValue("工号");
  566. row1cell3.setCellStyle(cellStyle);
  567. sheet.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber + 1, 2, 2));
  568. Cell row1cell4 = row1.createCell(3);
  569. row1cell4.setCellValue("组织机构");
  570. row1cell4.setCellStyle(cellStyle);
  571. sheet.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber + 1, 3, 3));
  572. Cell row1cell8 = row1.createCell(4);
  573. row1cell8.setCellValue("考勤组应出勤天数");
  574. row1cell8.setCellStyle(cellStyle);
  575. sheet.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber + 1, 4, 4));
  576. Cell row1cell5 = row1.createCell(5);
  577. row1cell5.setCellValue("正常考勤次数");
  578. row1cell5.setCellStyle(cellStyle);
  579. sheet.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber + 1, 5, 5));
  580. Cell row1cell6 = row1.createCell(6);
  581. row1cell6.setCellValue("请假次数");
  582. row1cell6.setCellStyle(cellStyle);
  583. sheet.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber + 1, 6, 6));
  584. Cell row1cell7 = row1.createCell(7);
  585. row1cell7.setCellValue("缺勤次数");
  586. row1cell7.setCellStyle(cellStyle);
  587. sheet.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber + 1, 7, 7));
  588. for (int i = 0; i < 7; i++) {
  589. sheet.autoSizeColumn(i);
  590. }
  591. sheet.setColumnWidth(4, 24 * 256);
  592. sheet.setColumnWidth(5, 24 * 256);
  593. int cellNumber = 8;
  594. for (String dayOfWeek : weekList) {
  595. Cell row1cell = row1.createCell(cellNumber);
  596. row1cell.setCellValue(dayOfWeek);
  597. row1cell.setCellStyle(cellStyle);
  598. cellNumber++;
  599. }
  600. }
  601. /**
  602. * @param startColumn 开始列
  603. * @param dateList 2024年10月20日
  604. */
  605. void createFourthTitle(Workbook workbook, Sheet sheet, int startColumn, List<String> dateList) {
  606. int rowNumber = 3;
  607. Font font = workbook.createFont();
  608. font.setFontName("宋体");
  609. font.setFontHeightInPoints((short) 12);
  610. CellStyle cellStyle = workbook.createCellStyle();
  611. cellStyle.setFont(font); // 将字体应用到样式
  612. cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
  613. cellStyle.setAlignment(HorizontalAlignment.CENTER);
  614. Row row1 = sheet.createRow(rowNumber);
  615. for (String date : dateList) {
  616. Cell row1cell = row1.createCell(startColumn);
  617. row1cell.setCellValue(date);
  618. row1cell.setCellStyle(cellStyle);
  619. startColumn++;
  620. }
  621. }
  622. }