Skip to content
切换导航条
切换导航条
当前项目
正在载入...
登录
孙克
/
smf-core
转到一个项目
切换导航栏
切换导航栏固定状态
项目
群组
代码片段
帮助
项目
活动
版本库
流水线
图表
问题
0
合并请求
0
维基
网络
创建新的问题
作业
提交
问题看板
文件
提交
网络
比较
分支
标签
Commit f0858e97
由
张少辉
编写于
2026-01-21 10:35:49 +0800
浏览文件
选项
浏览文件
标签
下载
电子邮件补丁
差异文件
工单超过200盘 拆分工单
1 个父辈
cbc849b7
隐藏空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
194 行增加
和
64 行删除
src/main/java/com/neotel/smfcore/core/kanban/rest/BoxKanbanController.java
src/main/java/com/neotel/smfcore/core/order/listener/DefaultOrderFileListener.java
src/main/java/com/neotel/smfcore/custom/lizhen/agvBox/util/StationCacheUtil.java
src/main/java/com/neotel/smfcore/custom/lizhen/third/maicheng/util/StationStatusCache.java
src/main/java/com/neotel/smfcore/core/kanban/rest/BoxKanbanController.java
查看文件 @
f0858e9
...
...
@@ -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
;
...
...
src/main/java/com/neotel/smfcore/core/order/listener/DefaultOrderFileListener.java
查看文件 @
f0858e9
...
...
@@ -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
.
get
So
());
int
reelCountIndex
=
csvRead
.
getIndex
(
"REELCOUNT"
,
orderSetting
.
get
ReelCount
());
// 修正原代码参数错误!
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
){
//判断是否是有效的工单文件
...
...
src/main/java/com/neotel/smfcore/custom/lizhen/agvBox/util/StationCacheUtil.java
查看文件 @
f0858e9
...
...
@@ -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
){
...
...
src/main/java/com/neotel/smfcore/custom/lizhen/third/maicheng/util/StationStatusCache.java
查看文件 @
f0858e9
...
...
@@ -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!
Cancel
请
注册
或
登录
后发表评论