Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
M
mini-wms
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Members
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
周海峰
mini-wms
Commits
63dd6d31
Commit
63dd6d31
authored
Dec 15, 2025
by
yubin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
导入修改
parent
ff9d771e
显示空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
264 行增加
和
189 行删除
+264
-189
ruoyi-admin-vue/src/views/inventory/orders/index.vue
+1
-0
ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
+2
-0
ruoyi-inventory/src/main/java/com/ruoyi/inventory/domain/OutboundOrderItems.java
+3
-0
ruoyi-inventory/src/main/java/com/ruoyi/inventory/mapper/InventoryMapper.java
+2
-0
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/OutboundOrdersServiceImpl.java
+244
-189
ruoyi-inventory/src/main/resources/mapper/inventory/OutboundOrdersMapper.xml
+12
-0
没有找到文件。
ruoyi-admin-vue/src/views/inventory/orders/index.vue
View file @
63dd6d31
...
...
@@ -683,6 +683,7 @@ export default {
this
.
getList
()
}
)
this
.
handleShip
=
this
.
debounce
(
this
.
handleShip
,
500
)
}
,
methods
:
{
debounce
(
fn
,
delay
=
300
)
{
...
...
ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
View file @
63dd6d31
...
...
@@ -719,4 +719,5 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
}
return
sb
.
toString
();
}
}
\ No newline at end of file
ruoyi-inventory/src/main/java/com/ruoyi/inventory/domain/OutboundOrderItems.java
View file @
63dd6d31
...
...
@@ -117,5 +117,7 @@ public class OutboundOrderItems extends BaseEntity
@Excel
(
name
=
"排序号"
)
private
String
updateUserCode
;
private
String
InventoryType
;
}
\ No newline at end of file
ruoyi-inventory/src/main/java/com/ruoyi/inventory/mapper/InventoryMapper.java
View file @
63dd6d31
...
...
@@ -116,4 +116,6 @@ public interface InventoryMapper
public
List
<
java
.
util
.
Map
<
String
,
String
>>
selectInventoryTopTenByAmount
();
public
List
<
java
.
util
.
Map
<
String
,
String
>>
selectInventoryTopTenByQuantity
();
public
void
batchUpdateInventory
(
List
<
Inventory
>
inventoryList
);
}
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/OutboundOrdersServiceImpl.java
View file @
63dd6d31
...
...
@@ -4,6 +4,7 @@ import java.util.*;
import
java.util.stream.Collectors
;
import
com.ruoyi.common.annotation.SerialExecution
;
import
com.ruoyi.common.config.WarehouseConfig
;
import
com.ruoyi.common.core.domain.entity.Materials
;
import
com.ruoyi.common.exception.ServiceException
;
import
com.ruoyi.common.utils.DateUtils
;
...
...
@@ -28,6 +29,10 @@ import org.springframework.util.CollectionUtils;
/**
* 出库单主Service业务层处理
* 核心修正:
* 1. 库存匹配Key统一为 仓库ID_物料ID_库位ID_库存类型
* 2. 确保inventoryType完整参与匹配
* 3. 库存扣减后≤0时强制设置inventory_status=0
*
* @author ruoyi
* @date 2025-12-03
...
...
@@ -54,6 +59,10 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
private
MaterialsServiceImpl
materialsService
;
@Autowired
private
StorageLocationsServiceImpl
storageLocationsService
;
@Autowired
private
InventoryMapper
inventoryMapper
;
/**
* 查询出库单主
*
...
...
@@ -63,7 +72,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Override
public
OutboundOrders
selectOutboundOrdersById
(
String
id
)
{
return
outboundOrdersMapper
.
selectOutboundOrdersById
(
id
);
}
...
...
@@ -112,7 +120,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
outboundOrdersMapper
.
deleteOutboundOrderItemsByOrderId
(
outboundOrders
.
getId
());
outboundOrderLogMapper
.
deleteOutboundOrderLogByOrdersId
(
outboundOrders
.
getId
());
outboundOrders
.
setUpdateUserCode
(
SystemUtils
.
getUserName
());
outboundOrders
.
setUpdateTime
(
DateUtils
.
getNowDate
());
insertOutboundOrderItems
(
outboundOrders
);
...
...
@@ -147,42 +154,161 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
outboundOrdersMapper
.
deleteOutboundOrderItemsByOrderId
(
id
);
return
outboundOrdersMapper
.
deleteOutboundOrdersById
(
id
);
}
@SerialExecution
(
group
=
"inventoryRefresh"
,
fair
=
true
)
@Override
public
int
ship
(
OutboundOrders
outboundOrders
)
{
OutboundOrderItems
outboundOrderItems1
=
new
OutboundOrderItems
();
outboundOrderItems1
.
setOutboundOrderId
(
outboundOrders
.
getId
());
outboundOrderItems1
.
setDivisor
(
null
);
List
<
OutboundOrderItems
>
outboundOrderItems
=
outboundOrderItemsMapper
.
selectOutboundOrderItemsList
(
outboundOrderItems1
);
List
<
OutboundOrderItems
>
outboundOrderItems2
=
outboundOrderItems
;
// 1. 查询当前出库单的所有明细
OutboundOrderItems
query
=
new
OutboundOrderItems
();
query
.
setOutboundOrderId
(
outboundOrders
.
getId
());
query
.
setDivisor
(
null
);
List
<
OutboundOrderItems
>
outboundOrderItems
=
outboundOrderItemsMapper
.
selectOutboundOrderItemsList
(
query
);
// 2. 更新明细和订单状态
OutboundOrderLog
outboundOrderLog
=
new
OutboundOrderLog
();
String
updateUser
=
SystemUtils
.
getUserName
();
Date
updateTime
=
DateUtils
.
getNowDate
();
for
(
OutboundOrderItems
item
:
outboundOrderItems
)
{
item
.
setItemStatus
(
3L
);
item
.
setUpdateBy
(
updateUser
);
item
.
setUpdateTime
(
updateTime
);
outboundOrderItemsMapper
.
updateOutboundOrderItems
(
item
);
for
(
OutboundOrderItems
outboundOrderItem
:
outboundOrderItems
)
{
outboundOrderLog
.
setId
(
item
.
getId
());
outboundOrderLog
.
setItemStatus
(
item
.
getItemStatus
());
outboundOrderLogMapper
.
updateOutboundOrderLog
(
outboundOrderLog
);
}
outboundOrderItem
.
setItemStatus
(
3
l
);
outboundOrderItemsMapper
.
updateOutboundOrderItems
(
outboundOrderItem
);
outboundOrders
.
setOrderStatus
(
2L
);
outboundOrders
.
setUpdateTime
(
updateTime
);
outboundOrders
.
setUpdateUserCode
(
updateUser
);
outboundOrdersMapper
.
updateOutboundOrders
(
outboundOrders
);
outboundOrderLog
.
setId
(
outboundOrderItem
.
getId
());
outboundOrderLog
.
setItemStatus
(
outboundOrderItem
.
getItemStatus
());
outboundOrderLogMapper
.
updateOutboundOrderLog
(
outboundOrderLog
);
// 3. 执行库存扣减(包含inventoryType匹配 + 0值状态更新)
this
.
deductInventory
(
outboundOrderItems
,
updateUser
,
updateTime
);
return
1
;
}
/**
* 核心库存扣减逻辑
* 1. 按 仓库ID_物料ID_库位ID_库存类型 匹配库存
* 2. 扣减后数量≤0时设置inventory_status=0
* @param outboundOrderItems 出库明细
* @param updateUser 操作人
* @param updateTime 操作时间
*/
private
void
deductInventory
(
List
<
OutboundOrderItems
>
outboundOrderItems
,
String
updateUser
,
Date
updateTime
)
{
if
(
CollectionUtils
.
isEmpty
(
outboundOrderItems
))
{
return
;
}
// 1. 预加载库存映射:Key=仓库ID_物料ID_库位ID_库存类型 Value=库存对象
Map
<
String
,
Inventory
>
inventoryMap
=
this
.
loadInventoryMap
();
// 2. 构建扣减数量Map:Key=仓库ID_物料ID_库位ID_库存类型 Value=总扣减数量
Map
<
String
,
Long
>
deductQtyMap
=
this
.
buildDeductQtyMap
(
outboundOrderItems
);
outboundOrders
.
setId
(
outboundOrders
.
getId
());
outboundOrders
.
setOrderStatus
(
2
l
);
outboundOrders
.
setUpdateTime
(
DateUtils
.
getNowDate
());
outboundOrders
.
setUpdateUserCode
(
SystemUtils
.
getUserName
()
);
outboundOrdersMapper
.
updateOutboundOrders
(
outboundOrders
);
// 3. 遍历扣减Map,执行扣减+状态更新
List
<
Inventory
>
needUpdateList
=
new
ArrayList
<>(
);
for
(
Map
.
Entry
<
String
,
Long
>
entry
:
deductQtyMap
.
entrySet
())
{
String
key
=
entry
.
getKey
(
);
Long
deductQty
=
entry
.
getValue
(
);
inventoryService
.
ship
(
outboundOrderItems2
);
return
1
;
// 匹配库存
Inventory
inventory
=
inventoryMap
.
get
(
key
);
if
(
inventory
==
null
)
{
String
[]
keyParts
=
key
.
split
(
"_"
);
String
warehouseId
=
keyParts
.
length
>
0
?
keyParts
[
0
]
:
""
;
String
materialId
=
keyParts
.
length
>
1
?
keyParts
[
1
]
:
""
;
String
locationId
=
keyParts
.
length
>
2
?
keyParts
[
2
]
:
""
;
String
inventoryType
=
keyParts
.
length
>
3
?
keyParts
[
3
]
:
""
;
throw
new
ServiceException
(
String
.
format
(
"仓库[%s]物料[%s]库位[%s]库存类型[%s]的库存不存在,无法扣减"
,
warehouseId
,
materialId
,
locationId
,
inventoryType
));
}
// 执行数量扣减
Long
currentQty
=
Optional
.
ofNullable
(
inventory
.
getQuantity
()).
orElse
(
0L
);
Long
newQty
=
currentQty
-
deductQty
;
inventory
.
setQuantity
(
newQty
);
// 核心规则:扣减后数量≤0 → 状态置0
if
(
newQty
<=
0
)
{
inventory
.
setInventoryStatus
(
0L
);
}
// 补充审计字段
inventory
.
setUpdateBy
(
updateUser
);
inventory
.
setUpdateTime
(
updateTime
);
needUpdateList
.
add
(
inventory
);
}
// 4. 批量更新库存
if
(!
needUpdateList
.
isEmpty
())
{
inventoryMapper
.
batchUpdateInventory
(
needUpdateList
);
// 刷新库存缓存
List
<
String
>
inventoryIds
=
needUpdateList
.
stream
()
.
map
(
Inventory:
:
getId
)
.
distinct
()
.
collect
(
Collectors
.
toList
());
inventoryService
.
RefreshInventory
(
inventoryIds
);
}
}
/**
* 预加载库存Map
* Key规则:仓库ID_物料ID_库位ID_库存类型
*/
private
Map
<
String
,
Inventory
>
loadInventoryMap
()
{
Inventory
query
=
new
Inventory
();
query
.
setInventoryStatus
(
1L
);
query
.
setIsUsed
(
1L
);
List
<
Inventory
>
inventoryList
=
inventoryService
.
selectInventoryList
(
query
);
if
(
CollectionUtils
.
isEmpty
(
inventoryList
))
{
return
Collections
.
emptyMap
();
}
Map
<
String
,
Inventory
>
inventoryMap
=
new
HashMap
<>();
for
(
Inventory
inv
:
inventoryList
)
{
String
key
=
String
.
join
(
"_"
,
Optional
.
ofNullable
(
inv
.
getWarehousesId
()).
orElse
(
""
),
Optional
.
ofNullable
(
inv
.
getMaterialId
()).
orElse
(
""
),
Optional
.
ofNullable
(
inv
.
getLocationId
()).
orElse
(
""
),
Optional
.
ofNullable
(
inv
.
getInventoryType
()).
map
(
String:
:
valueOf
).
orElse
(
""
)
);
// 重复key保留第一个(避免覆盖)
if
(!
inventoryMap
.
containsKey
(
key
))
{
inventoryMap
.
put
(
key
,
inv
);
}
}
return
inventoryMap
;
}
/**
* 构建扣减数量Map
* Key规则:仓库ID_物料ID_库位ID_库存类型(和库存Map完全对齐)
*/
private
Map
<
String
,
Long
>
buildDeductQtyMap
(
List
<
OutboundOrderItems
>
items
)
{
Map
<
String
,
Long
>
deductQtyMap
=
new
HashMap
<>();
for
(
OutboundOrderItems
item
:
items
)
{
String
key
=
String
.
join
(
"_"
,
Optional
.
ofNullable
(
item
.
getWarehouseId
()).
orElse
(
""
),
Optional
.
ofNullable
(
item
.
getMaterialId
()).
orElse
(
""
),
Optional
.
ofNullable
(
item
.
getLocationId
()).
orElse
(
""
),
Optional
.
ofNullable
(
item
.
getInventoryType
()).
map
(
String:
:
valueOf
).
orElse
(
""
)
);
// 累加扣减数量
Long
qty
=
Optional
.
ofNullable
(
item
.
getActualQuantity
()).
orElse
(
0L
);
deductQtyMap
.
put
(
key
,
deductQtyMap
.
getOrDefault
(
key
,
0L
)
+
qty
);
}
return
deductQtyMap
;
}
@Override
public
List
<
Map
<
String
,
String
>>
outboundOrdersTopTenByQuantity
()
{
List
<
Map
<
String
,
String
>>
o
=
outboundOrdersMapper
.
SelectOutboundOrdersMaterialsTopTenByQuantity
();
return
o
;
return
outboundOrdersMapper
.
SelectOutboundOrdersMaterialsTopTenByQuantity
();
}
@Override
...
...
@@ -192,7 +318,6 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Override
public
String
outboundOrdersCount
()
{
return
outboundOrdersMapper
.
outboundOrdersCount
();
}
...
...
@@ -204,48 +329,41 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
public
void
insertOutboundOrderItems
(
OutboundOrders
outboundOrders
)
{
List
<
OutboundOrderItems
>
outboundOrderItemsList
=
outboundOrders
.
getOutboundOrderItemsList
();
String
id
=
outboundOrders
.
getId
();
// 1. 先做空列表校验(提前返回,避免无效逻辑)
if
(
outboundOrderItemsList
==
null
||
outboundOrderItemsList
.
isEmpty
())
{
if
(
CollectionUtils
.
isEmpty
(
outboundOrderItemsList
))
{
return
;
}
//
2. 库存校验:失败时抛异常(核心修正:! 取反 + 异常抛出后代码立即终止)
//
库存校验:失败时抛异常
boolean
isValid
=
inventoryService
.
inventoryLockValidation
(
outboundOrderItemsList
);
if
(!
isValid
)
{
// 校验失败(返回false)时抛异常
throw
new
RuntimeException
(
"库存被修改请重新确认"
);
// 抛异常后,方法立即停止运行
if
(!
isValid
)
{
throw
new
RuntimeException
(
"库存被修改请重新确认"
);
}
//
2.
为明细设置订单ID和主键ID
// 为明细设置订单ID和主键ID
for
(
OutboundOrderItems
items
:
outboundOrderItemsList
)
{
items
.
setOutboundOrderId
(
id
);
items
.
setOrderId
(
outboundOrders
.
getOrderId
());
// 生成无横线的UUID作为主键
items
.
setId
(
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
));
}
//
3.
批量插入出库单明细
// 批量插入出库单明细
outboundOrdersMapper
.
batchOutboundOrderItems
(
outboundOrderItemsList
);
//
4. 正确拷贝明细列表到日志列表(修复核心错误:遍历逐个拷贝)
//
拷贝明细到日志列表
List
<
String
>
inventoryIds
=
new
ArrayList
<>();
List
<
OutboundOrderLog
>
outboundOrderLogs
=
new
ArrayList
<>();
for
(
OutboundOrderItems
items
:
outboundOrderItemsList
)
{
OutboundOrderLog
log
=
new
OutboundOrderLog
();
BeanUtils
.
copyProperties
(
items
,
log
);
// 单个对象属性拷贝
BeanUtils
.
copyProperties
(
items
,
log
);
log
.
setOrderId
(
items
.
getOutboundOrderId
());
outboundOrderLogs
.
add
(
log
);
inventoryIds
.
add
(
log
.
getInventoryId
());
deleteOutboundOrdersById
(
items
.
getId
());
}
//
5. 非空校验后插入日志(避免空列表触发SQL语法错误)
//
插入日志 + 刷新库存
if
(!
outboundOrderLogs
.
isEmpty
())
{
outboundOrderLogMapper
.
batchOutboundOrderLog
(
outboundOrderLogs
);
}
// 7. 非空校验后刷新库存
if
(!
inventoryIds
.
isEmpty
())
{
inventoryService
.
RefreshInventory
(
inventoryIds
);
}
...
...
@@ -254,12 +372,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Transactional
(
rollbackFor
=
Exception
.
class
)
@Override
public
String
importOutboundOrders
(
List
<
OutboundTemplateVO
>
inboundOrdersList
,
Boolean
isUpdateSupport
,
String
operName
,
Integer
orderType
)
{
// 1. 基础空值校验
(保留原有逻辑)
// 1. 基础空值校验
if
(
CollectionUtils
.
isEmpty
(
inboundOrdersList
))
{
throw
new
ServiceException
(
"导入数据不能为空!"
);
}
// 2. 初始化变量
(保留原有逻辑)
// 2. 初始化变量
int
totalMainSuccess
=
0
;
int
totalMainFailure
=
0
;
int
totalItemSuccess
=
0
;
...
...
@@ -274,18 +392,19 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
Map
<
String
,
List
<
OutboundOrderItems
>>
validItemMap
=
new
HashMap
<>();
boolean
hasValidateError
=
false
;
// ========== 新增:预加载映射缓存(核心优化) ==========
Map
<
String
,
String
>
sapToMaterialIdMap
=
loadSapToMaterialIdMap
();
// 物料SAP->ID
Map
<
String
,
String
>
locationNameToIdMap
=
loadLocationNameToIdMap
();
// 库位名称->ID
Map
<
String
,
String
>
warehouseNameToIdMap
=
loadWarehouseNameToIdMap
();
// 仓库名称->ID
Map
<
String
,
String
>
ownerNameToIdMap
=
loadOwnerNameToIdMap
();
// 货主名称->ID
// 3. 预加载映射缓存
Map
<
String
,
String
>
sapToMaterialIdMap
=
loadSapToMaterialIdMap
();
Map
<
String
,
String
>
locationNameToIdMap
=
loadLocationNameToIdMap
();
Map
<
String
,
String
>
warehouseNameToIdMap
=
loadWarehouseNameToIdMap
();
Map
<
String
,
String
>
ownerNameToIdMap
=
loadOwnerNameToIdMap
();
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
inventoryTOIdMap
=
loadInventoryTOIdMap
();
//
3. 按入库单号分组(保留原有逻辑)
//
4. 按入库单号分组
Map
<
String
,
List
<
OutboundTemplateVO
>>
orderGroupMap
=
inboundOrdersList
.
stream
()
.
filter
(
vo
->
StringUtils
.
isNotBlank
(
vo
.
getOrderId
()))
.
collect
(
Collectors
.
groupingBy
(
OutboundTemplateVO:
:
getOrderId
));
//
4. 第一步:仅验证所有数据(替换为预加载映射校验)
//
5. 数据验证
for
(
Map
.
Entry
<
String
,
List
<
OutboundTemplateVO
>>
entry
:
orderGroupMap
.
entrySet
())
{
String
orderId
=
entry
.
getKey
();
List
<
OutboundTemplateVO
>
voList
=
entry
.
getValue
();
...
...
@@ -294,8 +413,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
try
{
OutboundTemplateVO
firstVO
=
voList
.
get
(
0
);
// 检查出库单是否已存在(保留原有逻辑)
// 检查出库单是否已存在
OutboundOrders
outboundOrdersQuery
=
new
OutboundOrders
();
outboundOrdersQuery
.
setOrderId
(
orderId
);
List
<
OutboundOrders
>
existMains
=
outboundOrdersMapper
.
selectOutboundOrdersList
(
outboundOrdersQuery
);
...
...
@@ -309,38 +427,33 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
if
(!
Boolean
.
TRUE
.
equals
(
isUpdateSupport
))
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】已存在,且不支持更新"
,
orderId
));
}
// 复用已有主表ID(保留原有逻辑)
mainDO
=
existMain
;
BeanUtils
.
copyProperties
(
firstVO
,
mainDO
,
"id"
,
"createBy"
,
"createTime"
);
mainDO
.
setUpdateBy
(
operId
);
mainDO
.
setWarehouseId
(
"572ba484-199c-45d9-9735-610928ed5c70"
);
mainDO
.
setWarehouseId
(
WarehouseConfig
.
DEFAULT_WAREHOUSE_ID
);
mainDO
.
setUpdateTime
(
now
);
mainDO
.
setUpdateUserCode
(
operId
);
}
else
{
// 新增主表(保留原有结构,替换货主校验为预加载映射)
mainDO
=
new
OutboundOrders
();
BeanUtils
.
copyProperties
(
firstVO
,
mainDO
,
"sapNo"
,
"materialName"
,
"plannedQuantity"
,
"actualQuantity"
,
"plannedPackages"
,
"materialUnit"
,
"materialRemark"
);
//
========== 货主校验(替换为预加载映射) ==========
//
货主校验
String
ownerName
=
firstVO
.
getOwnerName
();
String
ownerId
=
firstVO
.
getOwnerId
();
if
(
StringUtils
.
isNotBlank
(
ownerName
))
{
// 从预加载映射获取货主ID,无需重复查询DB
String
mappedOwnerId
=
ownerNameToIdMap
.
get
(
ownerName
.
trim
());
if
(
StringUtils
.
isBlank
(
mappedOwnerId
))
{
throw
new
ServiceException
(
String
.
format
(
"业主【%s】不存在,无法新增入库单【%s】"
,
ownerName
,
orderId
));
}
// 货主ID匹配校验
if
(
ownerId
!=
null
&&
!
ownerId
.
equals
(
mappedOwnerId
))
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】的业主ID【%s】与业主名称【%s】不匹配"
,
orderId
,
ownerId
,
ownerName
));
}
mainDO
.
setOwnerId
(
mappedOwnerId
);
}
else
if
(
ownerId
!=
null
)
{
// 仅货主ID校验(保留原有逻辑)
Owners
ownerById
=
ownersService
.
selectOwnersById
(
ownerId
);
if
(
ownerById
==
null
)
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】的业主ID【%s】不存在"
,
...
...
@@ -351,7 +464,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】的业主名称/ID不能为空"
,
orderId
));
}
// 填充主表必填字段
(保留原有逻辑)
// 填充主表必填字段
mainDO
.
setId
(
UUID
.
randomUUID
().
toString
());
mainDO
.
setOrderId
(
orderId
);
mainDO
.
setOrderStatus
(
2L
);
...
...
@@ -365,7 +478,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
mainDO
.
setSortNo
(
Optional
.
ofNullable
(
mainDO
.
getSortNo
()).
orElse
(
0L
));
}
//
========== 明细校验(替换为预加载映射) ==========
//
明细校验
for
(
int
i
=
0
;
i
<
voList
.
size
();
i
++)
{
OutboundTemplateVO
vo
=
voList
.
get
(
i
);
int
lineNo
=
i
+
1
;
...
...
@@ -374,7 +487,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
BeanUtils
.
copyProperties
(
vo
,
itemDO
,
"orderId"
,
"systemNo"
,
"orderTypeId"
,
"batchId"
);
// 填充明细必填字段
(保留原有逻辑)
// 填充明细必填字段
itemDO
.
setId
(
UUID
.
randomUUID
().
toString
());
itemDO
.
setOrderId
(
orderId
);
itemDO
.
setBatchCode
(
Optional
.
ofNullable
(
mainDO
.
getBatchCode
()).
orElse
(
""
));
...
...
@@ -384,13 +497,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
itemDO
.
setCreateUserCode
(
operId
);
itemDO
.
setSortNo
(
0L
);
//
========== 物料SAP校验(替换为预加载映射) ==========
//
物料SAP校验
String
sapNo
=
vo
.
getSapNo
()
!=
null
?
vo
.
getSapNo
().
trim
()
:
""
;
if
(
StringUtils
.
isBlank
(
sapNo
))
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】第%d条明细的物料SAP号不能为空"
,
orderId
,
lineNo
));
}
// 从预加载映射获取物料ID,无需重复查询DB
String
materialId
=
sapToMaterialIdMap
.
get
(
sapNo
);
if
(
StringUtils
.
isBlank
(
materialId
))
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】第%d条明细的SAP号【%s】对应的物料不存在"
,
...
...
@@ -398,13 +510,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
itemDO
.
setMaterialId
(
materialId
);
//
========== 库位校验(替换为预加载映射) ==========
//
库位校验
String
locationName
=
vo
.
getLocationName
()
!=
null
?
vo
.
getLocationName
().
trim
()
:
""
;
if
(
StringUtils
.
isBlank
(
locationName
))
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】第%d条明细的库位名称不能为空"
,
orderId
,
lineNo
));
}
// 从预加载映射获取库位ID,无需重复查询DB
String
locationId
=
locationNameToIdMap
.
get
(
locationName
);
if
(
StringUtils
.
isBlank
(
locationId
))
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】第%d条明细的库位【%s】不存在"
,
...
...
@@ -412,7 +523,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
itemDO
.
setLocationId
(
locationId
);
//
========== 仓库校验(替换为预加载映射) ==========
//
仓库校验
String
warehouseName
=
vo
.
getWarehouseName
()
!=
null
?
vo
.
getWarehouseName
().
trim
()
:
""
;
String
warehouseId
=
warehouseNameToIdMap
.
get
(
warehouseName
);
if
(
StringUtils
.
isBlank
(
warehouseId
))
{
...
...
@@ -421,20 +532,17 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
itemDO
.
setWarehouseId
(
warehouseId
);
// ========== 库存校验(保留原有逻辑,使用预加载的ID) ==========
Inventory
itemsList
=
new
Inventory
();
itemsList
.
setWarehousesId
(
itemDO
.
getWarehouseId
());
itemsList
.
setMaterialId
(
itemDO
.
getMaterialId
());
itemsList
.
setLocationId
(
itemDO
.
getLocationId
());
itemsList
.
setInventoryType
(
Long
.
valueOf
(
orderType
));
List
<
Inventory
>
inventory
=
inventoryService
.
selectInventoryList
(
itemsList
);
if
(
CollectionUtils
.
isEmpty
(
inventory
))
{
// 库存校验(包含inventoryType)
String
inventoryTypeStr
=
Optional
.
ofNullable
(
orderType
).
map
(
String:
:
valueOf
).
orElse
(
""
);
String
inventoryMatchKey
=
String
.
join
(
"_"
,
warehouseId
,
materialId
,
locationId
,
inventoryTypeStr
);
AbstractMap
.
SimpleEntry
<
String
,
Long
>
inventoryEntry
=
inventoryTOIdMap
.
get
(
inventoryMatchKey
);
if
(
inventoryEntry
==
null
)
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】第%d条明细:仓库【%s】+物料【%s】+库位【%s】组合的库存记录不存在"
,
orderId
,
lineNo
,
vo
.
getWarehouseName
(),
vo
.
getMaterialName
(),
vo
.
getLocationName
()));
"入库单号【%s】第%d条明细:仓库【%s】+物料【%s】+库位【%s】
+库存类型【%s】
组合的库存记录不存在"
,
orderId
,
lineNo
,
vo
.
getWarehouseName
(),
vo
.
getMaterialName
(),
vo
.
getLocationName
()
,
inventoryTypeStr
));
}
itemDO
.
setInventoryId
(
inventory
.
get
(
0
).
getId
());
itemDO
.
setInventoryId
(
inventory
Entry
.
getKey
());
itemDO
.
setInventoryType
(
inventoryTypeStr
);
// 填充库存类型
itemDO
.
setItemStatus
(
3L
);
itemDOList
.
add
(
itemDO
);
}
...
...
@@ -450,26 +558,21 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
//
5. 有验证失败直接抛异常(保留原有逻辑)
//
6. 有验证失败直接抛异常
if
(
hasValidateError
)
{
throw
new
ServiceException
(
String
.
format
(
"验证失败,导入终止!失败详情:%s"
,
failureMsg
.
toString
()));
}
//
6. 验证全通过:统一执行入库/更新操作(保留原有逻辑)
//
7. 执行入库/更新操作
for
(
Map
.
Entry
<
String
,
OutboundOrders
>
entry
:
validMainMap
.
entrySet
())
{
String
orderId
=
entry
.
getKey
();
OutboundOrders
mainDO
=
entry
.
getValue
();
List
<
OutboundOrderItems
>
itemDOList
=
validItemMap
.
get
(
orderId
);
if
(
mainDO
!=
null
)
{
outboundOrdersMapper
.
insertOutboundOrders
(
mainDO
);
totalMainSuccess
++;
successMsg
.
append
(
String
.
format
(
"入库单号【%s】已新增;\n"
,
orderId
));
}
else
{
outboundOrdersMapper
.
updateOutboundOrders
(
mainDO
);
totalMainSuccess
++;
successMsg
.
append
(
String
.
format
(
"入库单号【%s】已更新;\n"
,
orderId
));
}
if
(!
CollectionUtils
.
isEmpty
(
itemDOList
))
{
int
itemSuccess
=
outboundOrderItemsMapper
.
batchInsertOutboundOrderItems
(
itemDOList
);
...
...
@@ -484,9 +587,8 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
//
事务提交后执行自定义逻辑(保留原有逻辑)
//
8. 事务提交后执行库存扣减
if
(
TransactionSynchronizationManager
.
isSynchronizationActive
())
{
Map
<
String
,
OutboundOrders
>
finalValidMainMap
=
new
HashMap
<>(
validMainMap
);
Map
<
String
,
List
<
OutboundOrderItems
>>
finalValidItemMap
=
new
HashMap
<>(
validItemMap
);
String
finalOperId
=
operId
;
Date
finalNow
=
now
;
...
...
@@ -494,14 +596,18 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
TransactionSynchronizationManager
.
registerSynchronization
(
new
TransactionSynchronization
()
{
@Override
public
void
afterCommit
()
{
executeCustomLogic
(
finalValidMainMap
,
finalValidItemMap
,
finalOperId
,
finalNow
);
for
(
List
<
OutboundOrderItems
>
itemList
:
finalValidItemMap
.
values
())
{
deductInventory
(
itemList
,
finalOperId
,
finalNow
);
}
}
});
}
else
{
executeCustomLogic
(
validMainMap
,
validItemMap
,
operId
,
now
);
for
(
List
<
OutboundOrderItems
>
itemList
:
validItemMap
.
values
())
{
deductInventory
(
itemList
,
operId
,
now
);
}
}
//
8. 结果汇总(保留原有逻辑)
//
9. 结果汇总
if
(
totalMainFailure
>
0
||
totalItemFailure
>
0
)
{
String
finalFailureMsg
=
String
.
format
(
"导入结果:成功新增/更新%d个入库单,失败%d个;成功导入%d条明细,失败%d条。失败详情:%s"
,
...
...
@@ -517,10 +623,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
}
}
// ========== 新增:预加载映射辅助方法(核心) ==========
/**
* 预加载物料SAP号 -> 物料ID映射
*/
// ========== 预加载映射辅助方法 ==========
private
Map
<
String
,
String
>
loadSapToMaterialIdMap
()
{
List
<
Materials
>
materialsList
=
materialsService
.
selectMaterialsList
(
new
Materials
());
if
(
CollectionUtils
.
isEmpty
(
materialsList
))
{
...
...
@@ -529,18 +632,15 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return
materialsList
.
stream
()
.
filter
(
m
->
StringUtils
.
isNotBlank
(
m
.
getSapNo
()))
.
collect
(
Collectors
.
toMap
(
m
->
m
.
getSapNo
().
trim
(),
// key:SAP号(去空格)
Materials:
:
getId
,
// value:物料ID
(
k1
,
k2
)
->
k1
// 重复SAP号保留第一个
m
->
m
.
getSapNo
().
trim
(),
Materials:
:
getId
,
(
k1
,
k2
)
->
k1
));
}
/**
* 预加载库位名称(locationCode) -> 库位ID映射
*/
private
Map
<
String
,
String
>
loadLocationNameToIdMap
()
{
StorageLocations
query
=
new
StorageLocations
();
query
.
setIsUsed
(
1L
);
// 只查可用库位
query
.
setIsUsed
(
1L
);
List
<
StorageLocations
>
locationList
=
storageLocationsService
.
selectStorageLocationsList
(
query
);
if
(
CollectionUtils
.
isEmpty
(
locationList
))
{
return
Collections
.
emptyMap
();
...
...
@@ -548,18 +648,15 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return
locationList
.
stream
()
.
filter
(
l
->
StringUtils
.
isNotBlank
(
l
.
getLocationCode
()))
.
collect
(
Collectors
.
toMap
(
l
->
l
.
getLocationCode
().
trim
(),
// key:库位名称(去空格)
StorageLocations:
:
getId
,
// value:库位ID
(
k1
,
k2
)
->
k1
// 重复库位名称保留第一个
l
->
l
.
getLocationCode
().
trim
(),
StorageLocations:
:
getId
,
(
k1
,
k2
)
->
k1
));
}
/**
* 预加载仓库名称 -> 仓库ID映射
*/
private
Map
<
String
,
String
>
loadWarehouseNameToIdMap
()
{
Warehouses
query
=
new
Warehouses
();
query
.
setIsUsed
(
1L
);
// 只查可用仓库
query
.
setIsUsed
(
1L
);
List
<
Warehouses
>
warehouseList
=
warehousesService
.
selectWarehousesList
(
query
);
if
(
CollectionUtils
.
isEmpty
(
warehouseList
))
{
return
Collections
.
emptyMap
();
...
...
@@ -567,15 +664,12 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return
warehouseList
.
stream
()
.
filter
(
w
->
StringUtils
.
isNotBlank
(
w
.
getWarehousesName
()))
.
collect
(
Collectors
.
toMap
(
w
->
w
.
getWarehousesName
().
trim
(),
// key:仓库名称(去空格)
Warehouses:
:
getId
,
// value:仓库ID
(
k1
,
k2
)
->
k1
// 重复仓库名称保留第一个
w
->
w
.
getWarehousesName
().
trim
(),
Warehouses:
:
getId
,
(
k1
,
k2
)
->
k1
));
}
/**
* 预加载货主名称 -> 货主ID映射
*/
private
Map
<
String
,
String
>
loadOwnerNameToIdMap
()
{
List
<
Owners
>
ownerList
=
ownersService
.
selectOwnersList
(
new
Owners
());
if
(
CollectionUtils
.
isEmpty
(
ownerList
))
{
...
...
@@ -584,96 +678,56 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
return
ownerList
.
stream
()
.
filter
(
o
->
StringUtils
.
isNotBlank
(
o
.
getOwnerName
()))
.
collect
(
Collectors
.
toMap
(
o
->
o
.
getOwnerName
().
trim
(),
// key:货主名称(去空格)
Owners:
:
getId
,
// value:货主ID
(
k1
,
k2
)
->
k1
// 重复货主名称保留第一个
o
->
o
.
getOwnerName
().
trim
(),
Owners:
:
getId
,
(
k1
,
k2
)
->
k1
));
}
private
Map
<
String
,
String
>
loadInventoryTOIdMap
()
{
private
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
loadInventoryTOIdMap
()
{
Inventory
inventory
=
new
Inventory
();
inventory
.
setInventoryStatus
(
1
l
);
inventory
.
setIsUsed
(
1
l
);
inventory
.
setInventoryStatus
(
1
L
);
inventory
.
setIsUsed
(
1
L
);
List
<
Inventory
>
inventoryList
=
inventoryService
.
selectInventoryList
(
inventory
);
if
(
CollectionUtils
.
isEmpty
(
inventoryList
))
{
return
Collections
.
emptyMap
();
}
return
inventoryList
.
stream
()
.
filter
(
inv
->
{
// 过滤有效库存:仓库/物料/库位/库存ID都不能为空
return
StringUtils
.
isNotBlank
(
inv
.
getWarehousesId
());
})
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
emptyLocationMap
=
inventoryList
.
stream
()
.
filter
(
inv
->
StringUtils
.
isNotBlank
(
inv
.
getWarehousesId
())
&&
StringUtils
.
isNotBlank
(
inv
.
getMaterialId
())
&&
inv
.
getInventoryType
()
!=
null
&&
StringUtils
.
isNotBlank
(
inv
.
getId
())
&&
StringUtils
.
isBlank
(
inv
.
getLocationId
()))
.
collect
(
Collectors
.
toMap
(
// Key:仓库ID+物料ID+库位ID(拼接成唯一标识)
inv
->
String
.
join
(
"_"
,
inv
.
getWarehousesId
().
trim
(),
inv
.
getMaterialId
().
trim
(),
inv
.
getLocationId
().
trim
()
),
// Value:库存ID(去空格)
inv
->
inv
.
getId
().
trim
(),
// 重复Key处理:保留第一个(避免主键冲突)
""
,
inv
.
getInventoryType
().
toString
()),
inv
->
new
AbstractMap
.
SimpleEntry
<>(
inv
.
getId
().
trim
(),
Optional
.
ofNullable
(
inv
.
getQuantity
()).
orElse
(
0L
)),
(
k1
,
k2
)
->
k1
,
// 指定Map类型(可选,默认HashMap)
HashMap:
:
new
));
}
// 仅修改这个方法中【设置日志ID】的一行代码,其余完全保留
private
void
executeCustomLogic
(
Map
<
String
,
OutboundOrders
>
validMainMap
,
Map
<
String
,
List
<
OutboundOrderItems
>>
validItemMap
,
String
operId
,
Date
now
)
{
for
(
String
orderId
:
validMainMap
.
keySet
())
{
OutboundOrders
mainDO
=
validMainMap
.
get
(
orderId
);
List
<
OutboundOrderItems
>
itemList
=
validItemMap
.
get
(
orderId
);
System
.
out
.
println
(
String
.
format
(
"订单【%s】导入成功,主表ID:%s,明细数:%d"
,
orderId
,
mainDO
.
getId
(),
itemList
.
size
()));
List
<
OutboundOrderLog
>
outboundOrderLogs
=
new
ArrayList
<>();
for
(
OutboundOrderItems
item
:
itemList
)
{
OutboundOrderLog
outboundOrderLog
=
new
OutboundOrderLog
();
// ========== 唯一改动:设置日志ID = 明细ID ==========
outboundOrderLog
.
setId
(
item
.
getId
());
outboundOrderLog
.
setOrderId
(
item
.
getOutboundOrderId
());
BeanUtils
.
copyProperties
(
item
,
outboundOrderLog
);
outboundOrderLogs
.
add
(
outboundOrderLog
);
}
outboundOrderLogMapper
.
batchOutboundOrderLog
(
outboundOrderLogs
);
ship
(
itemList
);
}
}
public
int
ship
(
List
<
OutboundOrderItems
>
outboundOrderItems
)
{
if
(!
outboundOrderItems
.
isEmpty
())
{
List
<
String
>
inventoryIds
=
new
ArrayList
<>();
// 手动收集inventoryId
for
(
OutboundOrderItems
outboundOrderItem
:
outboundOrderItems
)
{
// 直接用明细的inventoryId,不查日志!
String
inventoryId
=
outboundOrderItem
.
getInventoryId
();
if
(
StringUtils
.
isBlank
(
inventoryId
))
{
throw
new
ServiceException
(
"明细ID【"
+
outboundOrderItem
.
getId
()
+
"】的库存ID为空,无法扣减"
);
}
Inventory
inventory
=
inventoryService
.
selectInventoryById
(
inventoryId
);
if
(
inventory
==
null
)
{
throw
new
ServiceException
(
"库存ID【"
+
inventoryId
+
"】不存在,无法扣减"
);
}
// 扣减库存
inventory
.
setQuantity
(
inventory
.
getQuantity
()
-
outboundOrderItem
.
getActualQuantity
());
if
(
inventory
.
getQuantity
()
==
0
)
{
inventory
.
setInventoryStatus
(
0L
);
}
inventoryService
.
updateInventory
(
inventory
);
Map
<
String
,
AbstractMap
.
SimpleEntry
<
String
,
Long
>>
nonEmptyLocationMap
=
inventoryList
.
stream
()
.
filter
(
inv
->
StringUtils
.
isNotBlank
(
inv
.
getWarehousesId
())
&&
StringUtils
.
isNotBlank
(
inv
.
getMaterialId
())
&&
StringUtils
.
isNotBlank
(
inv
.
getLocationId
())
&&
StringUtils
.
isNotBlank
(
inv
.
getId
())
&&
inv
.
getInventoryType
()
!=
null
)
.
collect
(
Collectors
.
toMap
(
inv
->
String
.
join
(
"_"
,
inv
.
getWarehousesId
().
trim
(),
inv
.
getMaterialId
().
trim
(),
inv
.
getLocationId
().
trim
(),
inv
.
getInventoryType
().
toString
()),
inv
->
new
AbstractMap
.
SimpleEntry
<>(
inv
.
getId
().
trim
(),
Optional
.
ofNullable
(
inv
.
getQuantity
()).
orElse
(
0L
)),
(
k1
,
k2
)
->
k1
,
HashMap:
:
new
));
inventoryIds
.
add
(
inventoryId
);
// 收集库存ID用于刷新
}
inventoryService
.
RefreshInventory
(
inventoryIds
);
}
return
1
;
emptyLocationMap
.
putAll
(
nonEmptyLocationMap
);
return
emptyLocationMap
;
}
}
\ No newline at end of file
ruoyi-inventory/src/main/resources/mapper/inventory/OutboundOrdersMapper.xml
View file @
63dd6d31
...
...
@@ -344,4 +344,15 @@
select count(*) from outbound_orders where is_used = 1 and order_status=2 and inbound_date
>
= DATE_FORMAT(CURDATE(), '%Y-%m-01')
and inbound_date
<
DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
</select>
<update
id=
"batchUpdateInventory"
>
<foreach
collection=
"list"
item=
"item"
separator=
";"
>
UPDATE inventory
SET quantity = #{item.quantity},
inventory_status = #{item.inventoryStatus},
update_by = #{item.updateBy},
update_time = #{item.updateTime}
WHERE id = #{item.id}
</foreach>
</update>
</mapper>
\ No newline at end of file
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论