package com.aps.core.service.impl; import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; import com.alibaba.fastjson.JSONObject; import com.aps.common.core.utils.DateUtils; import com.aps.common.core.utils.bean.BeanUtils; import com.aps.common.core.web.domain.AjaxResult; import com.aps.common.security.utils.DictUtils; import com.aps.common.security.utils.SecurityUtils; import com.aps.core.domain.*; import com.aps.core.mapper.*; import com.aps.system.api.domain.SysDictData; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.*; import org.apache.poi.util.IOUtils; import org.apache.poi.xssf.streaming.SXSSFCell; import org.apache.poi.xssf.streaming.SXSSFRow; import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.aps.core.service.IApsPlateProcessShopStatService; import org.springframework.transaction.annotation.Transactional; /** * 钣金车间统计Service业务层处理 * * @author zhl * @date 2025-04-23 */ @Slf4j @Service public class ApsPlateProcessShopStatServiceImpl implements IApsPlateProcessShopStatService { @Autowired private ApsPlateProcessShopStatMapper apsPlateProcessShopStatMapper; @Resource private ApsPlateProcessStatMapper apsPlateProcessStatMapper; @Resource private ApsShopMapper shopMapper; @Resource private ApsStandardProcessMapper standardProcessMapper; @Resource private ApsPlatePlanMapper apsPlatePlanMapper; /** * 查询钣金车间统计 * * @param id 钣金车间统计主键 * @return 钣金车间统计 */ @Override public ApsPlateProcessShopStat selectApsPlateProcessShopStatById(Long id) { return apsPlateProcessShopStatMapper.selectApsPlateProcessShopStatById(id); } /** * 查询钣金车间统计列表 * * @param apsPlateProcessShopStat 钣金车间统计 * @return 钣金车间统计 */ @Override public List selectApsPlateProcessShopStatList(ApsPlateProcessShopStat apsPlateProcessShopStat) { return apsPlateProcessShopStatMapper.selectApsPlateProcessShopStatList(apsPlateProcessShopStat); } /** * 新增钣金车间统计 * * @param apsPlateProcessShopStat 钣金车间统计 * @return 结果 */ @Override public int insertApsPlateProcessShopStat(ApsPlateProcessShopStat apsPlateProcessShopStat) { apsPlateProcessShopStat.setCreateTime(DateUtils.getNowDate()); return apsPlateProcessShopStatMapper.insertApsPlateProcessShopStat(apsPlateProcessShopStat); } /** * 修改钣金车间统计 * * @param apsPlateProcessShopStat 钣金车间统计 * @return 结果 */ @Override public int updateApsPlateProcessShopStat(ApsPlateProcessShopStat apsPlateProcessShopStat) { apsPlateProcessShopStat.setUpdateTime(DateUtils.getNowDate()); return apsPlateProcessShopStatMapper.updateApsPlateProcessShopStat(apsPlateProcessShopStat); } /** * 批量删除钣金车间统计 * * @param ids 需要删除的钣金车间统计主键 * @return 结果 */ @Override public int deleteApsPlateProcessShopStatByIds(Long[] ids) { return apsPlateProcessShopStatMapper.deleteApsPlateProcessShopStatByIds(ids); } /** * 删除钣金车间统计信息 * * @param id 钣金车间统计主键 * @return 结果 */ @Override public int deleteApsPlateProcessShopStatById(Long id) { return apsPlateProcessShopStatMapper.deleteApsPlateProcessShopStatById(id); } /** * 保存钣金车间统计 */ @Transactional @Override public void saveShopStat() { try { // 开始之前先删除所有历史数据 apsPlateProcessShopStatMapper.deleteAll(); // 定义该功能使用数据源为南通的工厂 final String plant = "FORTUNA"; final String major="钣金"; // 查询相关数据 ApsPlatePlan platePlan = new ApsPlatePlan(); platePlan.setPlant(plant); List planList = apsPlatePlanMapper.selectApsPlatePlanList(platePlan); List statList = apsPlateProcessStatMapper.selectApsPlateProcessStatList(new ApsPlateProcessStat()); ApsShop apsShop = new ApsShop(); apsShop.setPlantCode(plant); List shopList = shopMapper.selectApsShopList(apsShop); ApsStandardProcess process = new ApsStandardProcess(); process.setPlant(plant); process.setMajor(major); List shopProcesses = standardProcessMapper.selectApsStandardProcessList(process); if (planList.isEmpty() || shopList.isEmpty() || shopProcesses.isEmpty()) { log.warn("计划列表、车间列表或工序列表为空,无法生成统计信息"); return; } // 构建车间名称到工序名称的映射 Map> shopToProcessNames = shopProcesses.stream() .collect(Collectors.groupingBy(ApsStandardProcess::getWorkShop, Collectors.mapping(ApsStandardProcess::getProcessName, Collectors.toList()) )); // 批量插入统计数据 List statsToInsert = new ArrayList<>(); for (ApsPlatePlan plan : planList) { for (ApsShop shop : shopList) { ApsPlateProcessShopStat stat = createShopStat(plan, shop, shopToProcessNames, statList); statsToInsert.add(stat); } } // 批量插入以提高性能 if (!statsToInsert.isEmpty()) { int batchSize = 1000; for (int i = 0; i < statsToInsert.size(); i += batchSize) { int end = Math.min(i + batchSize, statsToInsert.size()); List batch = statsToInsert.subList(i, end); apsPlateProcessShopStatMapper.batchInsert(batch); } } } catch (Exception e) { log.error("保存钣金车间统计时发生异常", e); throw new RuntimeException("保存钣金车间统计失败", e); } } /** * 创建单个车间统计对象 */ private ApsPlateProcessShopStat createShopStat(ApsPlatePlan plan, ApsShop shop, Map> shopToProcessNames, List statList) { String shopName = shop.getShopName(); ApsPlateProcessShopStat stat = new ApsPlateProcessShopStat(); stat.setDocNo(plan.getDocumentNumber()); stat.setShopCode(shop.getShopCode()); stat.setShopName(shopName); stat.setDelFlag("0"); stat.setCreateBy(SecurityUtils.getUsername()); stat.setCreateTime(DateUtils.getNowDate()); stat.setWorkCenter(plan.getWorkCenter()); stat.setProcessNumber(plan.getProcessNumber()); try { List processNames = shopToProcessNames.getOrDefault(shopName, Collections.emptyList()); if (!processNames.isEmpty()) { // 根据工序名称,查询该工单下所有的工序信息 List processStats = statList.stream() .filter(x -> processNames.contains(x.getProcessName()) && x.getWorkOrderNo().equals(plan.getDocumentNumber()) &&x.getProcessPlanStartDay()!=null&&x.getProcessPlanEndDay()!=null ).toList(); if (!processStats.isEmpty()) { // 取出工单下工序的最小开始时间和最大结束时间 List startDayList = processStats.stream().map(ApsPlateProcessStat::getProcessPlanStartDay).filter(Objects::nonNull).toList(); if (!startDayList.isEmpty()) { if (startDayList.size() == 1) { stat.setPlanStartDate(DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", startDayList.get(0))); } else { Optional minStartDay = processStats.stream() .map(ApsPlateProcessStat::getProcessPlanStartDay) .min(Comparator.naturalOrder()); Date date = minStartDay.get(); stat.setPlanStartDate(DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", date)); } } List endDayList = processStats.stream().map(ApsPlateProcessStat::getProcessPlanEndDay).filter(Objects::nonNull).toList(); if (!endDayList.isEmpty()) { if (endDayList.size() == 1) { stat.setPlanEndDate(DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", endDayList.get(0))); } else { Optional maxEndDay = processStats.stream() .map(ApsPlateProcessStat::getProcessPlanEndDay) .max(Comparator.naturalOrder()); Date date = maxEndDay.get(); stat.setPlanEndDate(DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", date)); } } } } } catch (Exception e) { log.error("computer error:"+ JSONObject.toJSONString(stat)); } return stat; } @Override public AjaxResult getShopPlanStat() { // 提取硬编码值为常量 final String PLANT_CODE = "FORTUNA"; // 初始化对象 ApsPlatePlan platePlan = new ApsPlatePlan(); platePlan.setPlant(PLANT_CODE); ApsShop apsShop = new ApsShop(); apsShop.setPlantCode(PLANT_CODE); // 获取车间列表并处理空值 List shopList = Optional.ofNullable(shopMapper.selectApsShopList(apsShop)) .orElse(Collections.emptyList()) .stream() .map(ApsShop::getShopName) .toList(); // 获取计划列表和状态列表 List planList = Optional.ofNullable(apsPlatePlanMapper.selectApsPlatePlanList(platePlan)) .orElse(Collections.emptyList()); List shopStates = Optional.ofNullable(apsPlateProcessShopStatMapper.selectApsPlateProcessShopStatList(new ApsPlateProcessShopStat())) .orElse(Collections.emptyList()); // 提前对 shopStates 按 docNo 分组,减少重复流操作 Map> shopStatesByDocNo = shopStates.stream() .collect(Collectors.groupingBy(ApsPlateProcessShopStat::getDocNo)); List businessTypeDic = DictUtils.getDictCache("aps_business_type"); List documentStatusDic = DictUtils.getDictCache("aps_document_status"); // 构建结果列表 List shopPlanStats = planList.stream() .map(plan -> { ApsPlateProcessShopPlanStat shopPlanStat = new ApsPlateProcessShopPlanStat(); BeanUtils.copyProperties(plan, shopPlanStat); // 确保目标对象是合法的单个对象实例 // 根据 docNo 获取对应的 shopStatList List shopStatList = shopStatesByDocNo.getOrDefault(plan.getDocumentNumber(), Collections.emptyList()); shopPlanStat.setDeptPlans(shopStatList); if (businessTypeDic != null) { businessTypeDic.stream().filter(x -> x.getDictValue().equals(plan.getBusinessType())).findFirst() .ifPresent(sysDictData -> shopPlanStat.setBusinessType(sysDictData.getDictLabel())); } if (documentStatusDic != null) { documentStatusDic.stream().filter(x->x.getDictValue().equals(plan.getDocumentStatus())) .findFirst().ifPresent(sysDictData -> shopPlanStat.setDocumentStatus(sysDictData.getDictLabel())); } return shopPlanStat; }).toList(); // 构建返回结果 AjaxResult success = AjaxResult.success(shopPlanStats); success.put("shopNames", shopList); return success; } @Override public void exportExcel(HttpServletResponse response) { SXSSFWorkbook wb = new SXSSFWorkbook(500); wb.createSheet(); wb.setSheetName(0, "钣金计划表"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); Map styles = createStyles(wb); CellStyle title = styles.get("title"); try { AjaxResult stat = getShopPlanStat(); List shopNames = (List) stat.get("shopNames"); List table= (List)stat.get("data"); SXSSFSheet sheet = wb.getSheetAt(0); /*填写日期列 和 工时列*/ SXSSFRow rowTitle = sheet.createRow(0); SXSSFCell mainPartNumberTitle = rowTitle.createCell(0); mainPartNumberTitle.setCellValue("主件料号"); mainPartNumberTitle.setCellStyle(title); SXSSFCell businessTypeTitle = rowTitle.createCell(1); businessTypeTitle.setCellValue("业务类型"); businessTypeTitle.setCellStyle(title); SXSSFCell documentNumberTitle = rowTitle.createCell(2); documentNumberTitle.setCellValue("单据号"); documentNumberTitle.setCellStyle(title); SXSSFCell requirementTypeTitle = rowTitle.createCell(3); requirementTypeTitle.setCellValue("需求分类"); requirementTypeTitle.setCellStyle(title); SXSSFCell documentStatusTitle = rowTitle.createCell(4); documentStatusTitle.setCellValue("单据状态"); documentStatusTitle.setCellStyle(title); SXSSFCell approveDateTitle = rowTitle.createCell(5); approveDateTitle.setCellValue("审核时间"); approveDateTitle.setCellStyle(title); SXSSFCell workCenterTitle = rowTitle.createCell(6); workCenterTitle.setCellValue("当前工序"); workCenterTitle.setCellStyle(title); SXSSFCell departmentTitle = rowTitle.createCell(7); departmentTitle.setCellValue("当前工序负责人"); departmentTitle.setCellStyle(title); SXSSFCell crtPcsStsTitle = rowTitle.createCell(8); crtPcsStsTitle.setCellValue("当前工序状态"); crtPcsStsTitle.setCellStyle(title); SXSSFCell nextProcessTitle = rowTitle.createCell(9); nextProcessTitle.setCellValue("下一工序"); nextProcessTitle.setCellStyle(title); SXSSFCell nextProcessDeparmentTitle = rowTitle.createCell(10); nextProcessDeparmentTitle.setCellValue("下一工序负责人"); nextProcessDeparmentTitle.setCellStyle(title); SXSSFCell itemNumberTitle = rowTitle.createCell(11); itemNumberTitle.setCellValue("料号"); itemNumberTitle.setCellStyle(title); SXSSFCell drawingNoTitle = rowTitle.createCell(12); drawingNoTitle.setCellValue("图号"); drawingNoTitle.setCellStyle(title); SXSSFCell versionNumberTitle = rowTitle.createCell(13); versionNumberTitle.setCellValue("版本号"); versionNumberTitle.setCellStyle(title); SXSSFCell lowNumTitle = rowTitle.createCell(14); lowNumTitle.setCellValue("低阶码"); lowNumTitle.setCellStyle(title); SXSSFCell productionQuantityTitle = rowTitle.createCell(15); productionQuantityTitle.setCellValue("生产数量"); productionQuantityTitle.setCellStyle(title); SXSSFCell planSendDateTitle = rowTitle.createCell(16); planSendDateTitle.setCellValue("工单计划下发时间"); planSendDateTitle.setCellStyle(title); SXSSFCell planEndDayTitle = rowTitle.createCell(17); planEndDayTitle.setCellValue("系统完工时间"); planEndDayTitle.setCellStyle(title); String firstShopName = shopNames.get(0); SXSSFCell firstShopBeginDateTitle = rowTitle.createCell(18); firstShopBeginDateTitle.setCellValue(firstShopName+"开工时间"); firstShopBeginDateTitle.setCellStyle(title); SXSSFCell firstShopEndDateTitle = rowTitle.createCell(19); firstShopEndDateTitle.setCellValue(firstShopName+"完工时间"); firstShopEndDateTitle.setCellStyle(title); /*焊接件齐套*/ SXSSFCell hanJieQiTaoTitle= rowTitle.createCell(20); hanJieQiTaoTitle.setCellValue("焊接件齐套开始时间"); hanJieQiTaoTitle.setCellStyle(title); int i1 = 19; for (int i = 1; i < shopNames.size(); i++) { String shopName = shopNames.get(i); SXSSFCell beginDateCell = rowTitle.createCell(i * 2 + i1); SXSSFCell endDateCell = rowTitle.createCell(i * 2 + i1+1); beginDateCell.setCellValue(shopName+"开工时间"); endDateCell.setCellValue(shopName+"完工时间"); beginDateCell.setCellStyle(title); endDateCell.setCellStyle(title); } SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (int i = 0; i < table.size(); i++) { ApsPlateProcessShopPlanStat plan = table.get(i); SXSSFRow dataRow = sheet.createRow(i+1); //主件料号 SXSSFCell mainPartNumberCell = dataRow.createCell(0); mainPartNumberCell.setCellValue(plan.getMainPartNumber()); //业务类型; SXSSFCell businessTypeCell= dataRow.createCell(1); businessTypeCell.setCellValue(plan.getBusinessType()); //单据号; SXSSFCell documentNumberCell = dataRow.createCell(2); documentNumberCell.setCellValue(plan.getDocumentNumber()); //需求分类; SXSSFCell requirementTypeCell = dataRow.createCell(3); requirementTypeCell.setCellValue(plan.getRequirementType()); // 单据状态 SXSSFCell documentStatusCell = dataRow.createCell(4); documentStatusCell.setCellValue(plan.getDocumentStatus()); // 审核时间 SXSSFCell approveDateCell = dataRow.createCell(5); approveDateCell.setCellValue(""); //当前工序 SXSSFCell workCenterCell = dataRow.createCell(6); workCenterCell.setCellValue(plan.getWorkCenter()); //当前工序负责人; SXSSFCell departmentCell = dataRow.createCell(7); departmentCell.setCellValue(plan.getDepartment()); SXSSFCell crtPcsStsCell = dataRow.createCell(8); crtPcsStsCell.setCellValue(""); //下一工序 SXSSFCell nextProcessCell = dataRow.createCell(9); nextProcessCell.setCellValue(""); //下一工序工序负责人 SXSSFCell nextProcessDeparmentCell = dataRow.createCell(10); nextProcessDeparmentCell.setCellValue(plan.getNextProcessDeparment()); //料号 SXSSFCell itemNumberCell = dataRow.createCell(11); itemNumberCell.setCellValue(plan.getItemNumber()); // 图号 SXSSFCell drawingNoCell = dataRow.createCell(12); drawingNoCell.setCellValue(plan.getDrawingNo()); //版本号 SXSSFCell versionNumberCell = dataRow.createCell(13); versionNumberCell.setCellValue(plan.getVersionNumber()); //低阶码 SXSSFCell lowNumCell = dataRow.createCell(14); lowNumCell.setCellValue(""); //生产数量 SXSSFCell productionQuantityCell = dataRow.createCell(15); productionQuantityCell.setCellValue(plan.getProductionQuantity().toString()); //工单计划下发时间 SXSSFCell planSendDateCell = dataRow.createCell(16); planSendDateCell.setCellValue(""); //系统完工时间 SXSSFCell planEndDayCell = dataRow.createCell(17); planEndDayCell.setCellValue(dateFormat.format(plan.getPlanEndDay())); SXSSFCell firstShopBeginDateCell = dataRow.createCell(18); SXSSFCell firstShopEndDateCell = dataRow.createCell(19); plan.getDeptPlans().stream().filter(x->x.getShopName().equals(firstShopName)).findFirst().ifPresent(x->{ firstShopBeginDateCell.setCellValue(x.getPlanStartDate()); firstShopEndDateCell.setCellValue(x.getPlanEndDate()); }); /*焊接件齐套*/ SXSSFCell hanJieQiTaoCell = dataRow.createCell(20); hanJieQiTaoCell.setCellValue(""); for (int j = 1; j< shopNames.size(); j++) { String shopName = shopNames.get(j); SXSSFCell beginDateCell = dataRow.createCell(j * 2 + i1); SXSSFCell endDateCell = dataRow.createCell(j * 2 + i1+1); plan.getDeptPlans().stream().filter(x->x.getShopName().equals(shopName)).findFirst().ifPresent(x->{ beginDateCell.setCellValue(x.getPlanStartDate()); endDateCell.setCellValue(x.getPlanEndDate()); }); } } for (int i = 0; i < rowTitle.getLastCellNum(); i++) { sheet.setColumnWidth(i, 20 * 256); } wb.write(response.getOutputStream()); } catch (Exception e) { log.error("导出Excel异常{}", e.getMessage()); } finally { IOUtils.closeQuietly(wb); } } private Map createStyles(SXSSFWorkbook wb) { Map styles=new HashMap<>(); CellStyle style = wb.createCellStyle(); style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER); Font titleFont = wb.createFont(); titleFont.setFontName("Arial"); titleFont.setFontHeightInPoints((short) 12); titleFont.setBold(true); style.setFont(titleFont); DataFormat dataFormat = wb.createDataFormat(); style.setDataFormat(dataFormat.getFormat("@")); styles.put("title", style); return styles; } }