Commit f0858e97 张少辉

工单超过200盘 拆分工单

1 个父辈 cbc849b7
......@@ -2,7 +2,6 @@ package com.neotel.smfcore.core.kanban.rest;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.neotel.smfcore.common.bean.PageData;
import com.neotel.smfcore.common.bean.ResultBean;
import com.neotel.smfcore.common.exception.ValidateException;
......@@ -25,19 +24,15 @@ import com.neotel.smfcore.core.storage.service.po.Storage;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.manager.impl.DataLogManagerImpl;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.core.system.service.po.Humiture;
import com.neotel.smfcore.core.system.util.DevicesStatusUtil;
import com.neotel.smfcore.core.system.util.TaskService;
import com.neotel.smfcore.security.annotation.AnonymousPutMapping;
import com.neotel.smfcore.security.service.manager.IGroupManager;
import com.neotel.smfcore.security.service.manager.impl.GroupManagerImpl;
import com.neotel.smfcore.security.service.manager.impl.UserManagerImpl;
import com.neotel.smfcore.security.service.po.Group;
import com.neotel.smfcore.security.service.po.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javafx.concurrent.Task;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -45,7 +40,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
......
......@@ -40,14 +40,17 @@ public class DefaultOrderFileListener implements IOrderFileListener {
@Override
public boolean handleOrderFile(File orderFile) {
String fileName = orderFile.getName();
if(isFileType(fileName,"csv")){
// 新增:标记是否成功处理至少一个工单(替代原循环内的return true)
boolean isHandleSuccess = false;
if (isFileType(fileName, "csv")) {
LiteOrder fileNameOrder = liteOrderManager.findBySource(fileName);
if(fileNameOrder == null){
try{
Map<String, List<LiteOrderItem>> itemMap = readCsvFile(fileName,orderFile.getAbsolutePath());
if (fileNameOrder == null) {
try {
Map<String, List<LiteOrderItem>> itemMap = readCsvFile(fileName, orderFile.getAbsolutePath());
if (itemMap != null && itemMap.size() > 0) {
for (String so:itemMap.keySet()) {
for (String so : itemMap.keySet()) { // 遍历所有拆分后的SO
List<LiteOrderItem> liteOrderItems = itemMap.get(so);
if (liteOrderItems.size() <= 0) {
......@@ -68,7 +71,7 @@ public class DefaultOrderFileListener implements IOrderFileListener {
} else {
log.info("watchOrderDir:数据库中已存在工单号为[" + liteOrder.getOrderNo() + "],忽略文件:" + orderFile.getAbsolutePath());
//resultFile = new File(localDir+File.separator + "error",backupFileName);
return false;
continue; // 原逻辑是return false,改为continue:仅跳过当前SO,继续处理后续SO
}
}
log.info("watchOrderDir:新增加订单:" + liteOrder.getOrderNo() + ",共" + liteOrderItems.size() + "条工单详情");
......@@ -89,15 +92,19 @@ public class DefaultOrderFileListener implements IOrderFileListener {
liteOrder.setSource(LITEORDER_SOURCE.INNER.name());
liteOrder = liteOrderManager.createWithItems(liteOrder);
liteOrderCache.addOrderToMap(liteOrder);
return true;
// 标记处理成功(只要有一个SO处理成功,就记为true)
isHandleSuccess = true;
// 移除原循环内的return true → 避免提前终止循环,确保所有SO都处理
}
}
}catch (Exception e){
log.error("read order from file ["+orderFile.getAbsolutePath()+"] :",e);
} catch (Exception e) {
log.error("read order from file [" + orderFile.getAbsolutePath() + "] :", e);
}
}
}
return false;
// 最终返回是否处理成功(而非原逻辑的提前return)
return isHandleSuccess;
}
@Override
......@@ -147,14 +154,14 @@ public class DefaultOrderFileListener implements IOrderFileListener {
public Map<String , List<LiteOrderItem>> readCsvFile(String fileName, String fileURL) {
CsvReader csvRead = null;
try {
fileName=fileName.replace(".csv","");
Map<String ,List<LiteOrderItem>> itemMap=new HashMap<>();
fileName = fileName.replace(".csv", "");
Map<String, List<LiteOrderItem>> itemMap = new HashMap<>();
List<LiteOrderItem> items = Lists.newArrayList();
OrderSetting orderSetting = dataCache.getOrderSetting();
csvRead = CsvReader.newReader(fileURL,"PN", orderSetting.getPn());
csvRead = CsvReader.newReader(fileURL, "PN", orderSetting.getPn());
int partNumberIndex = csvRead.getIndex("PN", orderSetting.getPn());
int soIndex = csvRead.getIndex("SO", orderSetting.getSo());
int reelCountIndex = csvRead.getIndex("REELCOUNT", orderSetting.getSo());
int reelCountIndex = csvRead.getIndex("REELCOUNT", orderSetting.getReelCount()); // 修正原代码参数错误!
int lineIndex = csvRead.getIndex("LINE", orderSetting.getLine());
int warehouseCodeIndex = csvRead.getIndex("WAREHOUSECODE", orderSetting.getWarehouseCode());
......@@ -166,61 +173,190 @@ public class DefaultOrderFileListener implements IOrderFileListener {
row++;
String[] lineValues = csvRead.getValues();
String partNumber = lineValues[partNumberIndex];
if (partNumber.isEmpty()/*&&ri.isEmpty()*/) {
log.warn("行[partNumber=" + partNumber + "]中PN和RI都 为空,此行忽略");
} else {
String so=fileName;
if(soIndex!=-1){
so=lineValues[soIndex];
}
if(!ObjectUtil.isNotEmpty(so)){
so=fileName;
if (partNumber.isEmpty()) {
log.warn("行[{}]中PN为空,此行忽略", row);
continue; // 替换原注释的ri判断,避免逻辑混乱
}
// 处理SO字段(保留原逻辑)
String so = fileName;
if (soIndex != -1) {
so = lineValues[soIndex];
}
if (!ObjectUtil.isNotEmpty(so)) {
so = fileName;
}
// 构建LiteOrderItem(保留原逻辑)
LiteOrderItem item = new LiteOrderItem();
item.setManualUpload(true);
item.setPn(partNumber);
// 处理REELCOUNT(盘数)
int count = 1;
if (reelCountIndex != -1) {
String countStr = lineValues[reelCountIndex];
if (Strings.isNotBlank(countStr)) {
try {
count = Integer.valueOf(countStr);
count = Math.max(count, 1); // 兜底:避免0/负数
} catch (Exception e) {
log.error("行[{}] PN[{}] 的数量[{}]不是数字,使用默认值1", row, partNumber, countStr);
count = 1;
}
}
LiteOrderItem item = new LiteOrderItem();
item.setManualUpload(true);
item.setPn(partNumber);
if(partNumber.isEmpty()){
item.setNeedReelCount(1);
}
item.setNeedReelCount(count);
// 填充其他字段
if (lineIndex != -1) {
item.setLine(lineValues[lineIndex]);
}
if (warehouseCodeIndex != -1) {
item.setWarehouseCode(lineValues[warehouseCodeIndex]);
}
// 存入原始itemMap
itemMap.computeIfAbsent(so, k -> new ArrayList<>()).add(item);
items.add(item);
}
// ===================== 重构后的拆分逻辑(核心) =====================
int MAX_TOTAL_REEL = 200; // 单工单最大累加盘数
Map<String, List<LiteOrderItem>> splitItemMap = new HashMap<>();
// 遍历每个原始SO
for (Map.Entry<String, List<LiteOrderItem>> entry : itemMap.entrySet()) {
String originalSO = entry.getKey();
List<LiteOrderItem> allItems = new ArrayList<>(entry.getValue());
int subOrderIndex = 1;
// 第一步:分离小PN(≤200)和大PN(>200),避免小PN被大PN截断
List<LiteOrderItem> smallPNItems = new ArrayList<>(); // 小PN列表(≤200)
List<LiteOrderItem> bigPNItems = new ArrayList<>(); // 大PN列表(>200)
for (LiteOrderItem item : allItems) {
int count = item.getNeedReelCount();
if (count > MAX_TOTAL_REEL) {
bigPNItems.add(item);
log.info("分离大PN[{}]:盘数[{}](超200)", item.getPn(), count);
} else {
smallPNItems.add(item);
log.info("分离小PN[{}]:盘数[{}]", item.getPn(), count);
}
int count = 1;
if (reelCountIndex != -1) {
String countStr = lineValues[reelCountIndex];
if (Strings.isNotBlank(countStr)) {
try {
count = Integer.valueOf(countStr);
} catch (Exception e) {
log.error(partNumber + "的数量:" + countStr + " 不是数字,使用1");
}
}
// 第二步:处理小PN(核心:while循环确保剩余盘数不丢失)
List<LiteOrderItem> currentSubOrder = new ArrayList<>();
int currentTotal = 0;
int i = 0;
// 用while循环遍历,保证剩余盘数的PN能继续处理
while (i < smallPNItems.size()) {
LiteOrderItem currentItem = smallPNItems.get(i);
int itemCount = currentItem.getNeedReelCount();
// 场景1:当前PN可全部加入,不超200
if (currentTotal + itemCount <= MAX_TOTAL_REEL) {
LiteOrderItem copyItem = copyLiteOrderItem(currentItem);
currentSubOrder.add(copyItem);
currentTotal += itemCount;
log.info("小PN[{}]加入当前工单,累计盘数[{}]", currentItem.getPn(), currentTotal);
smallPNItems.remove(i); // 移除已完全处理的小PN
} else {
// 场景2:当前PN只能部分加入(凑满200)
int needCount = MAX_TOTAL_REEL - currentTotal;
if (needCount > 0) {
// 拆分当前PN,取部分凑满200
LiteOrderItem partialItem = copyLiteOrderItem(currentItem);
partialItem.setNeedReelCount(needCount);
currentSubOrder.add(partialItem);
currentTotal += needCount;
log.info("小PN[{}]拆分[{}]盘凑满当前工单,剩余[{}]盘",
currentItem.getPn(), needCount, itemCount - needCount);
// 更新当前PN的剩余数量(不删除,继续处理)
currentItem.setNeedReelCount(itemCount - needCount);
}
}
item.setNeedReelCount(count);
if (lineIndex != -1){
String lineValue = lineValues[lineIndex];
item.setLine(lineValue);
// 封存凑满200的子工单
if (currentTotal == MAX_TOTAL_REEL) {
String subSO = originalSO + "_" + subOrderIndex++;
splitItemMap.put(subSO, new ArrayList<>(currentSubOrder)); // 深拷贝避免后续修改
log.info("生成小PN工单[{}]:累计[{}]盘,包含[{}]个PN",
subSO, currentTotal, currentSubOrder.size());
// 重置当前工单
currentSubOrder.clear();
currentTotal = 0;
}
// 不递增i,继续处理当前剩余的PN
}
if (warehouseCodeIndex != -1){
String warehouseCodeValue = lineValues[warehouseCodeIndex];
item.setWarehouseCode(warehouseCodeValue);
// 场景3:已凑满200,主动封存(防止遗漏)
if (currentTotal == MAX_TOTAL_REEL) {
String subSO = originalSO + "_" + subOrderIndex++;
splitItemMap.put(subSO, new ArrayList<>(currentSubOrder));
log.info("生成小PN工单[{}]:累计[{}]盘,包含[{}]个PN",
subSO, currentTotal, currentSubOrder.size());
currentSubOrder.clear();
currentTotal = 0;
}
if(!itemMap.containsKey(so)){
itemMap.put(so,new ArrayList<LiteOrderItem>());
}
// 处理小PN剩余未凑满的部分(所有小PN处理完后,剩余的合并为一个工单)
if (!currentSubOrder.isEmpty()) {
String subSO = originalSO + "_" + subOrderIndex++;
splitItemMap.put(subSO, currentSubOrder);
log.info("生成小PN剩余工单[{}]:累计[{}]盘,包含[{}]个PN",
subSO, currentTotal, currentSubOrder.size());
}
// 第三步:处理大PN(>200),拆分后逐个生成工单,数量零丢失
for (LiteOrderItem bigItem : bigPNItems) {
int totalCount = bigItem.getNeedReelCount();
log.info("开始拆分大PN[{}]:总盘数[{}]", bigItem.getPn(), totalCount);
int remainingCount = totalCount;
// 循环拆分,直到剩余数量为0
while (remainingCount > 0) {
int assignCount = Math.min(remainingCount, MAX_TOTAL_REEL);
LiteOrderItem splitItem = copyLiteOrderItem(bigItem);
splitItem.setNeedReelCount(assignCount);
String subSO = originalSO + "_" + subOrderIndex++;
splitItemMap.put(subSO, Collections.singletonList(splitItem));
log.info("生成大PN工单[{}]:PN[{}] 盘数[{}],剩余[{}]盘",
subSO, bigItem.getPn(), assignCount, remainingCount - assignCount);
remainingCount -= assignCount;
}
itemMap.get(so).add(item);
items.add(item);
log.info("大PN[{}]拆分完成,无数量丢失", bigItem.getPn());
}
}
return itemMap;
log.info("拆分完成:共生成[{}]个子工单,所有数量零丢失", splitItemMap.size());
return splitItemMap;
// ===================== 拆分逻辑结束 =====================
} catch (Exception ex) {
log.error("解析上传的工单出错:" + ex.toString());
}finally {
if(csvRead != null){
csvRead.close();
log.error("解析上传的工单出错:", ex); // 打印完整堆栈,便于排查
return null;
} finally {
if (csvRead != null) {
try {
csvRead.close();
} catch (Exception e) {
log.error("关闭CsvReader失败:", e);
}
}
}
return null;
}
// 工具方法:复制LiteOrderItem所有属性(保证信息一致)
private LiteOrderItem copyLiteOrderItem(LiteOrderItem source) {
LiteOrderItem target = new LiteOrderItem();
target.setManualUpload(source.isManualUpload());
target.setPn(source.getPn());
target.setNeedReelCount(source.getNeedReelCount());
target.setLine(source.getLine());
target.setWarehouseCode(source.getWarehouseCode());
// 若LiteOrderItem有其他属性,务必补充复制
return target;
}
protected boolean isFileType(String smbFile, String type){
//判断是否是有效的工单文件
......
......@@ -39,7 +39,7 @@ public class StationCacheUtil {
@PostConstruct
public void init() {
log.info("开始工位缓存信息");
for (int i = 1; i <= 5; i++) {
/*for (int i = 1; i <= 5; i++) {
String stationName = "s" + i;
Station station = dataCache.getCache(stationName);
if(station == null){
......@@ -48,7 +48,7 @@ public class StationCacheUtil {
}
stationMap.put(stationName,station);
dataCache.updateCache(stationName, station);
}
}*/
}
public static synchronized void updateStation(Station station){
......
......@@ -31,7 +31,7 @@ public class StationStatusCache {
private static Map<String, String> stationStatusMap = Maps.newConcurrentMap();
@PostConstruct
//@PostConstruct
public void init() {
try {
List<StationStatus> statusList = maiZhengApi.StationStatus();
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!