/* * Copyright (c) 2018-2025, ztt All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the pig4cloud.com developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: ztt */ package com.chinaztt.mes.technology.service.impl; import cn.hutool.core.collection.CollectionUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.chinaztt.ifs.api.feign.IfsFeignClient; import com.chinaztt.mes.basic.entity.Part; import com.chinaztt.mes.basic.mapper.PartMapper; import com.chinaztt.mes.basic.util.DictUtils; import com.chinaztt.mes.common.numgen.NumberGenerator; import com.chinaztt.mes.technology.dto.StructureComponentDTO; import com.chinaztt.mes.technology.dto.StructureDTO; import com.chinaztt.mes.technology.dto.StructureTree; import com.chinaztt.mes.technology.entity.Operation; import com.chinaztt.mes.technology.entity.Structure; import com.chinaztt.mes.technology.entity.StructureComponent; import com.chinaztt.mes.technology.excel.StructureData; import com.chinaztt.mes.technology.mapper.OperationMapper; import com.chinaztt.mes.technology.mapper.StructureComponentMapper; import com.chinaztt.mes.technology.mapper.StructureMapper; import com.chinaztt.mes.technology.service.StructureService; import com.chinaztt.ztt.common.core.util.R; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.math.BigDecimal; import java.net.URLEncoder; import java.util.*; /** * 产品结构 * * @author zhangxy * @date 2020-08-19 10:48:07 */ @AllArgsConstructor @Service @Slf4j @Transactional(rollbackFor = Exception.class) public class StructureServiceImpl extends ServiceImpl implements StructureService { private OperationMapper operationMapper; private StructureComponentMapper structureComponentMapper; private StructureMapper structureMapper; private PartMapper partMapper; private NumberGenerator numberGenerator; private DictUtils dictUtils; private IfsFeignClient ifsFeignClient; /** * @Author: Hans * @Description: 保存产品结构数据 * @Date: 2022-05-31 */ private void saveStructureDTO(StructureDTO structureDTO) { if (StringUtils.isEmpty(structureDTO.getBomTypeDb())) { structureDTO.setBomTypeDb("M"); } //根据零件号和版本号查询数据 Part part = partMapper.selectOne(Wrappers.lambdaQuery() .eq(Part::getPartNo, structureDTO.getPartNo()) .eq(Part::getEngChgLevel, structureDTO.getVersion())); if (null == part) { throw new RuntimeException("找不到当前版本零件,请确认! 零件号:" + structureDTO.getPartNo() + " ! 版本号:" + structureDTO.getVersion()); } structureDTO.setPartId(part.getId()); int count = getBaseMapper().selectCount(Wrappers.lambdaQuery() .eq(Structure::getVersion, structureDTO.getVersion()) .eq(Structure::getAlternativeNo, structureDTO.getAlternativeNo()) .eq(Structure::getBomTypeDb, structureDTO.getBomTypeDb()) .eq(Structure::getPartId, structureDTO.getPartId())); if (count > 0) { throw new RuntimeException(" 零件号:" + structureDTO.getPartNo() + " ! " + "版本号:" + structureDTO.getVersion() + " " + "替代号:" + structureDTO.getAlternativeNo() + "" + "类型:" + structureDTO.getBomTypeDb() + "" + "重复!"); } baseMapper.insert(structureDTO); Long structureId = structureDTO.getId(); for (StructureComponentDTO structureComponentDTO : structureDTO.getComponents()) { structureComponentDTO.setStructureId(structureId); if (StringUtils.isEmpty(structureComponentDTO.getOperationName())) { throw new RuntimeException("缺少工序"); } // 查询工序 如果有多个获取第一个 List operationList = operationMapper.selectList(Wrappers.lambdaQuery() .eq(Operation::getName, structureComponentDTO.getOperationName()) .eq(Operation::getActive, true).orderByAsc(Operation::getId)); if (CollectionUtil.isEmpty(operationList)) { throw new RuntimeException("工序" + structureComponentDTO.getOperationName() + "不存在"); } // 填入工序的id structureComponentDTO.setOperationId(operationList.get(0).getId()); // 零件信息 Part partComponent = partMapper.selectOne(Wrappers.lambdaQuery() .eq(Part::getPartNo, structureComponentDTO.getPartNo()) .eq(Part::getEngChgLevel, structureComponentDTO.getVersion())); if (null == partComponent) { throw new RuntimeException("找不到当前版本零件,请确认! 零件号:" + structureComponentDTO.getPartNo() + " ! 版本号:" + structureComponentDTO.getVersion()); } structureComponentDTO.setPartId(partComponent.getId()); if (StringUtils.isEmpty(structureComponentDTO.getPlanningMethod())) { structureComponentDTO.setPlanningMethod(partComponent.getPlanningMethod()); } structureComponentMapper.insert(structureComponentDTO); } } /** * @Author: Hans * @Description: 修改产品结构导入方法 * @Date: 2022-05-31 */ @Override public void importExcelExt(Map headMap, List> dataList) { log.info("--start---导入判断----"); // 封装数据 List structureDTOList = new ArrayList<>(); try { for (Map map : dataList) { // 初始化产品结构主表数据 StructureDTO structureDTO = initStructureDTO(map); List structureComponentDTOList = initStructureComponentDTO(headMap, map); structureDTO.setComponents(structureComponentDTOList); structureDTOList.add(structureDTO); } } catch (Exception e) { throw new RuntimeException("请核对必填数据是否为空或者数据格式是否正确"); } // 保存产品结构数据 for (StructureDTO structureDTO : structureDTOList) { saveStructureDTO(structureDTO); } } /** * @Author: Hans * @Description: 初始化产品结构主表数据 * @Date: 2022-05-31 */ private StructureDTO initStructureDTO(Map map) { Map dicbomtype = dictUtils.getDicKey("bom_type_db"); StructureDTO structureDTO = new StructureDTO(); //第一列是零件号 String partNo = map.get(0); structureDTO.setPartNo(partNo); //第二列是零件名称 String partName = map.get(1); structureDTO.setPartName(partName); //第三列是版本号 String version = map.get(2); structureDTO.setVersion(version); //第四列是结构类型 String structureType = map.get(3); structureDTO.setBomTypeDb(StringUtils.isEmpty(structureType) ? "M" : (StringUtils.isEmpty(dicbomtype.get(structureType)) ? "M" : dicbomtype.get(structureType))); //第五列是替代 String alternativeNo = map.get(4); structureDTO.setAlternativeNo(StringUtils.isEmpty(alternativeNo) ? "*" : alternativeNo); //第六列是替代描述 String alternativeDesc = map.get(5); structureDTO.setAlternativeDesc(alternativeDesc); structureDTO.setIfsSync(false); return structureDTO; } /** * @Author: Hans * @Description: 初始化产品结构明细表数据 * @Date: 2022-05-31 */ private List initStructureComponentDTO(Map headMap, Map map) { List structureComponentDTOList = new ArrayList<>(); // 存储工序的下标 List operationIndexList = new ArrayList<>(); // 存储盘数(盘)的下标 List operationIndex2List = new ArrayList<>(); // 获取下标 for (int i = 0; i < headMap.size(); i++) { String columnName = headMap.get(i); if ("行项号".equals(columnName)) { operationIndexList.add(i); } if ("盘数(盘)".equals(columnName)) { operationIndex2List.add(i); } } //根据头标和角标开始查询子表数据 for (int i = 0; i < operationIndexList.size(); i++) { int start = operationIndexList.get(i); int end = operationIndex2List.get(i); StructureComponentDTO structureComponentDTO = new StructureComponentDTO(); String lineItemNo = map.get(start); if (StringUtils.isEmpty(lineItemNo)) { continue; } //行号 structureComponentDTO.setLineItemNo(Integer.valueOf(lineItemNo)); //工序 structureComponentDTO.setOperationName(map.get(start + 1)); //零件号 structureComponentDTO.setPartNo(map.get(start + 2)); //零件名称 structureComponentDTO.setPartName(map.get(start + 3)); //零件版本 structureComponentDTO.setVersion(map.get(start + 4)); //数量 structureComponentDTO.setQpa(new BigDecimal(map.get(start + 5))); //单位 structureComponentDTO.setUnit(map.get(start + 6)); //颜色 structureComponentDTO.setColor(map.get(start + 7)); //计划方法 structureComponentDTO.setPlanningMethod(map.get(start + 8)); //盘数可以为空 if (!StringUtils.isEmpty(map.get(end))) { structureComponentDTO.setDiscNum(Long.valueOf(map.get(end))); } structureComponentDTOList.add(structureComponentDTO); } return structureComponentDTOList; } @Override public void importExcel(List list) { list.forEach(System.out::println); System.out.println("================================"); return; //if (CollectionUtil.isEmpty(list)) { // return; //} //List uinck = new ArrayList<>(); //for (StructureData data : list) { // Part part = partMapper.selectOne(Wrappers.lambdaQuery().eq(Part::getPartNo, data.getFatherPartNo()).eq(Part::getEngChgLevel, data.getFatherVersion())); // if (part == null) { // throw new RuntimeException("零件号:" + data.getFatherPartNo() + "不存在"); // } // Structure structure = baseMapper.selectOne(Wrappers.lambdaQuery() // .eq(Structure::getPartId, part.getId()) // .eq(Structure::getBomTypeDb, data.getStructureType()) // .eq(Structure::getAlternativeNo, data.getAlternativeNo())); // if (structure == null) { // structure = new Structure(); // structure.setPartId(part.getId()); // structure.setVersion(data.getFatherVersion()); // structure.setAlternativeDesc(data.getAlternativeDesc()); // structure.setActive(true); // structure.setBomTypeDb(data.getStructureType()); // structure.setIfsSync(false); // structure.setAlternativeNo(data.getAlternativeNo()); // baseMapper.insert(structure); // } // if (uinck.contains(structure.getId() + "_" + data.getLineItemNo())) { // throw new RuntimeException("零件号:" + part.getPartNo() + "行项号重复"); // } // Part childPart = partMapper.selectOne(Wrappers.lambdaQuery().eq(Part::getPartNo, data.getChildPartNo()).eq(Part::getEngChgLevel, data.getChildVersion())); // if (childPart == null) { // throw new RuntimeException("零件号:" + data.getChildPartNo() + "不存在"); // } // StructureComponent component = new StructureComponent(); // component.setStructureId(structure.getId()); // component.setPartId(childPart.getId()); // component.setPlanningMethod(childPart.getPlanningMethod()); // component.setQpa(new BigDecimal(data.getQpa())); // component.setLineItemNo(Integer.valueOf(data.getLineItemNo())); // component.setDiscNum(StringUtils.isEmpty(data.getDiskNum()) ? null : new Long(data.getDiskNum())); // //查询工序 如果有多个获取第一个 // List operationList = operationMapper.selectList(Wrappers.lambdaQuery() // .eq(Operation::getName, data.getOperationName()) // .eq(Operation::getActive, true).orderByAsc(Operation::getId)); // if (CollectionUtil.isEmpty(operationList)) { // throw new RuntimeException("工序" + data.getOperationName() + "不存在"); // } // component.setOperationId(operationList.get(0).getId()); // structureComponentMapper.insert(component); // uinck.add(structure.getId() + "_" + data.getLineItemNo()); //} } @Override public StructureTree getMasterTreeByPartId(Long partId) { Structure structure = baseMapper.selectOne(Wrappers.lambdaQuery().eq(Structure::getMaster, true).eq(Structure::getPartId, partId)); if (structure != null) { return this.getTree(structure.getId()); } return null; } @Override public StructureDTO getFullById(Long id) { StructureDTO structure = baseMapper.getDtoById(id); if (structure == null) { return null; } structure.setComponents(baseMapper.getComponentDtoByStructureId(id)); return structure; } /** * 检验是否引用自己,是否存在死循环 * 该节点是否在父节点及父上级里 * * @param mainPartId * @param compPartId */ private void validate(Long mainPartId, Long compPartId) { if (mainPartId != null && mainPartId.equals(compPartId)) { throw new RuntimeException("不能引用自身"); } Set parentIds = baseMapper.getParentPartIds(mainPartId); if (CollectionUtil.isEmpty(parentIds)) { return; } if (parentIds.contains(compPartId)) { throw new RuntimeException("存在循环引用"); } for (Long id : parentIds) { validate(id, compPartId); } } @Override public R fullSave(StructureDTO structure) { System.out.println(structure); System.out.println("==============================="); int noCount = baseMapper.selectCount(Wrappers.lambdaQuery() .eq(Structure::getAlternativeNo, structure.getAlternativeNo()) .eq(Structure::getBomTypeDb, structure.getBomTypeDb()) .eq(Structure::getPartId, structure.getPartId())); if (noCount > 0) { return R.failed("该零件已存在产品结构"); } resetMaster(structure); baseMapper.insert(structure); return R.ok(structure.getId()); } private void resetMaster(StructureDTO structure) { if (structure.getMaster()) { UpdateWrapper updateWrapper = new UpdateWrapper(); updateWrapper.lambda().eq(Structure::getPartId, structure.getPartId()).set(Structure::getMaster, false); this.update(updateWrapper); } } @Override public R fullUpdate(StructureDTO structure) { int noCount = baseMapper.selectCount(Wrappers.lambdaQuery() .eq(Structure::getAlternativeNo, structure.getAlternativeNo()) .eq(Structure::getBomTypeDb, structure.getBomTypeDb()) .eq(Structure::getPartId, structure.getPartId()) .ne(Structure::getId, structure.getId())); if (noCount > 0) { return R.failed("该零件已存在产品结构"); } structure.setIfsSync(false); resetMaster(structure); baseMapper.updateById(structure); //// 对接ifs //List structureDTOS = structureMapper.getStructureDtoByIds(Arrays.asList(structure.getId())); //structureIfsSyncByDto(structureDTOS); return R.ok(); } @Override public StructureTree getTree(Long id) { StructureDTO structure = baseMapper.getDtoById(id); StructureTree root = new StructureTree(); root.setPartName(structure.getPartName()); root.setPartId(structure.getPartId()); root.setQpa(BigDecimal.ONE); List comps = baseMapper.getComponentDtoByStructureId(id); repeatComponents(root, comps); return root; } @Override public R changeState(List ids, String event) { return R.ok(); } @Override public IPage> getPage(Page page, QueryWrapper ew) { return baseMapper.getStructurePage(page, ew); } @Override public List getBuildBom() { return structureMapper.selectList(Wrappers.lambdaQuery().eq(Structure::getActive, true) .notInSql(Structure::getPartId, "SELECT part_id FROM technology_bom") .inSql(Structure::getPartId, "SELECT ID FROM basic_part WHERE planning_method = 'A'")); } /** * 复制产品结构 */ @Override public R copyStructureSave(List structure) { if (CollectionUtil.isEmpty(structure)) { return R.failed("请选择产品结构"); } for (StructureDTO structureDTO : structure) { List structureComponentList = structureComponentMapper.selectList(Wrappers.lambdaQuery().eq(StructureComponent::getStructureId, structureDTO.getId())); structureDTO.setId(null); structureDTO.setVersion(numberGenerator.generateNumberWithPrefix(Structure.DIGIT, Structure.PREFIX, Structure::getVersion)); structureDTO.setMaster(false); baseMapper.insert(structureDTO); for (StructureComponent structureComponent : structureComponentList) { structureComponent.setId(null); structureComponent.setStructureId(structureDTO.getId()); structureComponentMapper.insert(structureComponent); } } return R.ok(); } @Override public R addComponent(StructureComponent component) { if (null == component.getOperationId() || null == component.getPartId() || null == component.getQpa()) { throw new RuntimeException("工序、零件和数量不可为空"); } Structure structure = baseMapper.selectById(component.getStructureId()); structure.setIfsSync(false); validate(structure.getPartId(), component.getPartId()); structureComponentMapper.insert(component); // 对接ifs // List structureDTOS = structureMapper.getStructureDtoByIds(Arrays.asList(component.getStructureId())); // structureIfsSyncByDto(structureDTOS); return R.ok(component.getId()); } @Override public R batchComponent(List structureComponents) { structureComponents.forEach(structureComponent -> { StructureComponent structureComponentUpdate = structureComponentMapper.selectById(structureComponent.getId()); structureComponentUpdate.setLineItemNo(structureComponent.getLineItemNo()); structureComponentMapper.updateById(structureComponentUpdate); }); // 对接ifs List structureDTOS = structureMapper.getStructureDtoByIds(Arrays.asList(structureComponents.get(0).getStructureId())); structureIfsSyncByDto(structureDTOS); return R.ok(); } @Override public R updateComponent(StructureComponent component) { if (null == component.getOperationId() || null == component.getPartId() || null == component.getQpa()) { throw new RuntimeException("工序、零件和数量不可为空"); } Structure structure = baseMapper.selectById(component.getStructureId()); structure.setIfsSync(false); validate(structure.getPartId(), component.getPartId()); structureComponentMapper.updateById(component); // 对接ifs // List structureDTOS = structureMapper.getStructureDtoByIds(Arrays.asList(component.getStructureId())); // structureIfsSyncByDto(structureDTOS); return R.ok(); } @Override public R removeComponent(Long compId) { StructureComponent component = structureComponentMapper.selectById(compId); structureComponentMapper.deleteById(compId); Structure structure = baseMapper.selectById(component.getStructureId()); structure.setIfsSync(false); baseMapper.updateById(structure); return R.ok(); } private void repeatComponents(StructureTree parent, List comps) { if (CollectionUtil.isEmpty(comps)) { return; } BigDecimal parentQpa = parent.getQpa(); List children = new ArrayList<>(); for (StructureComponentDTO c : comps) { StructureTree child = new StructureTree(); child.setPartName(c.getPartName()); child.setPartId(c.getPartId()); child.setQpa(c.getQpa().multiply(parentQpa)); children.add(child); buildChildren(child, c.getPartId()); } parent.setChildren(children); } private void buildChildren(StructureTree parent, Long partId) { List comps = baseMapper.getMasterComponentsByPartId(partId); repeatComponents(parent, comps); } /** * 导入模板下载 * * @param response * @throws IOException */ @Override public void exportModel(HttpServletResponse response) throws IOException { response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("UTF-8"); // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 String fileName = URLEncoder.encode("structureImportModel", "UTF-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); try { //新建ExcelWriter ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build(); //获取sheet0对象 WriteSheet mainSheet = EasyExcel.writerSheet(0, "导入模板").head(StructureData.class).build(); //向sheet0写入数据 传入空list这样只导出表头 List structureData = new ArrayList<>(); excelWriter.write(structureData, mainSheet); //关闭流 excelWriter.finish(); } catch (IOException e) { throw new RuntimeException("导出失败"); } } /** * 产品结构对接ifs * * @param structureIds * @return */ @Override public R structureIfsSync(List structureIds) { List structureDTOS = structureMapper.getStructureDtoByIds(structureIds); return R.ok(structureIfsSyncByDto(structureDTOS)); } /** * 对接ifs * * @param structureDTOS * @return */ @Override public boolean structureIfsSyncByDto(List structureDTOS) { JSONObject jsonObject = new JSONObject() .fluentPut("RECORD_ID", UUID.randomUUID().toString()) .fluentPut("SYSCODE", "LMES") .fluentPut("SYSMODEL", "产品结构"); JSONArray batchInfoJsonArray = new JSONArray(); List ids = new ArrayList<>(); structureDTOS.forEach(structureDTO -> { JSONObject batchInfoJsonObj = new JSONObject() //版本 .fluentPut("ENG_CHG_LEVEL", structureDTO.getVersion()) //零件号 .fluentPut("PART_NO", structureDTO.getPartNo()) //bom类型 .fluentPut("BOM_TYPE", structureDTO.getBomTypeDb()) //生效日期 .fluentPut("EFF_PHASE_IN_DATE", "2000-01-01") //結束時間 .fluentPut("EFF_PHASE_OUT_DATE", ""); JSONArray alinfoArray = new JSONArray(); JSONObject alinfoObj = new JSONObject() //替代 .fluentPut("ALTERNATIVE_NO", structureDTO.getAlternativeNo()) //替代描述 .fluentPut("ALTERNATIVE_DESC", structureDTO.getAlternativeDesc()) //是否建立 .fluentPut("IS_BUILD", "TRUE"); JSONArray compInfoJsonArray = new JSONArray(); alinfoObj.put("COMP_INFO", compInfoJsonArray); structureDTO.getComponents().forEach(structureComponentDTO -> { compInfoJsonArray.add(new JSONObject() .fluentPut("LINE_ITEM_NO", structureComponentDTO.getLineItemNo()) .fluentPut("COMPONENT_PART", structureComponentDTO.getPartNo()) .fluentPut("QTY_PER_ASSEMBLY", structureComponentDTO.getQpa())); }); alinfoArray.add(alinfoObj); ids.add(structureDTO.getId()); batchInfoJsonObj.put("ALTER_INFO", alinfoArray); batchInfoJsonArray.add(batchInfoJsonObj); }); jsonObject.put("BATCH_INFO", batchInfoJsonArray); if (batchInfoJsonArray.size() > 0) { R result = ifsFeignClient.importProdStructureStd(jsonObject, true); if (result.getCode() == 1) { throw new RuntimeException("IFS错误——" + result.getMsg()); } else { structureMapper.updateIfsTrue(ids); } } return true; } }