gongchunyi
15 小时以前 c30bfeb3840c69f0ad7eeb0ff713d63da7494b23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
package com.ruoyi.technology.service.impl;
 
import cn.hutool.core.bean.BeanUtil;
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.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.technology.bean.dto.TechnologyRoutingDto;
import com.ruoyi.technology.bean.vo.TechnologyRoutingVo;
import com.ruoyi.production.mapper.ProductionOrderRoutingMapper;
import com.ruoyi.production.pojo.ProductionOrderRouting;
import com.ruoyi.technology.mapper.*;
import com.ruoyi.technology.pojo.*;
import com.ruoyi.technology.service.TechnologyRoutingService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
 
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class TechnologyRoutingServiceImpl extends ServiceImpl<TechnologyRoutingMapper, TechnologyRouting> implements TechnologyRoutingService {
 
    private final TechnologyRoutingMapper technologyRoutingMapper;
 
    private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper;
 
    private final TechnologyBomStructureMapper technologyBomStructureMapper;
 
    private final TechnologyOperationMapper technologyOperationMapper;
    private final TechnologyOperationParamMapper technologyOperationParamMapper;
    private final TechnologyParamMapper technologyParamMapper;
    private final TechnologyRoutingOperationParamMapper technologyRoutingOperationParamMapper;
 
    private final ProductionOrderRoutingMapper productionOrderRoutingMapper;
 
    @Override
    public IPage<TechnologyRoutingVo> pageTechnologyRouting(Page<TechnologyRoutingDto> page, TechnologyRoutingDto technologyRoutingDto) {
        return technologyRoutingMapper.pageTechnologyRouting(page, technologyRoutingDto);
    }
 
    @Override
    public TechnologyRoutingVo getTechnologyRoutingInfo(Long id) {
        TechnologyRouting technologyRouting = this.getById(id);
        if (technologyRouting == null) {
            return null;
        }
        return BeanUtil.copyProperties(technologyRouting, TechnologyRoutingVo.class);
    }
 
    @Override
    public Long saveTechnologyRouting(TechnologyRouting technologyRouting) {
        String code = OrderUtils.countTodayByCreateTime(technologyRoutingMapper, "GYLX", "process_route_code");
        technologyRouting.setProcessRouteCode(code);
        technologyRoutingMapper.insert(technologyRouting);
        // 带入bom产品结构
        syncRoutingOperationsByBom(technologyRouting);
        return technologyRouting.getId();
    }
 
    @Override
    public boolean updateTechnologyRouting(TechnologyRouting technologyRouting) {
        boolean updated = this.updateById(technologyRouting);
        if (updated) {
            TechnologyRouting currentRouting = this.getById(technologyRouting.getId());
            syncRoutingOperationsByBom(currentRouting);
        }
        return updated;
    }
 
    @Override
    public boolean removeTechnologyRouting(List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return false;
        }
        // 判断是否有生产订单绑定该工艺路线
        Long count = productionOrderRoutingMapper.selectCount(
                Wrappers.<ProductionOrderRouting>lambdaQuery()
                        .in(ProductionOrderRouting::getTechnologyRoutingId, ids));
        if (count > 0) {
            throw new ServiceException("该工艺路线已绑定生产订单,无法删除!");
        }
        List<TechnologyRoutingOperation> routingOperationList = technologyRoutingOperationMapper.selectList(
                Wrappers.<TechnologyRoutingOperation>lambdaQuery()
                        .in(TechnologyRoutingOperation::getTechnologyRoutingId, ids));
        if (!routingOperationList.isEmpty()) {
            List<Long> routingOperationIdList = routingOperationList.stream().map(TechnologyRoutingOperation::getId).collect(Collectors.toList());
            technologyRoutingOperationParamMapper.delete(Wrappers.<TechnologyRoutingOperationParam>lambdaQuery()
                    .in(TechnologyRoutingOperationParam::getTechnologyRoutingOperationId, routingOperationIdList));
        }
        technologyRoutingOperationMapper.delete(Wrappers.<TechnologyRoutingOperation>lambdaQuery()
                .in(TechnologyRoutingOperation::getTechnologyRoutingId, ids));
        return this.removeByIds(ids);
    }
 
    /**
     * 按当前选择的 BOM 重建工艺路线工序:
     * 1. 先删除该工艺路线下已有工序
     * 2. 读取 BOM 结构中的 operation_id
     * 3. 按 BOM 出现顺序去重后重新写入工艺路线工序表
     */
    private void syncRoutingOperationsByBom(TechnologyRouting technologyRouting) {
        List<TechnologyRoutingOperation> oldRoutingOperationList = technologyRoutingOperationMapper.selectList(
                Wrappers.<TechnologyRoutingOperation>lambdaQuery()
                        .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId()));
        if (!oldRoutingOperationList.isEmpty()) {
            List<Long> oldRoutingOperationIdList = oldRoutingOperationList.stream().map(TechnologyRoutingOperation::getId).collect(Collectors.toList());
            technologyRoutingOperationParamMapper.delete(Wrappers.<TechnologyRoutingOperationParam>lambdaQuery()
                    .in(TechnologyRoutingOperationParam::getTechnologyRoutingOperationId, oldRoutingOperationIdList));
            technologyRoutingOperationMapper.delete(Wrappers.<TechnologyRoutingOperation>lambdaQuery()
                    .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId()));
        }
        if (technologyRouting.getBomId() == null) {
            return;
        }
 
        List<TechnologyBomStructure> bomStructures = technologyBomStructureMapper.selectList(
                Wrappers.<TechnologyBomStructure>lambdaQuery()
                        .eq(TechnologyBomStructure::getBomId, technologyRouting.getBomId())
                        .orderByAsc(TechnologyBomStructure::getId)
        );
        if (bomStructures.isEmpty()) {
            throw new ServiceException("bom产品结构为空!");
        }
 
        Map<Long, TechnologyBomStructure> structureById = new HashMap<>();
        Map<Long, List<TechnologyBomStructure>> childrenByParentId = new LinkedHashMap<>();
        List<TechnologyBomStructure> rootList = new ArrayList<>();
        for (TechnologyBomStructure bomStructure : bomStructures) {
            if (bomStructure == null) {
                continue;
            }
            if (bomStructure.getId() != null) {
                structureById.put(bomStructure.getId(), bomStructure);
            }
            if (bomStructure.getParentId() == null || bomStructure.getParentId() == 0L) {
                rootList.add(bomStructure);
            } else {
                childrenByParentId.computeIfAbsent(bomStructure.getParentId(), key -> new ArrayList<>()).add(bomStructure);
            }
        }
 
        // 后序遍历BOM
        List<TechnologyBomStructure> orderedOperationNodes = new ArrayList<>();
        Set<Long> visitingIds = new HashSet<>();
        for (TechnologyBomStructure rootNode : rootList) {
            collectOperationNodePostOrder(rootNode, childrenByParentId, orderedOperationNodes, visitingIds);
        }
        if (orderedOperationNodes.isEmpty()) {
            throw new ServiceException("bom产品结构未配置消耗工序!");
        }
 
        // 同一层级(同一父节点)内相同工序合并为一道工序,例如同层的多个压接件合并成一道压接;
        // 合并后的位置取该工序最后一次出现的位置,确保其全部输入物料的工序都在它之前
        Map<String, Integer> lastPositionByKey = new HashMap<>();
        Map<String, TechnologyBomStructure> operationNodeByKey = new LinkedHashMap<>();
        for (int i = 0; i < orderedOperationNodes.size(); i++) {
            TechnologyBomStructure bomStructure = orderedOperationNodes.get(i);
            String dedupKey = buildBomOperationDedupKey(bomStructure);
            lastPositionByKey.put(dedupKey, i);
            operationNodeByKey.putIfAbsent(dedupKey, bomStructure);
        }
        List<String> sortedDedupKeys = new ArrayList<>(operationNodeByKey.keySet());
        sortedDedupKeys.sort(Comparator.comparingInt(lastPositionByKey::get));
 
        int dragSort = 1;
        for (String dedupKey : sortedDedupKeys) {
            TechnologyBomStructure bomStructure = operationNodeByKey.get(dedupKey);
            TechnologyRoutingOperation routingOperation = new TechnologyRoutingOperation();
            routingOperation.setTechnologyRoutingId(technologyRouting.getId());
            routingOperation.setProductModelId(resolveOutputProductModelId(bomStructure, structureById, technologyRouting.getProductModelId()));
            routingOperation.setTechnologyOperationId(bomStructure.getOperationId());
            routingOperation.setDragSort(dragSort++);
            TechnologyOperation technologyOperation = getOperation(bomStructure.getOperationId());
            routingOperation.setIsQuality(technologyOperation != null ? technologyOperation.getIsQuality() : null);
            routingOperation.setIsProduction(technologyOperation != null ? technologyOperation.getIsProduction() : null);
            routingOperation.setType(technologyOperation != null ? technologyOperation.getType() : null);
            technologyRoutingOperationMapper.insert(routingOperation);
            syncRoutingOperationParams(routingOperation.getId(), bomStructure.getOperationId());
        }
    }
 
    /**
     * 后序遍历 BOM
     */
    private void collectOperationNodePostOrder(TechnologyBomStructure node,
                                               Map<Long, List<TechnologyBomStructure>> childrenByParentId,
                                               List<TechnologyBomStructure> orderedOperationNodes,
                                               Set<Long> visitingIds) {
        if (node == null || node.getId() == null) {
            return;
        }
        if (!visitingIds.add(node.getId())) {
            return;
        }
        List<TechnologyBomStructure> children = childrenByParentId.get(node.getId());
        if (children != null && !children.isEmpty()) {
            for (TechnologyBomStructure child : children) {
                collectOperationNodePostOrder(child, childrenByParentId, orderedOperationNodes, visitingIds);
            }
        }
        visitingIds.remove(node.getId());
        if (node.getOperationId() != null) {
            orderedOperationNodes.add(node);
        }
    }
 
    /**
     * 去重键:同一父节点 + 相同工序(只在同一层级内合并,不同层级、不同分支的相同工序不会合并)。
     */
    private String buildBomOperationDedupKey(TechnologyBomStructure bomStructure) {
        Long operationId = bomStructure == null ? null : bomStructure.getOperationId();
        Long parentId = bomStructure == null ? null : bomStructure.getParentId();
        return operationId + "#" + (parentId == null ? "root" : String.valueOf(parentId));
    }
 
    /**
     * 工序产出产品 = 父节点的产品(本行物料由父节点层级的消耗工序消耗,产出的是上一层产品,
     * 而不是本行物料自身);仅当父节点缺失或父节点没有产品时,才回退到本节点自身产品、再回退到工艺路线产品。
     */
    private Long resolveOutputProductModelId(TechnologyBomStructure bomStructure,
                                             Map<Long, TechnologyBomStructure> structureById,
                                             Long routingProductModelId) {
        if (bomStructure == null) {
            return routingProductModelId;
        }
        Long parentId = bomStructure.getParentId();
        if (parentId != null && parentId != 0L && structureById != null) {
            TechnologyBomStructure parent = structureById.get(parentId);
            if (parent != null && parent.getProductModelId() != null) {
                return parent.getProductModelId();
            }
        }
        if (bomStructure.getProductModelId() != null) {
            return bomStructure.getProductModelId();
        }
        return routingProductModelId;
    }
 
    private void syncRoutingOperationParams(Long technologyRoutingOperationId, Long technologyOperationId) {
        List<TechnologyOperationParam> operationParamList = technologyOperationParamMapper.selectList(
                Wrappers.<TechnologyOperationParam>lambdaQuery()
                        .eq(TechnologyOperationParam::getTechnologyOperationId, technologyOperationId)
                        .orderByAsc(TechnologyOperationParam::getId));
        if (operationParamList.isEmpty()) {
            return;
        }
        for (TechnologyOperationParam operationParam : operationParamList) {
            TechnologyParam technologyParam = technologyParamMapper.selectById(operationParam.getTechnologyParamId());
            if (technologyParam == null) {
                continue;
            }
            TechnologyRoutingOperationParam snapshot = new TechnologyRoutingOperationParam();
            snapshot.setTechnologyRoutingOperationId(technologyRoutingOperationId);
            snapshot.setTechnologyOperationParamId(operationParam.getId());
            snapshot.setTechnologyOperationId(operationParam.getTechnologyOperationId());
            snapshot.setParamId(technologyParam.getId());
            snapshot.setParamCode(technologyParam.getParamCode());
            snapshot.setParamName(technologyParam.getParamName());
            snapshot.setParamType(technologyParam.getParamType());
            snapshot.setParamFormat(technologyParam.getParamFormat());
            snapshot.setUnit(technologyParam.getUnit());
            snapshot.setIsRequired(technologyParam.getIsRequired());
            snapshot.setRemark(technologyParam.getRemark());
            snapshot.setStandardValue(operationParam.getStandardValue());
            technologyRoutingOperationParamMapper.insert(snapshot);
        }
    }
 
    private TechnologyOperation getOperation(Long operationId) {
        if (operationId == null) {
            return null;
        }
        return technologyOperationMapper.selectById(operationId);
    }
 
    private String buildProcessRouteCode(Long id) {
        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String idStr = String.format("%06d", id);
        return "GYLX" + dateStr + idStr;
    }
 
    private boolean isNotBlank(String value) {
        return value != null && !value.trim().isEmpty();
    }
}