Skip to content
切换导航条
切换导航条
当前项目
正在载入...
登录
孙克
/
smf-core
转到一个项目
切换导航栏
切换导航栏固定状态
项目
群组
代码片段
帮助
项目
活动
版本库
流水线
图表
问题
0
合并请求
0
维基
网络
创建新的问题
作业
提交
问题看板
文件
提交
网络
比较
分支
标签
Commit 9dd7f709
由
张少辉
编写于
2026-03-05 19:07:51 +0800
浏览文件
选项
浏览文件
标签
下载
电子邮件补丁
差异文件
1.库位异常亮灯指令
1 个父辈
63ec2006
隐藏空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
374 行增加
和
22 行删除
src/main/java/com/neotel/smfcore/custom/haobo/HaoBoNlpPosHandler.java
src/main/java/com/neotel/smfcore/custom/haobo/HaoboApi.java
src/main/java/com/neotel/smfcore/custom/haobo/HaoboNlpController.java
src/main/java/com/neotel/smfcore/custom/haobo/bean/PosDiff.java
src/main/java/com/neotel/smfcore/custom/haobo/bean/PosTotalDiffResult.java
src/main/java/com/neotel/smfcore/custom/haobo/bean/response/GetInventoryResponse.java
src/main/java/com/neotel/smfcore/custom/haobo/HaoBoNlpPosHandler.java
0 → 100644
查看文件 @
9dd7f70
package
com
.
neotel
.
smfcore
.
custom
.
haobo
;
import
com.neotel.smfcore.core.api.SmfApi
;
import
com.neotel.smfcore.core.device.util.DataCache
;
import
com.neotel.smfcore.core.storage.service.po.Storage
;
import
com.neotel.smfcore.core.system.util.DevicesStatusUtil
;
import
com.neotel.smfcore.custom.haobo.bean.PosDiff
;
import
com.neotel.smfcore.custom.haobo.bean.PosTotalDiffResult
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
javax.annotation.PostConstruct
;
import
java.util.*
;
import
java.util.concurrent.Executors
;
import
java.util.concurrent.ScheduledExecutorService
;
import
java.util.concurrent.TimeUnit
;
@Slf4j
@Service
public
class
HaoBoNlpPosHandler
{
@Autowired
private
SmfApi
smfApi
;
@Autowired
private
HaoboApi
haoboApi
;
@Autowired
private
DataCache
dataCache
;
ScheduledExecutorService
scheduledThreadPool
=
Executors
.
newScheduledThreadPool
(
1
);
@PostConstruct
private
void
init
()
{
String
apiName
=
smfApi
.
getApiName
();
if
(
haoboApi
.
isForThisApi
(
apiName
))
{
scheduledThreadPool
.
scheduleWithFixedDelay
(
new
Runnable
()
{
@Override
public
void
run
()
{
handlePos
();
}
},
30
,
5
,
TimeUnit
.
SECONDS
);
}
}
private
void
handlePos
()
{
for
(
Storage
storage
:
dataCache
.
getAllStorage
().
values
())
{
String
cid
=
storage
.
getCid
();
// 提取CID,便于日志定位
if
(
storage
.
isNLPHBShelf
())
{
// ========== 1. 获取旧数据(空值兜底,避免索引越界) ==========
List
<
List
<
String
>>
oldDeviceData
=
DevicesStatusUtil
.
getDeviceData
(
cid
);
// ========== 2. 计算新的差异数据 ==========
List
<
String
>
inventoryPosName
=
haoboApi
.
getInventoryPosName
(
cid
);
List
<
String
>
usedPosNameList
=
dataCache
.
getUsedPosNameList
(
cid
);
PosDiff
posDiff
=
calculatePosDiff
(
inventoryPosName
,
usedPosNameList
);
List
<
String
>
newInventoryOnly
=
posDiff
.
getInventoryOnly
()
==
null
?
new
ArrayList
<>()
:
posDiff
.
getInventoryOnly
();
List
<
String
>
newUsedOnly
=
posDiff
.
getUsedOnly
()
==
null
?
new
ArrayList
<>()
:
posDiff
.
getUsedOnly
();
List
<
List
<
String
>>
newData
=
new
ArrayList
<>();
newData
.
add
(
newInventoryOnly
);
newData
.
add
(
newUsedOnly
);
//3.开始新增差异信息,亮灯或者灭灯
PosTotalDiffResult
posTotalDiffResult
=
calculateAndPrintPosDiff
(
oldDeviceData
,
newData
);
List
<
String
>
totalAddList
=
posTotalDiffResult
.
getTotalAddList
();
List
<
String
>
totalReduceList
=
posTotalDiffResult
.
getTotalReduceList
();
haoboApi
.
lightUpSomeLampBeads
(
totalAddList
,
totalReduceList
);
DevicesStatusUtil
.
updateDeviceData
(
cid
,
newData
);
}
}
}
/**
* 计算并打印新旧货位数据的差异,同时返回合并后的总新增/总减少列表
* @param oldDeviceData 旧设备数据(二维列表:[库存独有列表, 已用独有列表])
* @param newData 新计算的差异数据(二维列表:[库存独有列表, 已用独有列表])
* @return PosTotalDiffResult 合并后的总新增、总减少货位列表
*/
private
PosTotalDiffResult
calculateAndPrintPosDiff
(
List
<
List
<
String
>>
oldDeviceData
,
List
<
List
<
String
>>
newData
)
{
// ========== 1. 解析旧数据(空值兜底) ==========
List
<
String
>
oldInventoryOnly
=
new
ArrayList
<>();
List
<
String
>
oldUsedOnly
=
new
ArrayList
<>();
if
(
oldDeviceData
!=
null
&&
!
oldDeviceData
.
isEmpty
())
{
oldInventoryOnly
=
oldDeviceData
.
size
()
>=
1
?
oldDeviceData
.
get
(
0
)
:
new
ArrayList
<>();
oldUsedOnly
=
oldDeviceData
.
size
()
>=
2
?
oldDeviceData
.
get
(
1
)
:
new
ArrayList
<>();
}
oldInventoryOnly
=
oldInventoryOnly
==
null
?
new
ArrayList
<>()
:
oldInventoryOnly
;
oldUsedOnly
=
oldUsedOnly
==
null
?
new
ArrayList
<>()
:
oldUsedOnly
;
// ========== 2. 解析新数据(空值兜底) ==========
List
<
String
>
newInventoryOnly
=
new
ArrayList
<>();
List
<
String
>
newUsedOnly
=
new
ArrayList
<>();
if
(
newData
!=
null
&&
!
newData
.
isEmpty
())
{
newInventoryOnly
=
newData
.
size
()
>=
1
?
newData
.
get
(
0
)
:
new
ArrayList
<>();
newUsedOnly
=
newData
.
size
()
>=
2
?
newData
.
get
(
1
)
:
new
ArrayList
<>();
}
newInventoryOnly
=
newInventoryOnly
==
null
?
new
ArrayList
<>()
:
newInventoryOnly
;
newUsedOnly
=
newUsedOnly
==
null
?
new
ArrayList
<>()
:
newUsedOnly
;
// ========== 3. 计算细分维度差异 ==========
Set
<
String
>
oldInventorySet
=
new
HashSet
<>(
oldInventoryOnly
);
Set
<
String
>
newInventorySet
=
new
HashSet
<>(
newInventoryOnly
);
Set
<
String
>
inventoryAdd
=
new
HashSet
<>(
newInventorySet
);
inventoryAdd
.
removeAll
(
oldInventorySet
);
Set
<
String
>
inventoryReduce
=
new
HashSet
<>(
oldInventorySet
);
inventoryReduce
.
removeAll
(
newInventorySet
);
Set
<
String
>
oldUsedSet
=
new
HashSet
<>(
oldUsedOnly
);
Set
<
String
>
newUsedSet
=
new
HashSet
<>(
newUsedOnly
);
Set
<
String
>
usedAdd
=
new
HashSet
<>(
newUsedSet
);
usedAdd
.
removeAll
(
oldUsedSet
);
Set
<
String
>
usedReduce
=
new
HashSet
<>(
oldUsedSet
);
usedReduce
.
removeAll
(
newUsedSet
);
// ========== 4. 合并总差异(去重)并转List ==========
Set
<
String
>
totalAddSet
=
new
HashSet
<>();
totalAddSet
.
addAll
(
inventoryAdd
);
totalAddSet
.
addAll
(
usedAdd
);
List
<
String
>
totalAddList
=
new
ArrayList
<>(
totalAddSet
);
// 转List更易用
Set
<
String
>
totalReduceSet
=
new
HashSet
<>();
totalReduceSet
.
addAll
(
inventoryReduce
);
totalReduceSet
.
addAll
(
usedReduce
);
List
<
String
>
totalReduceList
=
new
ArrayList
<>(
totalReduceSet
);
// 转List更易用
// ========== 6. 返回合并后的差异结果 ==========
return
new
PosTotalDiffResult
(
totalAddList
,
totalReduceList
);
}
/**
* 计算两个货位名称列表的双向差集(纯String类型,无DataItem依赖)
*
* @param inventoryPosNameList 库存货位名称列表
* @param usedPosNameList 已用货位名称列表
* @return PosDiff inventoryOnly=库存有但已用无的名称列表 | usedOnly=已用有但库存无的名称列表
*/
private
PosDiff
calculatePosDiff
(
List
<
String
>
inventoryPosNameList
,
List
<
String
>
usedPosNameList
)
{
// 1. 空值兜底:避免NPE
List
<
String
>
safeInventoryNames
=
inventoryPosNameList
==
null
?
new
ArrayList
<>()
:
inventoryPosNameList
;
List
<
String
>
safeUsedNames
=
usedPosNameList
==
null
?
new
ArrayList
<>()
:
usedPosNameList
;
// 2. 转Set去重+提高差集计算效率(纯String操作)
Set
<
String
>
inventoryPosSet
=
new
HashSet
<>(
safeInventoryNames
);
Set
<
String
>
usedPosSet
=
new
HashSet
<>(
safeUsedNames
);
// 3. 计算:库存有但已用无的货位名称列表
Set
<
String
>
inventoryOnlySet
=
new
HashSet
<>(
inventoryPosSet
);
inventoryOnlySet
.
removeAll
(
usedPosSet
);
List
<
String
>
inventoryOnly
=
new
ArrayList
<>(
inventoryOnlySet
);
// 4. 计算:已用有但库存无的货位名称列表
Set
<
String
>
usedOnlySet
=
new
HashSet
<>(
usedPosSet
);
usedOnlySet
.
removeAll
(
inventoryPosSet
);
List
<
String
>
usedOnly
=
new
ArrayList
<>(
usedOnlySet
);
// 5. 返回纯String类型的差集结果
return
new
PosDiff
(
inventoryOnly
,
usedOnly
);
}
}
src/main/java/com/neotel/smfcore/custom/haobo/HaoboApi.java
查看文件 @
9dd7f70
package
com
.
neotel
.
smfcore
.
custom
.
haobo
;
import
com.alibaba.fastjson.JSON
;
import
com.alibaba.fastjson.JSONObject
;
import
com.neotel.smfcore.common.exception.ApiException
;
import
com.neotel.smfcore.common.utils.DateUtil
;
import
com.neotel.smfcore.common.utils.HttpHelper
;
import
com.neotel.smfcore.core.api.listener.BaseSmfApiListener
;
import
com.neotel.smfcore.core.device.util.DataCache
;
import
com.neotel.smfcore.core.order.enums.ORDER_COLOR
;
import
com.neotel.smfcore.core.system.service.po.DataLog
;
import
com.neotel.smfcore.custom.haobo.bean.response.GetInventoryResponse
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.stereotype.Service
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.Map
;
import
javax.annotation.PostConstruct
;
import
java.util.*
;
@Slf4j
@Service
public
class
HaoboApi
extends
BaseSmfApiListener
{
@Value
(
"${api.getInventoryUrl:}"
)
private
String
getInventoryUrl
=
""
;
@Value
(
"${api.lightUpLabelUrl:}"
)
private
String
lightUpLabelUrl
=
""
;
@Value
(
"${api.lightUpSomeLampBeadsUrl:}"
)
private
String
lightUpSomeLampBeadsUrl
=
""
;
@Autowired
private
DataCache
dataCache
;
@PostConstruct
private
void
init
()
{
getInventoryUrl
=
dataCache
.
getConfigCache
(
"api.haobo.getInventory"
,
getInventoryUrl
);
lightUpLabelUrl
=
dataCache
.
getConfigCache
(
"api.haobo.lightUpLabel"
,
lightUpLabelUrl
);
lightUpSomeLampBeadsUrl
=
dataCache
.
getConfigCache
(
"api.haobo.lightUpSomeLampBeads"
,
lightUpSomeLampBeadsUrl
);
}
@Override
public
boolean
isForThisApi
(
String
apiName
)
{
return
"haobo"
.
equalsIgnoreCase
(
apiName
);
...
...
@@ -44,25 +68,99 @@ public class HaoboApi extends BaseSmfApiListener {
@Override
public
void
outTaskStatusChange
(
String
outNotifyUrl
,
DataLog
task
)
{
if
(
task
.
isCheckOutTask
()
&&
(
task
.
isExecuting
()
||
task
.
isCancel
()))
{
Map
<
String
,
Object
>
paramMap
=
new
HashMap
<>();
String
labelIdListJson
=
"["
+
"\'"
+
task
.
getBarcode
()
+
"\'"
+
"]"
;
paramMap
.
put
(
"labelIdListJson"
,
labelIdListJson
);
i
f
(
task
.
isExecuting
())
{
paramMap
.
put
(
"color"
,
getHBColor
(
task
.
getLightColor
()));
paramMap
.
put
(
"outStockType"
,
2
)
;
}
else
if
(
task
.
isCancel
())
{
paramMap
.
put
(
"color"
,
0
)
;
paramMap
.
put
(
"outStockType"
,
1
)
;
String
barcodeStr
=
task
.
getBarcode
()
;
int
lightColor
=
getHBColor
(
task
.
getLightColor
()
);
i
nt
outStockType
=
0
;
if
(
task
.
isExecuting
()){
outStockType
=
2
;
}
else
if
(
task
.
isCancel
()){
outStockType
=
1
;
lightColor
=
0
;
}
paramMap
.
put
(
"newShelfCode"
,
""
);
paramMap
.
put
(
"detailsJson"
,
""
);
log
.
info
(
task
.
getBarcode
()
+
":出库通知,请求浩博料架,地址为:"
+
outNotifyUrl
+
",参数为:"
+
JSON
.
toJSONString
(
paramMap
));
try
{
String
result
=
HttpHelper
.
postJson
(
outNotifyUrl
,
paramMap
);
log
.
info
(
task
.
getBarcode
()
+
":出库通知,浩博料架返回结果为:"
+
result
);
}
catch
(
ApiException
e
)
{
log
.
info
(
task
.
getBarcode
()
+
":出库通知,浩博料架返回异常,异常信息为:"
+
e
.
getMessage
());
lightUpLabel
(
barcodeStr
,
lightColor
,
outStockType
);
}
}
protected
List
<
String
>
getInventoryPosName
(
String
cid
)
{
List
<
String
>
hasReelPosNameList
=
new
ArrayList
<>();
Map
<
String
,
Object
>
paramMap
=
new
HashMap
<>();
paramMap
.
put
(
"shelfCode"
,
cid
);
try
{
String
resultStr
=
HttpHelper
.
getParam
(
getInventoryUrl
,
null
,
paramMap
);
GetInventoryResponse
getInventoryResponse
=
JSONObject
.
parseObject
(
resultStr
,
GetInventoryResponse
.
class
);
GetInventoryResponse
.
Result
result
=
getInventoryResponse
.
getResult
();
if
(
result
.
isSuccess
())
{
List
<
GetInventoryResponse
.
DataItem
>
data
=
result
.
getData
();
if
(
data
!=
null
&&
!
data
.
isEmpty
())
{
for
(
GetInventoryResponse
.
DataItem
item
:
data
)
{
hasReelPosNameList
.
add
(
item
.
getExternalLocation
());
}
}
}
}
catch
(
ApiException
e
)
{
log
.
error
(
"获取浩博料架库存信息失败,异常信息为:{}"
,
e
.
getMessage
());
}
return
hasReelPosNameList
;
}
protected
void
lightUpLabel
(
String
barcodeStr
,
int
lightColor
,
int
outStockType
)
{
Map
<
String
,
Object
>
paramMap
=
new
HashMap
<>();
String
labelIdListJson
=
"["
+
"\'"
+
barcodeStr
+
"\'"
+
"]"
;
paramMap
.
put
(
"labelIdListJson"
,
labelIdListJson
);
paramMap
.
put
(
"color"
,
lightColor
);
paramMap
.
put
(
"outStockType"
,
outStockType
);
paramMap
.
put
(
"newShelfCode"
,
""
);
paramMap
.
put
(
"detailsJson"
,
""
);
log
.
info
(
barcodeStr
+
":出库通知,请求浩博料架,地址为:"
+
lightUpLabelUrl
+
",参数为:"
+
JSON
.
toJSONString
(
paramMap
));
try
{
String
result
=
HttpHelper
.
postJson
(
lightUpLabelUrl
,
paramMap
);
log
.
info
(
barcodeStr
+
":出库通知,浩博料架返回结果为:"
+
result
);
}
catch
(
ApiException
e
)
{
log
.
info
(
barcodeStr
+
":出库通知,浩博料架返回异常,异常信息为:"
+
e
.
getMessage
());
}
}
protected
void
lightUpSomeLampBeads
(
List
<
String
>
onPosNameList
,
List
<
String
>
offPosNameList
)
{
// 1. 前置校验:无任何货位数据时直接返回
boolean
hasOnData
=
onPosNameList
!=
null
&&
!
onPosNameList
.
isEmpty
();
boolean
hasOffData
=
offPosNameList
!=
null
&&
!
offPosNameList
.
isEmpty
();
if
(!
hasOnData
&&
!
hasOffData
)
{
//log.info("亮灯/灭灯货位列表均为空,无需调用亮灯接口");
return
;
}
// 2. 拼接货位项字符串(核心:亮灯color=1,灭灯color=0)
List
<
String
>
posItemList
=
new
ArrayList
<>();
// 处理亮灯货位(color='1')
if
(
hasOnData
)
{
for
(
String
location
:
onPosNameList
)
{
if
(
location
!=
null
&&
!
location
.
trim
().
isEmpty
())
{
posItemList
.
add
(
"{'color':'1','location':'"
+
location
.
trim
()
+
"'}"
);
}
}
}
// 处理灭灯货位(color='0')
if
(
hasOffData
)
{
for
(
String
location
:
offPosNameList
)
{
if
(
location
!=
null
&&
!
location
.
trim
().
isEmpty
())
{
posItemList
.
add
(
"{'color':'0','location':'"
+
location
.
trim
()
+
"'}"
);
}
}
}
// 3. 封装成指定的JSON格式:{"jsonData":"[货位项1,货位项2]"}
String
jsonArray
=
"["
+
String
.
join
(
","
,
posItemList
)+
"]"
;
Map
<
String
,
Object
>
paramMap
=
new
HashMap
<>();
paramMap
.
put
(
"jsonData"
,
jsonArray
);
try
{
log
.
info
(
"亮灯或者灭灯请求参数为:"
+
JSON
.
toJSONString
(
paramMap
));
String
result
=
HttpHelper
.
postJson
(
lightUpSomeLampBeadsUrl
,
paramMap
);
log
.
info
(
"亮灯或者灭灯返回参数为:"
+
result
);
}
catch
(
ApiException
e
)
{
log
.
info
(
"亮灯或者灭灯异常:"
,
e
);
}
}
...
...
src/main/java/com/neotel/smfcore/custom/haobo/HaoboNlpController.java
查看文件 @
9dd7f70
...
...
@@ -109,7 +109,9 @@ public class HaoboNlpController {
//判断在不在库位中,如果不在库位中,则忽略
StoragePos
pos
=
storagePosManager
.
getByBarcode
(
request
.
getLabelId
());
if
(
pos
==
null
)
{
pos
=
storagePosManager
.
getByPosName
(
request
.
getLocation
());
if
(
StringUtils
.
isNotEmpty
(
request
.
getLocation
()))
{
pos
=
storagePosManager
.
getByPosName
(
request
.
getLocation
());
}
}
if
(
pos
==
null
||
pos
.
getBarcode
()
==
null
)
{
log
.
info
(
"根据库位名称和条码都未找到对应的库存信息,库位名称为:{},唯一码为:{}"
,
request
.
getLocation
(),
request
.
getLabelId
());
...
...
@@ -118,7 +120,7 @@ public class HaoboNlpController {
Storage
storage
=
dataCache
.
getStorageById
(
pos
.
getStorageId
());
DataLog
createDataLog
=
new
DataLog
(
storage
,
pos
.
getBarcode
(),
pos
);
createDataLog
.
setType
(
OP
.
CHECKOUT
);
createDataLog
.
setOperator
(
"
NLP
"
);
createDataLog
.
setOperator
(
"
system
"
);
createDataLog
=
taskService
.
updateQueueTask
(
createDataLog
);
updateCheckoutData
(
createDataLog
,
OP_STATUS
.
FINISHED
);
return
new
HaoBoApiResponse
(
true
,
"出库完成"
);
...
...
src/main/java/com/neotel/smfcore/custom/haobo/bean/PosDiff.java
0 → 100644
查看文件 @
9dd7f70
package
com
.
neotel
.
smfcore
.
custom
.
haobo
.
bean
;
import
lombok.Data
;
import
java.util.List
;
@Data
public
class
PosDiff
{
List
<
String
>
inventoryOnly
;
List
<
String
>
usedOnly
;
public
PosDiff
(
List
<
String
>
inventoryOnly
,
List
<
String
>
usedOnly
)
{
this
.
inventoryOnly
=
inventoryOnly
;
this
.
usedOnly
=
usedOnly
;
}
}
src/main/java/com/neotel/smfcore/custom/haobo/bean/PosTotalDiffResult.java
0 → 100644
查看文件 @
9dd7f70
package
com
.
neotel
.
smfcore
.
custom
.
haobo
.
bean
;
import
java.util.List
;
public
class
PosTotalDiffResult
{
// 所有新增(多的)货位列表
private
List
<
String
>
totalAddList
;
// 所有减少(少的)货位列表
private
List
<
String
>
totalReduceList
;
public
PosTotalDiffResult
(
List
<
String
>
totalAddList
,
List
<
String
>
totalReduceList
)
{
this
.
totalAddList
=
totalAddList
;
this
.
totalReduceList
=
totalReduceList
;
}
// Getter方法(用于外部获取返回值)
public
List
<
String
>
getTotalAddList
()
{
return
totalAddList
;
}
public
List
<
String
>
getTotalReduceList
()
{
return
totalReduceList
;
}
}
src/main/java/com/neotel/smfcore/custom/haobo/bean/response/GetInventoryResponse.java
0 → 100644
查看文件 @
9dd7f70
package
com
.
neotel
.
smfcore
.
custom
.
haobo
.
bean
.
response
;
import
lombok.Data
;
import
java.util.List
;
@Data
public
class
GetInventoryResponse
{
private
Result
result
;
private
String
targetUrl
;
private
boolean
success
;
private
Object
error
;
private
boolean
unAuthorizedRequest
;
@Data
public
static
class
Result
{
private
boolean
success
;
private
String
message
;
private
List
<
DataItem
>
data
;
}
@Data
public
static
class
DataItem
{
private
String
location
;
private
String
labelId
;
private
String
shelfCode
;
private
String
operationTime
;
private
int
status
;
private
String
externalLocation
;
private
String
presentInductionLocation
;
private
String
transferShelfCode
;
private
String
inStockDetailsJson
;
private
String
outStockDetailsJson
;
private
boolean
inStockCallbackSuccess
;
private
int
inStockCallbackNum
;
private
boolean
outStockCallbackSuccess
;
private
int
outStockCallbackNum
;
private
String
updateTime
;
private
long
hardwareTime
;
private
String
materialCode
;
private
String
materialName
;
private
String
materialSpec
;
private
Integer
materialQuantity
;
private
String
productionDate
;
private
String
timeout
;
private
int
id
;
}
}
\ No newline at end of file
编写
预览
支持
Markdown
格式
附加文件
你添加了
0
人
到此讨论。请谨慎行事。
Finish editing this message first!
Cancel
请
注册
或
登录
后发表评论