package cn.iocoder.yudao.module.mes.service.pro.route;
|
|
import cn.hutool.core.util.StrUtil;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteCompleteImportWorkbook;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteSaveReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessSaveReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.product.MesProRouteProductSaveReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.productbom.MesProRouteProductBomSaveReqVO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.process.MesProProcessMapper;
|
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
|
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
|
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
|
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.support.TransactionTemplate;
|
|
import java.util.ArrayList;
|
import java.util.Comparator;
|
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;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_ROUTE_IMPORT_LIST_IS_EMPTY;
|
|
/** 按路线分事务批量导入完整配置;失败路线不会留下部分产品或工序。 */
|
@Service
|
public class MesProRouteCompleteImportService {
|
@Resource private TransactionTemplate transactionTemplate;
|
@Resource private MesProRouteService routeService;
|
@Resource private MesProRouteProductService productService;
|
@Resource private MesProRouteProcessService processService;
|
@Resource private MesProRouteProductBomService bomService;
|
@Resource private MesMdItemService itemService;
|
@Resource private MesProProcessMapper processMapper;
|
@Resource private MesWmWarehouseService warehouseService;
|
@Resource private MesWmWarehouseLocationService locationService;
|
@Resource private MesWmWarehouseAreaService areaService;
|
|
public MesProRouteImportRespVO importRoutes(MesProRouteCompleteImportWorkbook book) {
|
if (book.getRoutes() == null || book.getRoutes().isEmpty()) {
|
throw exception(PRO_ROUTE_IMPORT_LIST_IS_EMPTY);
|
}
|
MesProRouteImportRespVO result = MesProRouteImportRespVO.builder()
|
.createCodes(new ArrayList<>()).updateCodes(new ArrayList<>())
|
.failureCodes(new LinkedHashMap<>()).build();
|
Set<String> seen = new HashSet<>();
|
int rowNumber = 1;
|
for (MesProRouteCompleteImportWorkbook.RouteRow row : book.getRoutes()) {
|
rowNumber++;
|
String code = trim(row.getRouteCode());
|
String key = StrUtil.isBlank(code) ? "路线表第 " + rowNumber + " 行" : code;
|
if (StrUtil.isBlank(code) || StrUtil.isBlank(row.getName())) {
|
result.getFailureCodes().put(key, "路线编码和名称不能为空");
|
continue;
|
}
|
if (!seen.add(code)) {
|
result.getFailureCodes().put("路线表第 " + rowNumber + " 行(" + code + ")", "导入文件中路线编码重复");
|
continue;
|
}
|
try {
|
transactionTemplate.execute(status -> {
|
importOne(book, row, code);
|
return null;
|
});
|
result.getCreateCodes().add(code);
|
} catch (RuntimeException ex) {
|
result.getFailureCodes().put(key, ex.getMessage());
|
}
|
}
|
Set<String> known = book.getRoutes().stream().map(r -> trim(r.getRouteCode()))
|
.collect(Collectors.toSet());
|
collectUnknownRoutes(result.getFailureCodes(), known, "产品", book.getProducts() == null ? List.of() :
|
book.getProducts().stream().map(MesProRouteCompleteImportWorkbook.ProductRow::getRouteCode).toList());
|
collectUnknownRoutes(result.getFailureCodes(), known, "工序", book.getProcesses() == null ? List.of() :
|
book.getProcesses().stream().map(MesProRouteCompleteImportWorkbook.ProcessRow::getRouteCode).toList());
|
collectUnknownRoutes(result.getFailureCodes(), known, "工序产出", book.getOutputs() == null ? List.of() :
|
book.getOutputs().stream().map(MesProRouteCompleteImportWorkbook.OutputRow::getRouteCode).toList());
|
collectUnknownRoutes(result.getFailureCodes(), known, "工序投料", book.getInputs() == null ? List.of() :
|
book.getInputs().stream().map(MesProRouteCompleteImportWorkbook.InputRow::getRouteCode).toList());
|
return result;
|
}
|
|
private void importOne(MesProRouteCompleteImportWorkbook book,
|
MesProRouteCompleteImportWorkbook.RouteRow row, String code) {
|
List<MesProRouteCompleteImportWorkbook.ProductRow> products = rows(book.getProducts()).stream()
|
.filter(r -> code.equals(trim(r.getRouteCode()))).toList();
|
List<MesProRouteCompleteImportWorkbook.ProcessRow> processes = rows(book.getProcesses()).stream()
|
.filter(r -> code.equals(trim(r.getRouteCode())))
|
.sorted(Comparator.comparing(MesProRouteCompleteImportWorkbook.ProcessRow::getSort,
|
Comparator.nullsLast(Comparator.naturalOrder()))).toList();
|
if (products.isEmpty() || processes.isEmpty()) {
|
throw new IllegalArgumentException("至少需要一条产品和一道工序");
|
}
|
MesProRouteSaveReqVO route = new MesProRouteSaveReqVO();
|
route.setCode(code);
|
route.setName(trim(row.getName()));
|
route.setDescription(row.getDescription());
|
route.setRemark(row.getRemark());
|
Long routeId = routeService.createRoute(route);
|
Set<String> productCodes = new HashSet<>();
|
for (MesProRouteCompleteImportWorkbook.ProductRow product : products) {
|
String itemCode = trim(product.getItemCode());
|
if (!productCodes.add(itemCode)) {
|
throw new IllegalArgumentException("产品编码重复:" + itemCode);
|
}
|
MesProRouteProductSaveReqVO request = new MesProRouteProductSaveReqVO();
|
request.setRouteId(routeId);
|
request.setItemId(itemId(itemCode));
|
MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(trim(product.getWarehouseCode()));
|
MesWmWarehouseLocationDO location = locationService.getWarehouseLocationByCode(trim(product.getLocationCode()));
|
MesWmWarehouseAreaDO area = areaService.getWarehouseAreaByCode(trim(product.getAreaCode()));
|
if (warehouse == null || location == null || area == null) {
|
throw new IllegalArgumentException("产品 " + itemCode + " 的仓库、库区或库位编码不存在");
|
}
|
request.setWarehouseId(warehouse.getId());
|
request.setLocationId(location.getId());
|
request.setAreaId(area.getId());
|
request.setQuantity(product.getQuantity());
|
request.setProductionTime(product.getProductionTime());
|
request.setTimeUnitType(product.getTimeUnitType());
|
request.setRemark(product.getRemark());
|
productService.createRouteProduct(request);
|
}
|
Set<String> processCodes = new HashSet<>();
|
Set<Integer> sorts = new HashSet<>();
|
for (MesProRouteCompleteImportWorkbook.ProcessRow process : processes) {
|
String processCode = trim(process.getProcessCode());
|
if (!processCodes.add(processCode) || process.getSort() == null || !sorts.add(process.getSort())) {
|
throw new IllegalArgumentException("工序编码或排序重复/为空:" + processCode);
|
}
|
MesProProcessDO processDO = processMapper.selectByCode(processCode);
|
if (processDO == null) {
|
throw new IllegalArgumentException("工序编码不存在:" + processCode);
|
}
|
MesProRouteProcessSaveReqVO request = new MesProRouteProcessSaveReqVO();
|
request.setRouteId(routeId);
|
request.setProcessId(processDO.getId());
|
request.setSort(process.getSort());
|
request.setParamTemplateId(process.getParamTemplateId());
|
request.setYieldRate(process.getYieldRate());
|
request.setLinkType(process.getLinkType());
|
request.setPrepareTime(process.getPrepareTime());
|
request.setWaitTime(process.getWaitTime());
|
request.setColorCode(process.getColorCode());
|
request.setKeyFlag(flag(process.getKeyFlag(), processCode, "关键工序"));
|
request.setCheckFlag(flag(process.getCheckFlag(), processCode, "质检工序"));
|
request.setBackflushFlag(flag(process.getBackflushFlag(), processCode, "倒冲"));
|
request.setRemark(process.getRemark());
|
List<MesProRouteProcessSaveReqVO.OutputItem> outputs = new ArrayList<>();
|
for (MesProRouteCompleteImportWorkbook.OutputRow output : rows(book.getOutputs())) {
|
if (!code.equals(trim(output.getRouteCode())) || !processCode.equals(trim(output.getProcessCode()))) {
|
continue;
|
}
|
MesProRouteProcessSaveReqVO.OutputItem value = new MesProRouteProcessSaveReqVO.OutputItem();
|
value.setItemId(itemId(output.getItemCode()));
|
if (StrUtil.isNotBlank(output.getProductCode())) {
|
String ownerCode = trim(output.getProductCode());
|
if (!productCodes.contains(ownerCode)) {
|
throw new IllegalArgumentException("产出归属产品未关联路线:" + ownerCode);
|
}
|
value.setProductId(itemId(ownerCode));
|
}
|
value.setSort(output.getSort());
|
value.setRemark(output.getRemark());
|
outputs.add(value);
|
}
|
request.setOutputs(outputs);
|
processService.createRouteProcess(request);
|
}
|
for (MesProRouteCompleteImportWorkbook.OutputRow output : rows(book.getOutputs())) {
|
if (code.equals(trim(output.getRouteCode())) && !processCodes.contains(trim(output.getProcessCode()))) {
|
throw new IllegalArgumentException("产出引用了未配置的工序:" + output.getProcessCode());
|
}
|
}
|
for (MesProRouteCompleteImportWorkbook.InputRow input : rows(book.getInputs())) {
|
if (!code.equals(trim(input.getRouteCode()))) {
|
continue;
|
}
|
String processCode = trim(input.getProcessCode());
|
String productCode = trim(input.getProductCode());
|
if (!processCodes.contains(processCode) || !productCodes.contains(productCode)) {
|
throw new IllegalArgumentException("投料引用了未配置的工序或产品:" + processCode + "/" + productCode);
|
}
|
MesProRouteProductBomSaveReqVO request = new MesProRouteProductBomSaveReqVO();
|
request.setRouteId(routeId);
|
request.setProcessId(processMapper.selectByCode(processCode).getId());
|
request.setProductId(itemId(productCode));
|
request.setItemId(itemId(input.getItemCode()));
|
request.setQuantity(input.getQuantity());
|
request.setRemark(input.getRemark());
|
bomService.createRouteProductBom(request);
|
}
|
}
|
|
private Long itemId(String code) {
|
String normalized = trim(code);
|
MesMdItemDO item = StrUtil.isBlank(normalized) ? null : itemService.getItemByCode(normalized);
|
if (item == null) {
|
throw new IllegalArgumentException("物料编码不存在:" + normalized);
|
}
|
return item.getId();
|
}
|
|
private static boolean flag(String value, String processCode, String field) {
|
String normalized = trim(value);
|
if (StrUtil.isBlank(normalized) || "否".equals(normalized)
|
|| "false".equalsIgnoreCase(normalized) || "0".equals(normalized)) {
|
return false;
|
}
|
if ("是".equals(normalized) || "true".equalsIgnoreCase(normalized) || "1".equals(normalized)) {
|
return true;
|
}
|
throw new IllegalArgumentException("工序 " + processCode + " 的" + field + "只能填写是或否");
|
}
|
|
private static String trim(String value) {
|
return StrUtil.trim(value);
|
}
|
|
private static <T> List<T> rows(List<T> values) {
|
return values == null ? List.of() : values;
|
}
|
|
private static void collectUnknownRoutes(Map<String, String> failures, Set<String> known,
|
String sheet, List<String> codes) {
|
for (String code : codes) {
|
if (!known.contains(trim(code))) {
|
failures.put(sheet + "表路线 " + code, "路线表中不存在此路线编码");
|
}
|
}
|
}
|
}
|