/* * 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.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.dto.ParamDTO; import com.chinaztt.mes.basic.entity.Param; import com.chinaztt.mes.basic.entity.Part; import com.chinaztt.mes.basic.entity.Template; import com.chinaztt.mes.basic.mapper.BasicParamTemplateMapper; import com.chinaztt.mes.basic.mapper.ParamMapper; import com.chinaztt.mes.basic.mapper.PartMapper; import com.chinaztt.mes.basic.util.DictUtils; import com.chinaztt.mes.common.handler.StateMachineHandler; import com.chinaztt.mes.common.numgen.NumberGenerator; import com.chinaztt.mes.common.util.StateResult; import com.chinaztt.mes.technology.dto.*; import com.chinaztt.mes.technology.entity.*; import com.chinaztt.mes.technology.excel.RoutingData; import com.chinaztt.mes.technology.excel.RoutingExcelData; import com.chinaztt.mes.technology.mapper.*; import com.chinaztt.mes.technology.service.RoutingService; import com.chinaztt.mes.technology.state.bom.constant.BomStateStringValues; import com.chinaztt.mes.technology.state.routing.RoutingStateMachineConfig; import com.chinaztt.mes.technology.state.routing.constant.RoutingEvents; import com.chinaztt.mes.technology.state.routing.constant.RoutingStateStringValues; import com.chinaztt.mes.technology.state.routing.constant.RoutingStates; import com.chinaztt.ztt.common.core.util.R; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.statemachine.config.StateMachineFactory; import org.springframework.statemachine.persist.StateMachinePersister; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.math.BigDecimal; import java.net.URLEncoder; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; /** * 工艺 * * @author zhangxy * @date 2020-08-19 09:36:29 */ @Transactional(rollbackFor = Exception.class) @AllArgsConstructor @Service @Slf4j public class RoutingServiceImpl extends ServiceImpl implements RoutingService { private BomMapper bomMapper; private BasicParamTemplateMapper templateMapper; private DictUtils dictUtils; private IfsFeignClient ifsFeignClient; private OperationMapper operationMapper; private ParamMapper paramMapper; private PartMapper partMapper; private RoutingOperationMapper routingOperationMapper; private RoutingOperationParamMapper routingOperationParamMapper; private RoutingOperationRefMapper routingOperationRefMapper; private RoutingOperationTemplateMapper routingOperationTemplateMapper; private StateMachineFactory routingStateMachineFactory; private StateMachinePersister persister; private NumberGenerator numberGenerator; private NumberGenerator newNumberGenerator; @Override public IPage> getPage(Page page, QueryWrapper ew) { return baseMapper.getRoutingPage(page, ew); } @Override public RoutingDTO getRoutingById(Long id) { RoutingDTO routing = baseMapper.getRoutingDtoById(id); setIndex(routing); return routing; } private void setIndex(RoutingDTO routing) { if (null == routing) { return; } List operations = routing.getOperations(); if (CollectionUtil.isEmpty(operations)) { return; } if (operations.size() == 1) { operations.get(0).setIndex(1); return; } Set roIds = operations.stream().map(r -> r.getId()).collect(Collectors.toSet()); List refs = routing.getRefs(); Map refMap = refs.stream().collect(Collectors.toMap(RefDTO::getFromId, RefDTO::getToId)); //找到第一个节点 final Long first = roIds.stream().filter(id -> refMap.keySet().contains(id) && !refMap.values().contains(id)).findFirst().get(); operations.stream().filter(e -> e.getId().equals(first)).findFirst().get().setIndex(1); Long fromId = first; int index = 2; while (refMap.get(fromId) != null) { Long toId = refMap.get(fromId); if (toId == null) { break; } operations.stream().filter(o -> o.getId().equals(toId)).findAny().get().setIndex(index); fromId = toId; index++; } routing.setOperations(operations.stream().sorted(Comparator.comparing(RoutingOperationDTO::getIndex)) .collect(Collectors.toList())); } public List getByRoutingId(Long id) { List routing = baseMapper.getRoutingById(id); if (routing == null) { throw new RuntimeException("工艺不存在"); } return routing; } @Override public RoutingDTO fullSave(RoutingDTO routingDTO) { if (CollectionUtil.isEmpty(routingDTO.getOperations())) { throw new RuntimeException("请添加工序"); } List operations = routingDTO.getOperations().stream().sorted().collect(Collectors.toList()); //判断编号是重复 int noCount = baseMapper.selectCount(Wrappers.lambdaQuery().eq(Routing::getRoutingNo, routingDTO.getRoutingNo())); if (noCount > 0) { throw new RuntimeException("编号重复"); } Bom bom = bomMapper.selectById(routingDTO.getBomId()); if(Objects.equals(BomStateStringValues.DRAFT,bom.getState())){ throw new RuntimeException("BOM状态未通过!"); } //主表中【零件号】、【工艺版本】、【替代】值的组合须唯一。 noCount = baseMapper.selectCount(Wrappers.lambdaQuery().eq(Routing::getPartId, routingDTO.getPartId()) .eq(Routing::getBomTypeDb, routingDTO.getBomTypeDb()) .eq(Routing::getAlternativeNo, routingDTO.getAlternativeNo())); if (noCount > 0) { throw new RuntimeException("该版本零件已存在工艺路线"); } routingDTO.setRoutingNo(numberGenerator.generateNumberWithPrefix(Routing.DIGIT, Routing.PREFIX, Routing::getRoutingNo)); baseMapper.insert(routingDTO); for (int num = 0; num < operations.size(); num++) { RoutingOperationDTO op = operations.get(num); if (num + 1 == operations.size()) { op.setIsLast(true); } else { op.setIsLast(false); } op.setOperationOrder(op.getIndex()); op.setRoutingId(routingDTO.getId()); routingOperationMapper.insert(op); //复制工序模板和工序模板参数 copyTemplateParam(op); } createRef(routingDTO.getId(), operations); RoutingDTO routing = getRoutingById(routingDTO.getId()); //对接IFS routingIfsSync(Arrays.asList(routing.getId())); return routing; } @Override public RoutingDTO fullUpdate(RoutingDTO routingDTO) { if (CollectionUtil.isEmpty(routingDTO.getOperations())) { throw new RuntimeException("请添加工序"); } List operations = routingDTO.getOperations().stream().sorted().collect(Collectors.toList()); int noCount = baseMapper.selectCount(Wrappers.lambdaQuery().ne(Routing::getId, routingDTO.getId()) .eq(Routing::getRoutingNo, routingDTO.getRoutingNo())); if (noCount > 0) { throw new RuntimeException("编号重复"); } //主表中【零件号】、【工艺版本】、【替代】值的组合须唯一。 noCount = baseMapper.selectCount(Wrappers.lambdaQuery().ne(Routing::getId, routingDTO.getId()) .eq(Routing::getPartId, routingDTO.getPartId()) .eq(Routing::getBomTypeDb, routingDTO.getBomTypeDb()) .eq(Routing::getAlternativeNo, routingDTO.getAlternativeNo())); if (noCount > 0) { throw new RuntimeException("该版本零件已存在工艺路线"); } routingDTO.setIfsSync(false); baseMapper.updateById(routingDTO); List dbOperations = routingOperationMapper.selectList(Wrappers.query().lambda().eq(RoutingOperation::getRoutingId, routingDTO.getId())); Set dbOperationIds = new HashSet<>(); if (!CollectionUtil.isEmpty(dbOperations)) { dbOperationIds = dbOperations.stream().map(o -> o.getId()).collect(Collectors.toSet()); //删除原有关系记录 routingOperationRefMapper.delete(Wrappers.lambdaQuery().eq(RoutingOperationRef::getRoutingId, routingDTO.getId())); } for (int num = 0; num < operations.size(); num++) { RoutingOperationDTO op = operations.get(num); if (num + 1 == operations.size()) { op.setIsLast(true); } else { op.setIsLast(false); } op.setRoutingId(routingDTO.getId()); op.setOperationOrder(op.getIndex()); if (op.getId() != null && op.getId() > 0) { routingOperationMapper.updateById(op); } else { routingOperationMapper.insert(op); //复制工序模板和工序模板参数 copyTemplateParam(op); } dbOperationIds.remove(op.getId()); } //删除不要的工序节点 if (CollectionUtil.isNotEmpty(dbOperationIds)) { routingOperationMapper.delete(Wrappers.lambdaQuery().in(RoutingOperation::getId, dbOperationIds)); routingOperationTemplateMapper.delete(Wrappers.lambdaQuery().in(RoutingOperationTemplate::getRoutingOperationId, dbOperationIds)); routingOperationParamMapper.delete(Wrappers.lambdaQuery().in(RoutingOperationParam::getRoutingOperationId, dbOperationIds)); } createRef(routingDTO.getId(), operations); RoutingDTO routing = getRoutingById(routingDTO.getId()); //对接IFS routingIfsSync(Arrays.asList(routing.getId())); return routing; } @Override public boolean routingOperationChange(RoutingOperationDTO routingOperationDTO) { routingOperationMapper.updateById(routingOperationDTO); return true; } @Override public R changeState(Long id, String event) { Routing routing = baseMapper.selectById(id); Message message = MessageBuilder.withPayload(RoutingEvents.valueOf(event)).setHeader("routing", routing).build(); StateMachineHandler handler = new StateMachineHandler(routingStateMachineFactory, persister, RoutingStateMachineConfig.MACHINE_ID, routing); StateResult res = handler.sendEvent(message, routing.getId()); if (res.isSuccess()) { return R.ok(); } else { return R.failed(res.getMsg()); } } @Override public boolean changeState(List ids, String event) { for (Long id : ids) { Routing routing = baseMapper.selectById(id); Message message = MessageBuilder.withPayload(RoutingEvents.valueOf(event)).setHeader("routing", routing).build(); StateMachineHandler handler = new StateMachineHandler(routingStateMachineFactory, persister, RoutingStateMachineConfig.MACHINE_ID, routing); StateResult res = handler.sendEvent(message, routing.getId()); if (!res.isSuccess()) { throw new RuntimeException(res.getMsg()); } } return true; } /** * 复制工序模板和工序参数 */ public void copyTemplateParam(RoutingOperationDTO op) { //通过工序id获取工序模板 List