package com.aps.core.service.impl.ApsPlate; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.util.IdUtil; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson2.JSON; import com.alibaba.nacos.common.utils.JacksonUtils; import com.aps.common.core.utils.DateUtils; 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.domain.ApsPlate.ApsPlatePlan; import com.aps.core.domain.ApsPlate.ApsPlateProcessShopPlanStat; import com.aps.core.domain.ApsPlate.ApsPlateProcessShopStat; import com.aps.core.domain.ApsPlate.ApsPlateProcessStat; import com.aps.core.mapper.*; import com.aps.core.service.ApsPlate.IApsPlateProcessStatService; import com.aps.system.api.domain.SysDictData; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import lombok.AllArgsConstructor; import lombok.Data; 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.ApsPlate.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; @Autowired private IApsPlateProcessStatService apsPlateProcessStatService; /** * 查询钣金车间统计 * * @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( rollbackFor =Exception.class) @Override public void saveShopStat() { // 定义该功能使用数据源为南通的工厂 final String plant = "FORTUNA"; final String major="BJ"; // 查询相关数据 ApsPlatePlan platePlan = new ApsPlatePlan(); platePlan.setPlant(plant); List planList = apsPlatePlanMapper.selectApsPlatePlanList(platePlan); 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()) { // 开始之前先删除所有历史数据 apsPlateProcessStatMapper.deleteAll(); apsPlateProcessShopStatMapper.deleteAll(); List statList =apsPlateProcessStatService.computePlateProcessStat(); // 构建车间名称到工序名称的映射 Map> shopToProcessNames = shopProcesses.stream().filter(x -> null!=x.getWorkShop()) .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); } } List> processStatBatchList = ListUtil.split(statList, 1000); processStatBatchList.forEach(batch -> apsPlateProcessStatMapper.batchInsertPlateStat(batch)); List> shopStatBatchList = ListUtil.split(statsToInsert, 1000); shopStatBatchList.forEach(batch -> apsPlateProcessShopStatMapper.batchInsert(batch)); }else { if(shopProcesses.isEmpty()){ throw new RuntimeException("未找到标准工序数据!"); } if(shopList.isEmpty()){ throw new RuntimeException("未找到车间数据!"); } if(planList.isEmpty()){ throw new RuntimeException("未找到工单数据!"); } } } /** * 创建单个车间统计对象 */ 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)); throw new RuntimeException("计算车间统计数据异常!"); } 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); apsShop.setStatus("1"); // 获取车间列表并处理空值 List shopList = Optional.ofNullable(shopMapper.selectApsShopList(apsShop)) .orElse(Collections.emptyList()) .stream() .map(ApsShop::getShopName) .toList(); // 获取计划列表和状态列表 List planList = apsPlateProcessShopStatMapper.selectPlatePlanBaseTable(); 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"); // 构建结果列表 planList.forEach( plan -> { // 根据 docNo 获取对应的 shopStatList List shopStatList = shopStatesByDocNo.getOrDefault(plan.getDocumentNumber(), Collections.emptyList()); plan.setDeptPlans(shopStatList); if (businessTypeDic != null) { businessTypeDic.stream().filter(x -> x.getDictValue().equals(plan.getBusinessType())).findFirst() .ifPresent(sysDictData -> plan.setBusinessType(sysDictData.getDictLabel())); } if (documentStatusDic != null) { documentStatusDic.stream().filter(x->x.getDictValue().equals(plan.getDocumentStatus())) .findFirst().ifPresent(sysDictData -> plan.setDocumentStatus(sysDictData.getDictLabel())); } }); // 构建返回结果 AjaxResult success = AjaxResult.success(planList); 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 titleStyle = 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); Map FrontTitleMap = initFrontTitle(shopNames); FrontTitleMap.forEach((index, titleName) -> { createCell(rowTitle, index, titleName, titleStyle); }); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (int i = 0; i < table.size(); i++) { ApsPlateProcessShopPlanStat plan = table.get(i); //JSONObject jsonObject = (JSONObject) JSONObject.toJSON(plan); ObjectMapper mapper = new ObjectMapper(); ObjectNode node = mapper.valueToTree(plan); SXSSFRow dataRow = sheet.createRow(i+1); LinkedHashMap frontTitles= frontTitleName(); ArrayList> frontTitlesList = new ArrayList>( frontTitles.entrySet()); for (int i1 = 0; i1 < frontTitlesList.size(); i1++) { Map.Entry entry = frontTitlesList.get(i1); String filedKey = entry.getKey(); String fieldValue= node.get(filedKey).textValue(); createCell(dataRow, i1,fieldValue , null); } int shopBeginIndex = frontTitlesList.size(); for (int j = 0; j< shopNames.size(); j++) { String shopName = shopNames.get(0); SXSSFCell beginDateCell = dataRow.createCell(j * 2 + shopBeginIndex); SXSSFCell endDateCell = dataRow.createCell(j * 2 + shopBeginIndex+1); plan.getDeptPlans().stream().filter(x->x.getShopName().equals(shopName)).findFirst().ifPresent(x->{ beginDateCell.setCellValue(x.getPlanStartDate()); endDateCell.setCellValue(x.getPlanEndDate()); }); } LinkedHashMap backTitles= backTitleName(); ArrayList> backTitlesList = new ArrayList>( backTitles.entrySet()); int backBeginIndex = shopBeginIndex+shopNames.size()*2; for (int i1 = 0; i1 < backTitlesList.size(); i1++) { Map.Entry entry = backTitlesList.get(i1); String filedKey = entry.getKey(); String fieldValue= node.get(filedKey).textValue(); createCell(dataRow, i1+backBeginIndex,fieldValue , null); } } 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 LinkedHashMap frontTitleName(){ LinkedHashMap map = new LinkedHashMap<>(); map.put("mainPartNumber","主件料号"); map.put("customer","主件客户"); map.put("requireTrackId","建树行"); map.put("businessType","业务类型"); map.put("documentNumber","单据号"); map.put("itemNumber","料号"); map.put("drawingNo","图号"); map.put("versionNumber","版本号"); map.put("lowOrderCode","低阶码"); map.put("productionQuantity","生产数量"); map.put("requirementType","需求分类"); map.put("documentStatus","单据状态"); map.put("approveOn","审核时间"); map.put("processNumber","工序号"); map.put("workCenter","当前工序"); map.put("department","当前工序责任人"); map.put("opStatus","状态"); map.put("nextOpName","下一道工序"); map.put("planEndDay","系统完工时间"); return map; } private LinkedHashMap backTitleName(){ LinkedHashMap map = new LinkedHashMap<>(); map.put("orderCreateTime","工单创建时间"); map.put("startWorkDate","工单开工时间"); map.put("remainedProcess","剩余工序"); return map; } private Map initFrontTitle(List shopNames) { Map map = new HashMap<>(); Map frontTitles= frontTitleName(); AtomicInteger index= new AtomicInteger(); frontTitles.forEach((key,value)->{ map.put(index.get(), value); index.getAndIncrement(); }); for (int i = 0; i < shopNames.size(); i++) { map.put(i * 2 + index.get(),shopNames.get(i) + "开始时间"); map.put(i * 2 + index.get()+1,shopNames.get(i) + "结束时间"); } Map backTitles= backTitleName(); AtomicInteger begBackIndex= new AtomicInteger(frontTitles.size() + shopNames.size() * 2); backTitles.forEach((key,value)->{ map.put(begBackIndex.get(),value); begBackIndex.getAndIncrement(); }); return map; } private static void createCell(SXSSFRow rowTitle, int column, String titleName, CellStyle cellStyle) { SXSSFCell cell = rowTitle.createCell(column); cell.setCellValue(titleName); if(null!=cellStyle){ cell.setCellStyle(cellStyle); } } 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; } }