package cn.iocoder.yudao.module.mes.service.safety; import cn.idev.excel.FastExcelFactory; import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.ledger.SafetyLedgerExportReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.safety.*; import cn.iocoder.yudao.module.mes.enums.safety.*; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Service; import java.io.IOException; import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.SafetyErrorCodeConstants.SAFETY_LEDGER_DATE_RANGE_INVALID; /** * 安全生产 - 电力行业标准化台账导出 Service * *

按「全套」导出时一次生成多 Sheet 工作簿,覆盖安全标准化检查所需的全部台账: * 组织架构、特种作业证、巡检记录、隐患台账、作业票、监护记录、物资台账、 * 出入库流水、培训记录、演练记录、事故登记。

* * @author 超级管理员 */ @Service public class SafetyLedgerExportService { @Resource private SafetyOrgService orgService; @Resource private SafetyCertificateService certificateService; @Resource private SafetyInspectionService inspectionService; @Resource private SafetyHazardService hazardService; @Resource private SafetyWorkPermitService permitService; @Resource private SafetyMaterialService materialService; @Resource private SafetyTrainingService trainingService; /** * 导出标准化台账 */ public void export(SafetyLedgerExportReqVO reqVO, HttpServletResponse response) throws IOException { LocalDate start = reqVO.getStartDate(); LocalDate end = reqVO.getEndDate(); if (start != null && end != null && start.isAfter(end)) { throw exception(SAFETY_LEDGER_DATE_RANGE_INVALID); } String type = reqVO.getType() == null ? "ALL" : reqVO.getType(); List sheets = new ArrayList<>(); if ("ALL".equals(type) || "CERT".equals(type)) { sheets.add(certSheet()); } if ("ALL".equals(type) || "INSPECTION".equals(type)) { sheets.add(inspectionSheet(start, end)); } if ("ALL".equals(type) || "HAZARD".equals(type)) { sheets.add(hazardSheet(start, end)); } if ("ALL".equals(type) || "PERMIT".equals(type)) { sheets.add(permitSheet(start, end)); sheets.add(monitorSheet()); } if ("ALL".equals(type) || "MATERIAL".equals(type)) { sheets.add(materialSheet()); sheets.add(stockSheet(start, end)); } if ("ALL".equals(type) || "TRAINING".equals(type)) { sheets.add(trainingSheet(start, end)); } if ("ALL".equals(type) || "DRILL".equals(type)) { sheets.add(drillSheet(start, end)); } if ("ALL".equals(type) || "ACCIDENT".equals(type)) { sheets.add(accidentSheet(start, end)); } String filename = switch (type) { case "CERT" -> "特种作业证台账.xlsx"; case "INSPECTION" -> "安全巡检台账.xlsx"; case "HAZARD" -> "隐患台账.xlsx"; case "PERMIT" -> "高危作业票台账.xlsx"; case "MATERIAL" -> "应急物资台账.xlsx"; case "TRAINING" -> "安全培训台账.xlsx"; case "DRILL" -> "消防演练台账.xlsx"; case "ACCIDENT" -> "事故登记台账.xlsx"; default -> "安全生产标准化台账.xlsx"; }; write(response, filename, sheets); } // ==================== 各 Sheet ==================== private Sheet certSheet() { List headers = List.of("持证人", "工号", "部门", "证件类型", "证件编号", "发证机关", "发证日期", "有效期至", "下次复审", "状态", "预警天数", "备注"); var pageReqVO = new cn.iocoder.yudao.module.mes.controller.admin.safety.vo.cert.SafetyCertificatePageReqVO(); pageReqVO.setPageNo(1); pageReqVO.setPageSize(50_000); var certs = certificateService.getCertificatePage(pageReqVO).getList(); // 导出时用员工档案的最新姓名/工号/部门 certificateService.fillEmployeeInfo(certs); List> rows = certs.stream() .map(c -> Arrays.asList( str(c.getEmployeeName()), str(c.getEmployeeNo()), str(c.getDeptName()), SafetyCertificateTypeEnum.getNameByType(c.getCertType()), str(c.getCertNo()), str(c.getIssuer()), date(c.getIssueDate()), date(c.getExpireDate()), date(c.getReviewDate()), SafetyCertificateStatusEnum.getNameByStatus(SafetyCertificateService.resolveStatus( c.getExpireDate(), c.getWarnDays(), c.getStatus())), Objects.toString(c.getWarnDays(), ""), str(c.getRemark()))) .toList(); return new Sheet("特种作业证台账", headers, rows); } private Sheet inspectionSheet(LocalDate start, LocalDate end) { List headers = List.of("巡检时间", "点位名称", "点位类型", "巡检人", "结果", "巡检情况", "发现隐患数"); var reqVO = new cn.iocoder.yudao.module.mes.controller.admin.safety.vo.inspection.SafetyInspectionPageReqVO(); reqVO.setPageNo(1); reqVO.setPageSize(50_000); if (start != null && end != null) { reqVO.setInspectTime(new java.time.LocalDateTime[]{start.atStartOfDay(), end.atTime(23, 59, 59)}); } var inspections = inspectionService.getInspectionPage(reqVO).getList(); inspectionService.fillInspectorNames(inspections); List> rows = inspections.stream() .map(i -> Arrays.asList( dateTime(i.getInspectTime()), str(i.getPointName()), SafetyInspectionPointTypeEnum.getNameByType(i.getPointType()), str(i.getInspector()), "NORMAL".equals(i.getResult()) ? "正常" : "异常", str(i.getContent()), Objects.toString(i.getHazardCount(), "0"))) .toList(); return new Sheet("安全巡检台账", headers, rows); } private Sheet hazardSheet(LocalDate start, LocalDate end) { List headers = List.of("隐患编号", "标题", "等级", "来源", "位置", "上报人", "上报时间", "整改责任人", "整改期限", "整改措施", "整改时间", "复查人", "复查时间", "复查结论", "状态", "闭环时间"); var reqVO = new cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.SafetyHazardPageReqVO(); reqVO.setPageNo(1); reqVO.setPageSize(50_000); if (start != null && end != null) { reqVO.setReportTime(new java.time.LocalDateTime[]{start.atStartOfDay(), end.atTime(23, 59, 59)}); } var hazards = hazardService.getHazardPage(reqVO).getList(); hazardService.fillPersonNames(hazards); List> rows = hazards.stream() .map(h -> Arrays.asList( str(h.getCode()), str(h.getTitle()), SafetyHazardLevelEnum.getNameByLevel(h.getLevel()), SafetyHazardSourceEnum.getNameBySource(h.getSource()), str(h.getLocation()), str(h.getReporter()), dateTime(h.getReportTime()), str(h.getAssignee()), date(h.getRectifyDeadline()), str(h.getRectifyDesc()), dateTime(h.getRectifyTime()), str(h.getRechecker()), dateTime(h.getRecheckTime()), "PASSED".equals(str(h.getRecheckResult())) ? "通过" : "REJECTED".equals(str(h.getRecheckResult())) ? "不通过" : "", SafetyHazardStatusEnum.getNameByStatus(h.getStatus()), dateTime(h.getCloseTime()))) .toList(); return new Sheet("隐患台账", headers, rows); } private Sheet permitSheet(LocalDate start, LocalDate end) { List headers = List.of("作业票编号", "作业类型", "作业名称", "作业区域", "作业地点", "申请人", "申请时间", "计划开始", "计划结束", "实际开始", "实际结束", "风险等级", "监护人", "审批人", "审批时间", "审批意见", "状态"); var reqVO = new cn.iocoder.yudao.module.mes.controller.admin.safety.vo.permit.SafetyWorkPermitPageReqVO(); reqVO.setPageNo(1); reqVO.setPageSize(50_000); if (start != null && end != null) { reqVO.setPlanStartTime(new java.time.LocalDateTime[]{start.atStartOfDay(), end.atTime(23, 59, 59)}); } var permits = permitService.getPermitPage(reqVO).getList(); permitService.fillPermitRefs(permits); List> rows = permits.stream() .map(p -> Arrays.asList( str(p.getCode()), SafetyPermitTypeEnum.getNameByType(p.getPermitType()), str(p.getTitle()), str(p.getWorkshopName()), str(p.getLocation()), str(p.getApplicant()), dateTime(p.getApplyTime()), dateTime(p.getPlanStartTime()), dateTime(p.getPlanEndTime()), dateTime(p.getActualStartTime()), dateTime(p.getActualEndTime()), SafetyRiskLevelEnum.getNameByLevel(p.getRiskLevel()), str(p.getGuardian()), str(p.getApprover()), dateTime(p.getApproveTime()), str(p.getApproveComment()), SafetyPermitStatusEnum.getNameByStatus(p.getStatus()))) .toList(); return new Sheet("高危作业票台账", headers, rows); } private Sheet monitorSheet() { List headers = List.of("作业票编号", "监护时间", "监护人", "监护内容", "异常情况"); var reqVO = new cn.iocoder.yudao.module.mes.controller.admin.safety.vo.permit.SafetyWorkPermitPageReqVO(); reqVO.setPageNo(1); reqVO.setPageSize(5_000); List> rows = new ArrayList<>(); for (SafetyWorkPermitDO permit : permitService.getPermitPage(reqVO).getList()) { var monitors = permitService.getMonitorList(permit.getId()); permitService.fillMonitorGuardians(monitors); for (SafetyWorkPermitMonitorDO monitor : monitors) { rows.add(Arrays.asList(str(permit.getCode()), dateTime(monitor.getMonitorTime()), str(monitor.getGuardian()), str(monitor.getMonitorContent()), str(monitor.getAbnormal()))); } } return new Sheet("作业监护记录", headers, rows); } private Sheet materialSheet() { List headers = List.of("物资编码", "物资名称", "类别", "规格型号", "单位", "库存数量", "安全库存", "存放位置", "有效期至", "保管人", "状态"); var reqVO = new cn.iocoder.yudao.module.mes.controller.admin.safety.vo.material.SafetyMaterialPageReqVO(); reqVO.setPageNo(1); reqVO.setPageSize(50_000); var materials = materialService.getMaterialPage(reqVO).getList(); materialService.fillManagers(materials); List> rows = materials.stream() .map(m -> Arrays.asList( str(m.getCode()), str(m.getName()), SafetyMaterialCategoryEnum.getNameByCategory(m.getCategory()), str(m.getSpec()), str(m.getUnit()), number(m.getStockQty()), number(m.getSafeStock()), str(m.getStorageLocation()), date(m.getExpireDate()), str(m.getManager()), Integer.valueOf(0).equals(m.getStatus()) ? "启用" : "停用")) .toList(); return new Sheet("应急物资台账", headers, rows); } private Sheet stockSheet(LocalDate start, LocalDate end) { List headers = List.of("业务日期", "物资名称", "类别", "业务类型", "数量", "操作前库存", "操作后库存", "经办人", "领用人", "用途/去向"); var reqVO = new cn.iocoder.yudao.module.mes.controller.admin.safety.vo.material.SafetyMaterialStockPageReqVO(); reqVO.setPageNo(1); reqVO.setPageSize(50_000); if (start != null && end != null) { reqVO.setBizDate(new LocalDate[]{start, end}); } List> rows = materialService.getStockPage(reqVO).getList().stream() .map(s -> Arrays.asList( date(s.getBizDate()), str(s.getMaterialName()), SafetyMaterialCategoryEnum.getNameByCategory(s.getCategory()), "IN".equals(s.getBizType()) ? "入库" : "出库", number(s.getQuantity()), number(s.getStockBefore()), number(s.getStockAfter()), str(s.getOperator()), str(s.getReceiver()), str(s.getPurpose()))) .toList(); return new Sheet("物资出入库流水", headers, rows); } private Sheet trainingSheet(LocalDate start, LocalDate end) { List headers = List.of("培训日期", "培训主题", "培训类型", "学时", "讲师/组织人", "培训地点", "参加人数", "培训内容", "考核情况"); var trainings = trainingService.listTrainingBetween(start, end); trainingService.fillTrainers(trainings); List> rows = trainings.stream() .map(t -> Arrays.asList( date(t.getTrainDate()), str(t.getTitle()), SafetyTrainingTypeEnum.getNameByType(t.getTrainingType()), number(t.getDurationHours()), str(t.getTrainer()), str(t.getLocation()), Objects.toString(t.getParticipantCount(), "0"), str(t.getContent()), str(t.getExamResult()))) .toList(); return new Sheet("安全培训台账", headers, rows); } private Sheet drillSheet(LocalDate start, LocalDate end) { List headers = List.of("演练日期", "演练名称", "演练类型", "地点", "组织人", "参加人数", "时长(分钟)", "演练过程", "总结与改进"); var drills = trainingService.listDrillBetween(start, end); trainingService.fillOrganizers(drills); List> rows = drills.stream() .map(d -> Arrays.asList( date(d.getDrillDate()), str(d.getTitle()), SafetyDrillTypeEnum.getNameByType(d.getDrillType()), str(d.getLocation()), str(d.getOrganizer()), Objects.toString(d.getParticipantCount(), "0"), Objects.toString(d.getDurationMinutes(), ""), str(d.getProcessDesc()), str(d.getSummary()))) .toList(); return new Sheet("消防演练台账", headers, rows); } private Sheet accidentSheet(LocalDate start, LocalDate end) { List headers = List.of("事故编号", "事故名称", "事故类型", "等级", "发生时间", "发生地点", "伤亡情况", "经济损失(元)", "事故经过", "原因分析", "处理措施", "状态"); var accidents = trainingService.listAccidentBetween(start, end); trainingService.fillAccidentRefs(accidents); List> rows = accidents.stream() .map(a -> Arrays.asList( str(a.getCode()), str(a.getTitle()), SafetyAccidentTypeEnum.getNameByType(a.getAccidentType()), SafetyAccidentLevelEnum.getNameByLevel(a.getLevel()), dateTime(a.getOccurTime()), str(a.getLocation()), str(a.getCasualtyDesc()), number(a.getEconomicLoss()), str(a.getDescription()), str(a.getCauseAnalysis()), str(a.getMeasures()), SafetyAccidentStatusEnum.getNameByStatus(a.getStatus()))) .toList(); return new Sheet("事故登记台账", headers, rows); } // ==================== 工具方法 ==================== private static String str(String value) { return value == null ? "" : value; } private static String date(LocalDate value) { return value == null ? "" : value.toString(); } private static String dateTime(java.time.LocalDateTime value) { return value == null ? "" : value.toString().replace('T', ' '); } private static String number(java.math.BigDecimal value) { return value == null ? "" : value.stripTrailingZeros().toPlainString(); } private record Sheet(String name, List headers, List> rows) { } private void write(HttpServletResponse response, String filename, List sheets) throws IOException { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename)); try (var writer = FastExcelFactory.write(response.getOutputStream()).autoCloseStream(false).build()) { for (int i = 0; i < sheets.size(); i++) { Sheet sheet = sheets.get(i); writer.write(sheet.rows(), FastExcelFactory.writerSheet(i, sheet.name()) .head(sheet.headers().stream().map(List::of).toList()).build()); } } } }