Skip to content
切换导航条
切换导航条
当前项目
正在载入...
登录
孙克
/
smf-core
转到一个项目
切换导航栏
切换导航栏固定状态
项目
群组
代码片段
帮助
项目
活动
版本库
流水线
图表
问题
0
合并请求
0
维基
网络
创建新的问题
作业
提交
问题看板
文件
提交
网络
比较
分支
标签
Commit c3c25073
由
张少辉
编写于
2026-03-18 15:06:02 +0800
浏览文件
选项
浏览文件
标签
下载
电子邮件补丁
差异文件
1.SO1374-PokaNon入库通知对接
1 个父辈
1a544c4e
显示空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
444 行增加
和
0 行删除
src/main/java/com/neotel/smfcore/common/bean/ResultBean.java
src/main/java/com/neotel/smfcore/core/barcode/service/manager/IBarcodeManager.java
src/main/java/com/neotel/smfcore/core/barcode/service/manager/impl/BarcodeManagerImpl.java
src/main/java/com/neotel/smfcore/custom/so1374/PokaNonInboundAndReturnController.java
src/main/java/com/neotel/smfcore/custom/so1374/bean/PokaNonInboundReturnRequest.java
src/main/java/com/neotel/smfcore/common/bean/ResultBean.java
查看文件 @
c3c2507
...
...
@@ -19,6 +19,10 @@ import java.util.Locale;
@Data
public
class
ResultBean
<
T
>
{
public
static
ResultBean
newErrorResult
(
int
code
,
String
msg
)
{
return
newErrorResult
(
code
,
""
,
msg
);
}
public
static
ResultBean
newErrorResult
(
int
code
,
String
msgKey
,
String
msg
)
{
return
newErrorResult
(
code
,
msgKey
,
msg
,
new
String
[]{},
true
);
}
...
...
src/main/java/com/neotel/smfcore/core/barcode/service/manager/IBarcodeManager.java
查看文件 @
c3c2507
...
...
@@ -17,4 +17,6 @@ public interface IBarcodeManager extends IBaseManager<Barcode> {
void
download
(
List
<
Barcode
>
list
,
HttpServletResponse
response
)
throws
IOException
;
void
deleteBarcodes
(
Set
<
String
>
ids
);
Barcode
findOneByLockName
(
String
lockName
);
}
src/main/java/com/neotel/smfcore/core/barcode/service/manager/impl/BarcodeManagerImpl.java
查看文件 @
c3c2507
...
...
@@ -96,6 +96,11 @@ public class BarcodeManagerImpl implements IBarcodeManager {
}
@Override
public
Barcode
findOneByLockName
(
String
lockName
)
{
return
barcodeDao
.
findOne
(
new
Query
(
Criteria
.
where
(
"lockName"
).
is
(
lockName
)));
}
@Override
public
Barcode
saveBarcode
(
Barcode
resources
)
{
validateSave
(
resources
);
...
...
src/main/java/com/neotel/smfcore/custom/so1374/PokaNonInboundAndReturnController.java
0 → 100644
查看文件 @
c3c2507
package
com
.
neotel
.
smfcore
.
custom
.
so1374
;
import
com.alibaba.fastjson.JSON
;
import
com.neotel.smfcore.common.bean.ResultBean
;
import
com.neotel.smfcore.common.utils.DateUtil
;
import
com.neotel.smfcore.common.utils.StringUtils
;
import
com.neotel.smfcore.core.barcode.service.manager.IBarcodeManager
;
import
com.neotel.smfcore.core.barcode.service.manager.IComponentManager
;
import
com.neotel.smfcore.core.barcode.service.po.Barcode
;
import
com.neotel.smfcore.core.barcode.service.po.Component
;
import
com.neotel.smfcore.core.storage.service.manager.IStoragePosManager
;
import
com.neotel.smfcore.core.storage.service.po.StoragePos
;
import
com.neotel.smfcore.core.system.util.TaskService
;
import
com.neotel.smfcore.custom.so1374.bean.PokaNonInboundReturnRequest
;
import
com.neotel.smfcore.security.annotation.AnonymousAccess
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiOperation
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
java.util.Date
;
import
java.util.HashMap
;
import
java.util.Map
;
@Api
(
tags
=
"入库对接"
)
@Slf4j
@RestController
public
class
PokaNonInboundAndReturnController
{
@Autowired
private
IStoragePosManager
storagePosManager
;
@Autowired
private
IComponentManager
componentManager
;
@Autowired
private
IBarcodeManager
barcodeManager
;
@Autowired
private
TaskService
taskService
;
@ApiOperation
(
"入库/退库通知"
)
@RequestMapping
(
"/api/v2/inbound/register"
)
@AnonymousAccess
public
synchronized
ResultBean
register
(
@RequestBody
PokaNonInboundReturnRequest
request
)
{
// 新增:请求接收日志(拼接关键参数,替换原全量JSON)
log
.
info
(
"收到入库/退库通知:"
+
JSON
.
toJSONString
(
request
));
//判断是否为空
//1001 VALIDATION_ERROR 缺少 / 无效的必填字段(如 serial、qty、locationId 等),直接拒绝请求
if
(
requestIsEmpty
(
request
))
{
// 新增:参数空日志
log
.
error
(
"请求参数为空,拒绝请求,clientTxnId:"
+
request
.
getClientTxnId
());
return
ResultBean
.
newErrorResult
(
1001
,
"INVALID_REQUEST"
);
}
//判断库存是否存在
//1004 INVALID_LOCATION 库位地址(locationId)未找到 / 格式无效,拒绝入库
StoragePos
pos
=
checkLocationExist
(
request
);
if
(
pos
==
null
)
{
// 新增:库位不存在日志
log
.
error
(
"库位不存在,locationId:"
+
request
.
getLocation
().
getLocationId
()
+
",clientTxnId:"
+
request
.
getClientTxnId
());
return
ResultBean
.
newErrorResult
(
1004
,
"INVALID_LOCATION"
);
}
//判断库位上是否有物料
//1005 LOCATION_FULL 目标库位无法存储物料(库位已满 / 被锁定),拒绝入库
if
(
pos
.
getBarcode
()
!=
null
)
{
// 新增:库位占用日志
log
.
error
(
"库位已占用,locationId:"
+
pos
.
getPosName
()
+
",clientTxnId:"
+
request
.
getClientTxnId
());
return
ResultBean
.
newErrorResult
(
1005
,
"LOCATION_FULL"
);
}
//根据类型判断是入库还是退库
Barcode
barcode
=
null
;
String
operation
=
request
.
getOperation
();
PokaNonInboundReturnRequest
.
Material
material
=
request
.
getMaterial
();
PokaNonInboundReturnRequest
.
Label
label
=
request
.
getLabel
();
if
(
"INBOUND"
.
equals
(
operation
))
{
//新入库(operation=INBOUND):serial 在 SMF 在库中需保持唯一,重复则拒绝入库,成功后 SMF 生成并返回 uid
barcode
=
barcodeManager
.
findOneByLockName
(
material
.
getSerial
());
if
(
barcode
!=
null
)
{
// 新增:serial重复日志
log
.
error
(
"新入库序列号重复,serial:"
+
material
.
getSerial
()
+
",clientTxnId:"
+
request
.
getClientTxnId
());
//1002 DUPLICATE_SERIAL 新入库:序列号已存在于 SMF 在库中,拒绝入库;再入库:仅当未生成新 uid 时触发该错误
return
ResultBean
.
newErrorResult
(
1002
,
"DUPLICATE_SERIAL"
);
}
barcode
=
createBarcode
(
material
,
label
);
// 新增:新入库条码创建完成日志
log
.
info
(
"新入库条码创建完成,serial:"
+
material
.
getSerial
()
+
",uid:"
+
barcode
.
getBarcode
());
}
else
if
(
"RETURN"
.
equals
(
operation
))
{
barcode
=
createBarcode
(
material
,
label
);
// 新增:退库条码创建完成日志
log
.
info
(
"退库条码创建完成,serial:"
+
material
.
getSerial
()
+
",uid:"
+
barcode
.
getBarcode
());
}
else
{
// 新增:操作类型非法日志
log
.
error
(
"操作类型非法,operation:"
+
operation
+
",clientTxnId:"
+
request
.
getClientTxnId
());
//1001 VALIDATION_ERROR 缺少 / 无效的必填字段(如 serial、qty、locationId 等),直接拒绝请求
return
ResultBean
.
newErrorResult
(
1006
,
"VALIDATION_ERROR"
);
}
//创建元器件信息
createComponent
(
material
);
// 新增:元器件创建完成日志
log
.
info
(
"元器件创建完成,partNo:"
+
material
.
getPartNo
()
+
",makerPartNo:"
+
material
.
getMakerPartNo
());
//生成一个入库任务
//操作人
PokaNonInboundReturnRequest
.
Operator
operator
=
request
.
getOperator
();
String
operatorId
=
operator
.
getOperatorId
();
taskService
.
addTaskToFinished
(
pos
,
barcode
,
operatorId
);
// 新增:入库任务创建完成日志
log
.
info
(
"入库任务创建完成,operatorId:"
+
operatorId
+
",locationId:"
+
pos
.
getPosName
());
//增加返回值
Map
<
String
,
Object
>
resultMap
=
new
HashMap
<>();
resultMap
.
put
(
"smfTxnId"
,
request
.
getClientTxnId
());
resultMap
.
put
(
"serial"
,
material
.
getSerial
());
resultMap
.
put
(
"uid"
,
barcode
.
getBarcode
());
resultMap
.
put
(
"status"
,
"COMPLETED"
);
resultMap
.
put
(
"serverTime"
,
DateUtil
.
toDateString
(
new
Date
(),
"yyyy-MM-dd HH:mm:ss"
));
//构建registered信息
Map
<
String
,
Object
>
registeredMap
=
new
HashMap
<>();
registeredMap
.
put
(
"partNo"
,
material
.
getPartNo
());
registeredMap
.
put
(
"qty"
,
material
.
getQty
());
registeredMap
.
put
(
"locationId"
,
pos
.
getPosName
());
resultMap
.
put
(
"registered"
,
registeredMap
);
// 新增:请求处理完成日志
log
.
info
(
"入库/退库请求处理完成,clientTxnId:"
+
request
.
getClientTxnId
()
+
",uid:"
+
barcode
.
getBarcode
());
return
ResultBean
.
newOkResult
(
resultMap
);
}
private
Component
createComponent
(
PokaNonInboundReturnRequest
.
Material
material
)
{
//判断元器件是否存在,如果不存在,则自动创建一个
String
partNo
=
material
.
getPartNo
();
String
makerPartNo
=
material
.
getMakerPartNo
();
Integer
qty
=
material
.
getQty
();
Component
component
=
componentManager
.
findByPartNumberAndProvider
(
partNo
,
makerPartNo
);
if
(
component
==
null
)
{
// 新增:元器件新建日志
log
.
info
(
"元器件不存在,新建元器件,partNo:"
+
partNo
+
",makerPartNo:"
+
makerPartNo
);
component
=
new
Component
();
component
.
setPartNumber
(
partNo
);
component
.
setProvider
(
makerPartNo
);
component
.
setPlateSize
(
2
);
component
.
setHeight
(
2
);
component
.
setAmount
(
qty
);
componentManager
.
saveComponent
(
component
);
}
else
{
// 新增:元器件已存在日志
log
.
info
(
"元器件已存在,partNo:"
+
partNo
+
",makerPartNo:"
+
makerPartNo
);
}
return
component
;
}
//判断是否为空
private
boolean
requestIsEmpty
(
PokaNonInboundReturnRequest
request
)
{
String
clientTxnId
=
request
.
getClientTxnId
();
if
(
StringUtils
.
isEmpty
(
clientTxnId
))
{
log
.
warn
(
"参数为空:clientTxnId"
);
return
true
;
}
String
operation
=
request
.
getOperation
();
if
(
StringUtils
.
isEmpty
(
operation
))
{
log
.
warn
(
"参数为空:operation"
);
return
true
;
}
PokaNonInboundReturnRequest
.
Operator
operator
=
request
.
getOperator
();
if
(
operator
==
null
||
StringUtils
.
isEmpty
(
operator
.
getOperatorId
()))
{
log
.
warn
(
"参数为空:operatorId"
);
return
true
;
}
PokaNonInboundReturnRequest
.
Material
material
=
request
.
getMaterial
();
if
(
material
==
null
)
{
log
.
warn
(
"参数为空:material"
);
return
true
;
}
if
(
StringUtils
.
isEmpty
(
material
.
getSerial
()))
{
log
.
warn
(
"参数为空:material.serial"
);
return
true
;
}
if
(
StringUtils
.
isEmpty
(
material
.
getPartNo
()))
{
log
.
warn
(
"参数为空:material.partNo"
);
return
true
;
}
Integer
qty
=
material
.
getQty
();
if
(
qty
==
null
||
qty
==
0
)
{
log
.
warn
(
"参数为空/无效:material.qty"
);
return
true
;
}
PokaNonInboundReturnRequest
.
Location
location
=
request
.
getLocation
();
if
(
location
==
null
||
StringUtils
.
isEmpty
(
location
.
getLocationId
()))
{
log
.
warn
(
"参数为空:locationId"
);
return
true
;
}
return
false
;
}
//判断序列号是否存在
public
StoragePos
checkLocationExist
(
PokaNonInboundReturnRequest
request
)
{
//判断库位是否存在
//1004 INVALID_LOCATION 库位地址(locationId)未找到 / 格式无效,拒绝入库
PokaNonInboundReturnRequest
.
Location
location
=
request
.
getLocation
();
String
locationId
=
location
.
getLocationId
();
StoragePos
pos
=
storagePosManager
.
getByPosName
(
locationId
);
// 新增:库位校验日志
log
.
info
(
"库位校验,locationId:"
+
locationId
+
",是否存在:"
+
(
pos
!=
null
));
return
pos
;
}
private
Barcode
createBarcode
(
PokaNonInboundReturnRequest
.
Material
material
,
PokaNonInboundReturnRequest
.
Label
label
)
{
String
uid
=
material
.
getSerial
()
+
"_"
+
DateUtil
.
toDateString
(
new
Date
(),
"yyyyMMddHHmmss"
);
Barcode
barcode
=
new
Barcode
();
barcode
.
setBarcode
(
uid
);
barcode
.
setPartNumber
(
material
.
getPartNo
());
barcode
.
setProvider
(
material
.
getMakerPartNo
());
barcode
.
setBatch
(
material
.
getMakerLot
());
barcode
.
setMemo
(
material
.
getLotNo
());
barcode
.
setAmount
(
material
.
getQty
());
//设置生产日期和过期日期
String
receiveDate
=
material
.
getReceiveDate
();
if
(
StringUtils
.
isNotEmpty
(
receiveDate
))
{
barcode
.
setProduceDate
(
DateUtil
.
toDate
(
receiveDate
,
"yyyy-MM-dd"
));
}
String
expireDate
=
material
.
getExpireDate
();
if
(
StringUtils
.
isNotEmpty
(
expireDate
))
{
barcode
.
setExpireDate
(
DateUtil
.
toDate
(
expireDate
,
"yyyy-MM-dd"
));
}
//设置完整的条码信息
if
(
label
!=
null
)
{
String
fullCode
=
label
.
getFullCode
();
barcode
.
setFullCode
(
fullCode
);
Integer
qrQty
=
label
.
getQrQty
();
if
(
qrQty
!=
null
)
{
barcode
.
setLabelAmount
(
qrQty
);
}
}
barcode
.
setLockName
(
material
.
getSerial
());
barcode
=
barcodeManager
.
save
(
barcode
);
return
barcode
;
}
}
\ No newline at end of file
src/main/java/com/neotel/smfcore/custom/so1374/bean/PokaNonInboundReturnRequest.java
0 → 100644
查看文件 @
c3c2507
package
com
.
neotel
.
smfcore
.
custom
.
so1374
.
bean
;
import
lombok.Data
;
/**
* 入库/退货请求实体类 (PokaNon INBOUND/RETURN Request)
* 对应I/F 定義表的字段规范
*/
@Data
// Lombok注解,自动生成getter/setter/toString/equals/hashCode
public
class
PokaNonInboundReturnRequest
{
/**
* PokaNon交易ID(幂等键)
* 类型:string | 必填:Y
* 示例:POKANON-INB-20260124-000123
*/
private
String
clientTxnId
;
/**
* 客户编码(Panasonic可能固定)
* 类型:string | 必填:Y
* 示例:PANASONIC
*/
private
String
customerCode
;
/**
* 操作类型:INBOUND(新品入库) / RETURN(退货重新入库)
* 类型:string | 必填:Y
* 示例:INBOUND
*/
private
String
operation
;
/**
* 操作人员信息
* 类型:object | 必填:Y(子字段operatorId必填)
*/
private
Operator
operator
;
/**
* 物料信息
* 类型:object | 必填:Y(核心子字段必填)
*/
private
Material
material
;
/**
* 库位信息
* 类型:object | 必填:Y(子字段locationId必填)
*/
private
Location
location
;
/**
* 标签信息
* 类型:object | 必填:N
*/
private
Label
label
;
/**
* 客户端时间戳(审计用)
* 类型:string | 必填:N
* 格式:YYYY-MM-DD HH:mm:ss
* 示例:2026-01-24 10:12:30
*/
private
String
timestamp
;
/**
* 操作人员子实体
*/
@Data
public
static
class
Operator
{
/**
* 操作人员ID
* 类型:string | 必填:Y
* 示例:U001
*/
private
String
operatorId
;
}
/**
* 物料子实体
*/
@Data
public
static
class
Material
{
/**
* PokaNon序列号(标签序列号)
* 类型:string | 必填:Y
* 示例:ID-2026012400001-001
*/
private
String
serial
;
/**
* 主物料编号(建议必填)
* 类型:string | 必填:Y*
* 示例:GLOBAL-PN-001
*/
private
String
partNo
;
/**
* 制造商物料编号
* 类型:string | 必填:N
* 示例:ABC-123
*/
private
String
makerPartNo
;
/**
* 内部批次号(如有)
* 类型:string | 必填:N
* 示例:LOT202601
*/
private
String
lotNo
;
/**
* 制造商批次号
* 类型:string | 必填:N
* 示例:MLOT-5566
*/
private
String
makerLot
;
/**
* 实际入库数量(已确认)
* 类型:number | 必填:Y
* 示例:500
*/
private
Integer
qty
;
/**
* 计量单位
* 类型:string | 必填:N
* 示例:PCS
*/
private
String
uom
;
/**
* 接收日期
* 类型:date | 必填:N
* 格式:YYYY-MM-DD
* 示例:2026-01-24
*/
private
String
receiveDate
;
// 用String避免日期序列化问题,也可改用LocalDate
/**
* 有效期
* 类型:date | 必填:N
* 格式:YYYY-MM-DD
* 示例:2027-01-31
*/
private
String
expireDate
;
// 同上
}
/**
* 库位子实体
*/
@Data
public
static
class
Location
{
/**
* SMF库位地址(扫描的QR文本)
* 类型:string | 必填:Y
* 示例:R01-S03
*/
private
String
locationId
;
/**
* 库位类型(固定字面量,避免混淆)
* 类型:string | 必填:N
* 示例:SMF_ADDRESS
*/
private
String
locationType
;
}
/**
* 标签子实体
*/
@Data
public
static
class
Label
{
/**
* 原始标签QR文本(可选存储/校验)
* 类型:string | 必填:N
* 示例:(raw QR text)
*/
private
String
fullCode
;
/**
* QR标签上打印的数量(参考用)
* 类型:number | 必填:N
* 示例:500
*/
private
Integer
qrQty
;
}
}
\ No newline at end of file
编写
预览
支持
Markdown
格式
附加文件
你添加了
0
人
到此讨论。请谨慎行事。
Finish editing this message first!
Cancel
请
注册
或
登录
后发表评论