package com.ruoyi.production.service.impl;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.ruoyi.basic.mapper.ProductModelMapper;
|
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.production.bean.dto.ProductionBomStructureDto;
|
import com.ruoyi.production.bean.vo.ProductionBomStructureVo;
|
import com.ruoyi.production.mapper.ProductionBomStructureMapper;
|
import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
|
import com.ruoyi.production.mapper.ProductionOrderBomMapper;
|
import com.ruoyi.production.mapper.ProductionOrderMapper;
|
import com.ruoyi.production.mapper.ProductionOrderRoutingMapper;
|
import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper;
|
import com.ruoyi.production.mapper.ProductionOrderRoutingOperationParamMapper;
|
import com.ruoyi.production.mapper.ProductionProductMainMapper;
|
import com.ruoyi.production.pojo.ProductionBomStructure;
|
import com.ruoyi.production.pojo.ProductionOperationTask;
|
import com.ruoyi.production.pojo.ProductionOrder;
|
import com.ruoyi.production.pojo.ProductionOrderBom;
|
import com.ruoyi.production.pojo.ProductionOrderRouting;
|
import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
|
import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam;
|
import com.ruoyi.production.pojo.ProductionProductMain;
|
import com.ruoyi.production.service.ProductionBomStructureService;
|
import com.ruoyi.production.util.TaskPlanQuantityUtil;
|
import com.ruoyi.technology.mapper.TechnologyOperationMapper;
|
import com.ruoyi.technology.mapper.TechnologyOperationParamMapper;
|
import com.ruoyi.technology.mapper.TechnologyParamMapper;
|
import com.ruoyi.technology.pojo.TechnologyOperation;
|
import com.ruoyi.technology.pojo.TechnologyOperationParam;
|
import com.ruoyi.technology.pojo.TechnologyParam;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.math.BigDecimal;
|
import java.time.LocalDate;
|
import java.time.format.DateTimeFormatter;
|
import java.util.*;
|
import java.util.stream.Collectors;
|
|
/**
|
* <p>
|
* 生产订单BOM产品结构 服务实现类
|
* </p>
|
*
|
* @author 芯导软件(江苏)有限公司
|
* @since 2026-04-21 03:55:52
|
*/
|
@Service
|
@RequiredArgsConstructor()
|
public class ProductionBomStructureServiceImpl extends ServiceImpl<ProductionBomStructureMapper, ProductionBomStructure> implements ProductionBomStructureService {
|
|
private final ProductionBomStructureMapper productionBomStructureMapper;
|
private final ProductionOrderBomMapper productionOrderBomMapper;
|
private final ProductionOrderMapper productionOrderMapper;
|
private final ProductionOrderRoutingMapper productionOrderRoutingMapper;
|
private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
|
private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper;
|
private final ProductionOperationTaskMapper productionOperationTaskMapper;
|
private final ProductionProductMainMapper productionProductMainMapper;
|
private final ProductModelMapper productModelMapper;
|
private final TechnologyOperationMapper technologyOperationMapper;
|
private final TechnologyOperationParamMapper technologyOperationParamMapper;
|
private final TechnologyParamMapper technologyParamMapper;
|
|
/**
|
* 按订单BOM查询扁平物料明细。
|
*/
|
@Override
|
public List<ProductionBomStructureVo> listByBomId(Long bomId) {
|
if (bomId == null) {
|
return new ArrayList<>();
|
}
|
return productionBomStructureMapper.listByBomId(bomId);
|
}
|
|
/**
|
* 按生产订单查询订单BOM快照的扁平物料明细。
|
*/
|
@Override
|
public List<ProductionBomStructureVo> listByOrderId(Long productionOrderId) {
|
if (productionOrderId == null) {
|
return new ArrayList<>();
|
}
|
ProductionOrderBom orderBom = productionOrderBomMapper.selectOne(
|
Wrappers.<ProductionOrderBom>lambdaQuery()
|
.eq(ProductionOrderBom::getProductionOrderId, productionOrderId)
|
.orderByDesc(ProductionOrderBom::getId)
|
.last("limit 1"));
|
if (orderBom == null || orderBom.getId() == null) {
|
return new ArrayList<>();
|
}
|
return listByBomId(orderBom.getId());
|
}
|
|
/**
|
* 全量同步订单BOM快照的扁平物料明细,并按其中的工序重建订单工艺路线快照与工单。
|
*/
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Boolean addProductionBomStructure(ProductionBomStructureDto dto) {
|
Long orderBomId = dto.getProductionOrderBomId();
|
if (orderBomId == null) {
|
throw new ServiceException("订单BOM ID不能为空");
|
}
|
ProductionOrderBom orderBom = productionOrderBomMapper.selectById(orderBomId);
|
if (orderBom == null) {
|
throw new ServiceException("订单BOM不存在");
|
}
|
Long productionOrderId = dto.getProductionOrderId() != null
|
? dto.getProductionOrderId()
|
: orderBom.getProductionOrderId();
|
|
List<ProductionBomStructureDto> detailList = dto.getDetailList() == null
|
? Collections.emptyList()
|
: dto.getDetailList();
|
|
List<ProductionBomStructure> dbList = this.list(new LambdaQueryWrapper<ProductionBomStructure>()
|
.eq(ProductionBomStructure::getProductionOrderBomId, orderBomId));
|
Set<Long> dbIds = dbList.stream().map(ProductionBomStructure::getId).collect(Collectors.toSet());
|
|
Set<Long> submittedIds = new HashSet<>();
|
Set<String> relationKeys = new HashSet<>();
|
List<ProductionBomStructure> insertList = new ArrayList<>();
|
List<ProductionBomStructure> updateList = new ArrayList<>();
|
|
for (ProductionBomStructureDto item : detailList) {
|
Long productModelId = item.getProductModelId();
|
Long technologyOperationId = item.getTechnologyOperationId();
|
if (productModelId == null) {
|
throw new ServiceException("请选择物料规格");
|
}
|
if (productModelMapper.selectById(productModelId) == null) {
|
throw new ServiceException("物料规格不存在");
|
}
|
if (technologyOperationId == null) {
|
throw new ServiceException("请选择消耗工序");
|
}
|
if (technologyOperationMapper.selectById(technologyOperationId) == null) {
|
throw new ServiceException("消耗工序不存在");
|
}
|
if (productModelId.equals(orderBom.getProductModelId())) {
|
throw new ServiceException("成品规格不能作为自身的物料");
|
}
|
if (!relationKeys.add(productModelId + "#" + technologyOperationId)) {
|
throw new ServiceException("同一物料规格在同一工序下不能重复配置");
|
}
|
|
ProductionBomStructure entity = new ProductionBomStructure();
|
entity.setProductionOrderBomId(orderBomId);
|
entity.setProductionOrderId(productionOrderId);
|
entity.setProductModelId(productModelId);
|
entity.setTechnologyOperationId(technologyOperationId);
|
// 扁平快照不再使用层级与数量语义
|
entity.setParentId(null);
|
entity.setUnitQuantity(null);
|
entity.setDemandedQuantity(null);
|
entity.setUnit(null);
|
|
if (item.getId() == null) {
|
insertList.add(entity);
|
} else {
|
if (!dbIds.contains(item.getId())) {
|
throw new ServiceException("订单BOM物料明细不存在或不属于当前订单BOM");
|
}
|
entity.setId(item.getId());
|
submittedIds.add(item.getId());
|
updateList.add(entity);
|
}
|
}
|
|
Set<Long> deleteIds = new HashSet<>(dbIds);
|
deleteIds.removeAll(submittedIds);
|
if (!deleteIds.isEmpty()) {
|
this.removeByIds(deleteIds);
|
}
|
if (!updateList.isEmpty()) {
|
this.updateBatchById(updateList);
|
}
|
if (!insertList.isEmpty()) {
|
this.saveBatch(insertList);
|
}
|
|
syncRoutingAndTaskByStructure(orderBomId, productionOrderId);
|
return true;
|
}
|
|
/**
|
* 订单BOM物料变化后,按其工序集合重建订单工艺路线工序快照与工单计划数量。
|
*/
|
private void syncRoutingAndTaskByStructure(Long orderBomId, Long productionOrderId) {
|
if (orderBomId == null) {
|
return;
|
}
|
ProductionOrderBom orderBom = productionOrderBomMapper.selectById(orderBomId);
|
if (orderBom == null) {
|
return;
|
}
|
Long currentProductionOrderId = productionOrderId != null ? productionOrderId : orderBom.getProductionOrderId();
|
if (currentProductionOrderId == null) {
|
return;
|
}
|
ProductionOrder productionOrder = productionOrderMapper.selectById(currentProductionOrderId);
|
if (productionOrder == null) {
|
return;
|
}
|
|
List<ProductionBomStructure> structureList = this.list(
|
Wrappers.<ProductionBomStructure>lambdaQuery()
|
.eq(ProductionBomStructure::getProductionOrderBomId, orderBomId)
|
.orderByAsc(ProductionBomStructure::getId));
|
Long rootProductModelId = orderBom.getProductModelId() != null
|
? orderBom.getProductModelId()
|
: productionOrder.getProductModelId();
|
// 同步生产工艺路线快照
|
syncRoutingOperationsByBom(currentProductionOrderId, productionOrder, orderBom, structureList, rootProductModelId);
|
// 同步工单计划数量:扁平BOM无数量配置,计划数量统一取订单数量
|
syncTaskPlanQuantity(currentProductionOrderId, TaskPlanQuantityUtil.resolveTaskPlanQuantity(productionOrder));
|
}
|
|
private void syncTaskPlanQuantity(Long productionOrderId, BigDecimal planQuantity) {
|
List<ProductionOperationTask> taskList = productionOperationTaskMapper.selectList(
|
Wrappers.<ProductionOperationTask>lambdaQuery()
|
.eq(ProductionOperationTask::getProductionOrderId, productionOrderId)
|
.orderByAsc(ProductionOperationTask::getId));
|
if (taskList == null || taskList.isEmpty()) {
|
return;
|
}
|
for (ProductionOperationTask task : taskList) {
|
if (task == null || task.getId() == null) {
|
continue;
|
}
|
if (compareDecimal(task.getPlanQuantity(), planQuantity) == 0) {
|
continue;
|
}
|
ProductionOperationTask update = new ProductionOperationTask();
|
update.setId(task.getId());
|
update.setPlanQuantity(planQuantity);
|
productionOperationTaskMapper.updateById(update);
|
}
|
}
|
|
private void syncRoutingOperationsByBom(Long productionOrderId,
|
ProductionOrder productionOrder,
|
ProductionOrderBom orderBom,
|
List<ProductionBomStructure> structureList,
|
Long rootProductModelId) {
|
ProductionOrderRouting orderRouting = getOrCreateOrderRoutingSnapshot(productionOrderId, productionOrder, orderBom, rootProductModelId);
|
List<ProductionOrderRoutingOperation> desiredOperationList = buildDesiredRoutingOperationList(structureList, rootProductModelId);
|
List<ProductionOrderRoutingOperation> existingOperationList = productionOrderRoutingOperationMapper.selectList(
|
Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
|
.eq(ProductionOrderRoutingOperation::getOrderRoutingId, orderRouting.getId())
|
.eq(ProductionOrderRoutingOperation::getProductionOrderId, productionOrderId)
|
.orderByAsc(ProductionOrderRoutingOperation::getDragSort)
|
.orderByAsc(ProductionOrderRoutingOperation::getId));
|
Map<String, Deque<ProductionOrderRoutingOperation>> existingBucketMap = buildExistingRoutingOperationBucketMap(existingOperationList);
|
List<ProductionOrderRoutingOperation> finalOperationList = new ArrayList<>();
|
for (ProductionOrderRoutingOperation desiredOperation : desiredOperationList) {
|
String bucketKey = buildRoutingOperationBucketKey(
|
desiredOperation.getTechnologyOperationId(),
|
desiredOperation.getProductModelId());
|
Deque<ProductionOrderRoutingOperation> matchedQueue = existingBucketMap.get(bucketKey);
|
ProductionOrderRoutingOperation matchedOperation = matchedQueue == null ? null : matchedQueue.pollFirst();
|
if (matchedOperation == null) {
|
matchedOperation = insertRoutingOperationSnapshot(orderRouting.getId(), productionOrderId, desiredOperation);
|
} else {
|
updateRoutingOperationSnapshotIfNecessary(matchedOperation, orderRouting.getId(), productionOrderId, desiredOperation);
|
}
|
finalOperationList.add(matchedOperation);
|
}
|
for (Deque<ProductionOrderRoutingOperation> queue : existingBucketMap.values()) {
|
while (queue != null && !queue.isEmpty()) {
|
removeRoutingOperationSnapshot(queue.pollFirst());
|
}
|
}
|
syncRoutingOperationTasks(productionOrderId, finalOperationList);
|
}
|
|
private ProductionOrderRouting getOrCreateOrderRoutingSnapshot(Long productionOrderId,
|
ProductionOrder productionOrder,
|
ProductionOrderBom orderBom,
|
Long rootProductModelId) {
|
ProductionOrderRouting orderRouting = productionOrderRoutingMapper.selectOne(
|
Wrappers.<ProductionOrderRouting>lambdaQuery()
|
.eq(ProductionOrderRouting::getProductionOrderId, productionOrderId)
|
.orderByDesc(ProductionOrderRouting::getId)
|
.last("limit 1"));
|
if (orderRouting == null) {
|
orderRouting = new ProductionOrderRouting();
|
orderRouting.setProductionOrderId(productionOrderId);
|
orderRouting.setProductModelId(rootProductModelId);
|
orderRouting.setTechnologyRoutingId(productionOrder == null ? null : productionOrder.getTechnologyRoutingId());
|
orderRouting.setBomId(orderBom == null ? null : orderBom.getBomId());
|
orderRouting.setOrderBomId(orderBom == null ? null : orderBom.getId());
|
productionOrderRoutingMapper.insert(orderRouting);
|
return orderRouting;
|
}
|
ProductionOrderRouting update = new ProductionOrderRouting();
|
update.setId(orderRouting.getId());
|
boolean changed = false;
|
if (!Objects.equals(orderRouting.getProductModelId(), rootProductModelId)) {
|
update.setProductModelId(rootProductModelId);
|
orderRouting.setProductModelId(rootProductModelId);
|
changed = true;
|
}
|
Long technologyRoutingId = productionOrder == null ? null : productionOrder.getTechnologyRoutingId();
|
if (!Objects.equals(orderRouting.getTechnologyRoutingId(), technologyRoutingId)) {
|
update.setTechnologyRoutingId(technologyRoutingId);
|
orderRouting.setTechnologyRoutingId(technologyRoutingId);
|
changed = true;
|
}
|
Long bomId = orderBom == null ? null : orderBom.getBomId();
|
if (!Objects.equals(orderRouting.getBomId(), bomId)) {
|
update.setBomId(bomId);
|
orderRouting.setBomId(bomId);
|
changed = true;
|
}
|
Long orderBomId = orderBom == null ? null : orderBom.getId();
|
if (!Objects.equals(orderRouting.getOrderBomId(), orderBomId)) {
|
update.setOrderBomId(orderBomId);
|
orderRouting.setOrderBomId(orderBomId);
|
changed = true;
|
}
|
if (changed) {
|
productionOrderRoutingMapper.updateById(update);
|
}
|
return orderRouting;
|
}
|
|
/**
|
* 扁平BOM按工序去重生成期望的订单工序列表,
|
* 顺序取物料明细中工序首次出现的顺序,产出规格固定为订单成品规格。
|
*/
|
private List<ProductionOrderRoutingOperation> buildDesiredRoutingOperationList(List<ProductionBomStructure> structureList,
|
Long rootProductModelId) {
|
if (structureList == null || structureList.isEmpty()) {
|
return Collections.emptyList();
|
}
|
Set<Long> uniqueOperationIdList = new LinkedHashSet<>();
|
for (ProductionBomStructure structure : structureList) {
|
if (structure == null || structure.getTechnologyOperationId() == null) {
|
continue;
|
}
|
uniqueOperationIdList.add(structure.getTechnologyOperationId());
|
}
|
|
List<ProductionOrderRoutingOperation> desiredOperationList = new ArrayList<>(uniqueOperationIdList.size());
|
int dragSort = 1;
|
for (Long operationId : uniqueOperationIdList) {
|
TechnologyOperation technologyOperation = getTechnologyOperation(operationId);
|
ProductionOrderRoutingOperation routingOperation = new ProductionOrderRoutingOperation();
|
routingOperation.setProductModelId(rootProductModelId);
|
routingOperation.setTechnologyOperationId(operationId);
|
routingOperation.setOperationName(technologyOperation == null ? null : technologyOperation.getName());
|
routingOperation.setIsQuality(technologyOperation == null ? null : technologyOperation.getIsQuality());
|
routingOperation.setIsProduction(technologyOperation == null ? null : technologyOperation.getIsProduction());
|
routingOperation.setType(technologyOperation == null ? null : technologyOperation.getType());
|
routingOperation.setDragSort(dragSort++);
|
desiredOperationList.add(routingOperation);
|
}
|
return desiredOperationList;
|
}
|
|
private Map<String, Deque<ProductionOrderRoutingOperation>> buildExistingRoutingOperationBucketMap(List<ProductionOrderRoutingOperation> existingOperationList) {
|
Map<String, Deque<ProductionOrderRoutingOperation>> existingBucketMap = new LinkedHashMap<>();
|
if (existingOperationList == null || existingOperationList.isEmpty()) {
|
return existingBucketMap;
|
}
|
for (ProductionOrderRoutingOperation routingOperation : existingOperationList) {
|
String bucketKey = buildRoutingOperationBucketKey(
|
routingOperation.getTechnologyOperationId(),
|
routingOperation.getProductModelId());
|
existingBucketMap.computeIfAbsent(bucketKey, key -> new ArrayDeque<>()).addLast(routingOperation);
|
}
|
return existingBucketMap;
|
}
|
|
private ProductionOrderRoutingOperation insertRoutingOperationSnapshot(Long orderRoutingId,
|
Long productionOrderId,
|
ProductionOrderRoutingOperation desiredOperation) {
|
ProductionOrderRoutingOperation insert = new ProductionOrderRoutingOperation();
|
insert.setOrderRoutingId(orderRoutingId);
|
insert.setProductionOrderId(productionOrderId);
|
insert.setProductModelId(desiredOperation.getProductModelId());
|
insert.setTechnologyOperationId(desiredOperation.getTechnologyOperationId());
|
insert.setOperationName(desiredOperation.getOperationName());
|
insert.setIsQuality(desiredOperation.getIsQuality());
|
insert.setIsProduction(desiredOperation.getIsProduction());
|
insert.setType(desiredOperation.getType());
|
insert.setDragSort(desiredOperation.getDragSort());
|
productionOrderRoutingOperationMapper.insert(insert);
|
syncRoutingOperationParams(insert.getId(), productionOrderId, insert.getTechnologyOperationId());
|
return insert;
|
}
|
|
private void updateRoutingOperationSnapshotIfNecessary(ProductionOrderRoutingOperation currentOperation,
|
Long orderRoutingId,
|
Long productionOrderId,
|
ProductionOrderRoutingOperation desiredOperation) {
|
if (currentOperation == null || currentOperation.getId() == null) {
|
return;
|
}
|
ProductionOrderRoutingOperation update = new ProductionOrderRoutingOperation();
|
update.setId(currentOperation.getId());
|
boolean changed = false;
|
if (!Objects.equals(currentOperation.getOrderRoutingId(), orderRoutingId)) {
|
update.setOrderRoutingId(orderRoutingId);
|
currentOperation.setOrderRoutingId(orderRoutingId);
|
changed = true;
|
}
|
if (!Objects.equals(currentOperation.getProductionOrderId(), productionOrderId)) {
|
update.setProductionOrderId(productionOrderId);
|
currentOperation.setProductionOrderId(productionOrderId);
|
changed = true;
|
}
|
if (!Objects.equals(currentOperation.getProductModelId(), desiredOperation.getProductModelId())) {
|
update.setProductModelId(desiredOperation.getProductModelId());
|
currentOperation.setProductModelId(desiredOperation.getProductModelId());
|
changed = true;
|
}
|
if (!Objects.equals(currentOperation.getTechnologyOperationId(), desiredOperation.getTechnologyOperationId())) {
|
update.setTechnologyOperationId(desiredOperation.getTechnologyOperationId());
|
currentOperation.setTechnologyOperationId(desiredOperation.getTechnologyOperationId());
|
changed = true;
|
}
|
if (!Objects.equals(currentOperation.getOperationName(), desiredOperation.getOperationName())) {
|
update.setOperationName(desiredOperation.getOperationName());
|
currentOperation.setOperationName(desiredOperation.getOperationName());
|
changed = true;
|
}
|
if (!Objects.equals(currentOperation.getIsQuality(), desiredOperation.getIsQuality())) {
|
update.setIsQuality(desiredOperation.getIsQuality());
|
currentOperation.setIsQuality(desiredOperation.getIsQuality());
|
changed = true;
|
}
|
if (!Objects.equals(currentOperation.getIsProduction(), desiredOperation.getIsProduction())) {
|
update.setIsProduction(desiredOperation.getIsProduction());
|
currentOperation.setIsProduction(desiredOperation.getIsProduction());
|
changed = true;
|
}
|
if (!Objects.equals(currentOperation.getType(), desiredOperation.getType())) {
|
update.setType(desiredOperation.getType());
|
currentOperation.setType(desiredOperation.getType());
|
changed = true;
|
}
|
if (!Objects.equals(currentOperation.getDragSort(), desiredOperation.getDragSort())) {
|
update.setDragSort(desiredOperation.getDragSort());
|
currentOperation.setDragSort(desiredOperation.getDragSort());
|
changed = true;
|
}
|
if (changed) {
|
productionOrderRoutingOperationMapper.updateById(update);
|
}
|
}
|
|
private void removeRoutingOperationSnapshot(ProductionOrderRoutingOperation routingOperation) {
|
if (routingOperation == null || routingOperation.getId() == null) {
|
return;
|
}
|
ProductionOperationTask task = productionOperationTaskMapper.selectOne(
|
Wrappers.<ProductionOperationTask>lambdaQuery()
|
.eq(ProductionOperationTask::getProductionOrderRoutingOperationId, routingOperation.getId())
|
.last("limit 1"));
|
if (task != null) {
|
validateTaskCanRemove(task);
|
productionOperationTaskMapper.deleteById(task.getId());
|
}
|
productionOrderRoutingOperationParamMapper.delete(
|
Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
|
.eq(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, routingOperation.getId()));
|
productionOrderRoutingOperationMapper.deleteById(routingOperation.getId());
|
}
|
|
private void syncRoutingOperationTasks(Long productionOrderId, List<ProductionOrderRoutingOperation> routingOperationList) {
|
if (routingOperationList == null || routingOperationList.isEmpty()) {
|
return;
|
}
|
List<Long> routingOperationIdList = routingOperationList.stream()
|
.map(ProductionOrderRoutingOperation::getId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toList());
|
if (routingOperationIdList.isEmpty()) {
|
return;
|
}
|
Map<Long, ProductionOperationTask> taskByRoutingOperationId = productionOperationTaskMapper.selectList(
|
Wrappers.<ProductionOperationTask>lambdaQuery()
|
.in(ProductionOperationTask::getProductionOrderRoutingOperationId, routingOperationIdList)
|
.orderByAsc(ProductionOperationTask::getId))
|
.stream()
|
.filter(item -> item != null && item.getProductionOrderRoutingOperationId() != null)
|
.collect(Collectors.toMap(
|
ProductionOperationTask::getProductionOrderRoutingOperationId,
|
item -> item,
|
(left, right) -> left,
|
LinkedHashMap::new));
|
for (int i = 0; i < routingOperationList.size(); i++) {
|
ProductionOrderRoutingOperation routingOperation = routingOperationList.get(i);
|
if (routingOperation == null || routingOperation.getId() == null) {
|
continue;
|
}
|
boolean shouldHaveTask = i == routingOperationList.size() - 1 || Boolean.TRUE.equals(routingOperation.getIsProduction());
|
ProductionOperationTask existingTask = taskByRoutingOperationId.get(routingOperation.getId());
|
if (shouldHaveTask) {
|
if (existingTask == null) {
|
ProductionOperationTask task = new ProductionOperationTask();
|
task.setProductionOrderId(productionOrderId);
|
task.setProductionOrderRoutingOperationId(routingOperation.getId());
|
task.setPlanQuantity(BigDecimal.ZERO);
|
task.setCompleteQuantity(BigDecimal.ZERO);
|
task.setWorkOrderNo(generateNextTaskNo());
|
task.setStatus(2);
|
productionOperationTaskMapper.insert(task);
|
}
|
continue;
|
}
|
if (existingTask != null) {
|
validateTaskCanRemove(existingTask);
|
productionOperationTaskMapper.deleteById(existingTask.getId());
|
}
|
}
|
}
|
|
private void validateTaskCanRemove(ProductionOperationTask task) {
|
if (task == null || task.getId() == null) {
|
return;
|
}
|
if (defaultDecimal(task.getCompleteQuantity()).compareTo(BigDecimal.ZERO) > 0) {
|
throw new ServiceException("工序已产生报工记录,无法根据 BOM 变更删除对应工序快照" + task.getWorkOrderNo());
|
}
|
long reportCount = productionProductMainMapper.selectCount(
|
Wrappers.<ProductionProductMain>lambdaQuery()
|
.eq(ProductionProductMain::getProductionOperationTaskId, task.getId()));
|
if (reportCount > 0) {
|
throw new ServiceException("工序已产生报工记录,无法根据 BOM 变更删除对应工单");
|
}
|
}
|
|
private void syncRoutingOperationParams(Long routingOperationId, Long productionOrderId, Long technologyOperationId) {
|
if (routingOperationId == null || technologyOperationId == null) {
|
return;
|
}
|
List<TechnologyOperationParam> operationParamList = technologyOperationParamMapper.selectList(
|
Wrappers.<TechnologyOperationParam>lambdaQuery()
|
.eq(TechnologyOperationParam::getTechnologyOperationId, technologyOperationId)
|
.orderByAsc(TechnologyOperationParam::getId));
|
for (TechnologyOperationParam operationParam : operationParamList) {
|
TechnologyParam technologyParam = technologyParamMapper.selectById(operationParam.getTechnologyParamId());
|
if (technologyParam == null) {
|
continue;
|
}
|
ProductionOrderRoutingOperationParam snapshot = new ProductionOrderRoutingOperationParam();
|
snapshot.setProductionOrderId(productionOrderId);
|
snapshot.setProductionOrderRoutingOperationId(routingOperationId);
|
snapshot.setTechnologyOperationId(operationParam.getTechnologyOperationId());
|
snapshot.setTechnologyOperationParamId(operationParam.getId());
|
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());
|
productionOrderRoutingOperationParamMapper.insert(snapshot);
|
}
|
}
|
|
private String buildRoutingOperationBucketKey(Long operationId, Long outputProductModelId) {
|
return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
|
}
|
|
private TechnologyOperation getTechnologyOperation(Long technologyOperationId) {
|
if (technologyOperationId == null) {
|
return null;
|
}
|
return technologyOperationMapper.selectById(technologyOperationId);
|
}
|
|
private String generateNextTaskNo() {
|
String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
ProductionOperationTask latestTask = productionOperationTaskMapper.selectOne(
|
Wrappers.<ProductionOperationTask>lambdaQuery()
|
.likeRight(ProductionOperationTask::getWorkOrderNo, "GD" + datePrefix)
|
.orderByDesc(ProductionOperationTask::getWorkOrderNo)
|
.last("limit 1"));
|
int sequenceNumber = 1;
|
if (latestTask != null && latestTask.getWorkOrderNo() != null && latestTask.getWorkOrderNo().startsWith("GD" + datePrefix)) {
|
try {
|
sequenceNumber = Integer.parseInt(latestTask.getWorkOrderNo().substring(("GD" + datePrefix).length())) + 1;
|
} catch (NumberFormatException ignored) {
|
sequenceNumber = 1;
|
}
|
}
|
return "GD" + String.format("%s%03d", datePrefix, sequenceNumber);
|
}
|
|
private BigDecimal defaultDecimal(BigDecimal value) {
|
return value == null ? BigDecimal.ZERO : value;
|
}
|
|
private int compareDecimal(BigDecimal left, BigDecimal right) {
|
return defaultDecimal(left).compareTo(defaultDecimal(right));
|
}
|
|
}
|