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
92fcc23b
Commit
92fcc23b
authored
Dec 15, 2025
by
yubin
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/master'
parents
69db319b
ab86dcba
显示空白字符变更
内嵌
并排
正在显示
20 个修改的文件
包含
254 行增加
和
76 行删除
+254
-76
ruoyi-admin-vue/.env.development
+1
-1
ruoyi-admin-vue/.env.production
+1
-1
ruoyi-admin-vue/.env.staging
+1
-1
ruoyi-admin-vue/package.json
+2
-2
ruoyi-admin-vue/src/views/inventory/inbound/details.vue
+1
-1
ruoyi-admin-vue/src/views/inventory/inbound/index.vue
+5
-4
ruoyi-admin-vue/src/views/inventory/return_op/index.vue
+0
-1
ruoyi-admin-vue/vue.config.js
+2
-2
ruoyi-admin/src/main/java/com/ruoyi/web/controller/inventory/InboundOrdersController.java
+54
-1
ruoyi-admin/src/main/resources/application-druid.yml
+7
-1
ruoyi-admin/src/main/resources/application.yml
+1
-1
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
+32
-10
ruoyi-inventory/src/main/java/com/ruoyi/inventory/controller/InventoryController.java
+2
-2
ruoyi-inventory/src/main/java/com/ruoyi/inventory/controller/InventoryStatisticsController.java
+1
-1
ruoyi-inventory/src/main/java/com/ruoyi/inventory/controller/ReturnOrderItemsController.java
+3
-3
ruoyi-inventory/src/main/java/com/ruoyi/inventory/domain/Inventory.java
+2
-0
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/InboundOrdersServiceImpl.java
+133
-42
ruoyi-inventory/src/main/resources/mapper/inventory/InboundOrderItemsMapper.xml
+2
-1
ruoyi-inventory/src/main/resources/mapper/inventory/InboundOrdersMapper.xml
+1
-0
ruoyi-inventory/src/main/resources/mapper/inventory/InventoryMapper.xml
+3
-1
没有找到文件。
ruoyi-admin-vue/.env.development
View file @
92fcc23b
# 页面标题
VUE_APP_TITLE =
若依
管理系统
VUE_APP_TITLE =
库存
管理系统
# 开发环境配置
ENV = 'development'
...
...
ruoyi-admin-vue/.env.production
View file @
92fcc23b
# 页面标题
VUE_APP_TITLE =
若依
管理系统
VUE_APP_TITLE =
库存
管理系统
# 生产环境配置
ENV = 'production'
...
...
ruoyi-admin-vue/.env.staging
View file @
92fcc23b
# 页面标题
VUE_APP_TITLE =
若依
管理系统
VUE_APP_TITLE =
库存
管理系统
BABEL_ENV = production
...
...
ruoyi-admin-vue/package.json
View file @
92fcc23b
{
"name"
:
"ruoyi"
,
"version"
:
"3.9.0"
,
"description"
:
"
若依
管理系统"
,
"author"
:
"
若依
"
,
"description"
:
"
库存
管理系统"
,
"author"
:
""
,
"license"
:
"MIT"
,
"scripts"
:
{
"dev"
:
"vue-cli-service serve"
,
...
...
ruoyi-admin-vue/src/views/inventory/inbound/details.vue
View file @
92fcc23b
...
...
@@ -110,7 +110,7 @@
height=
"100%"
:data=
"inboundList"
@
selection-change=
"handleSelectionChange"
:row-key=
"(row) => row.materialId + '_' + row.orderId"
:row-key=
"(row) => row.materialId + '_' + row.orderId
+ '_' + row.warehousesName + '_' + row.locationName
"
>
<el-table-column
label=
"物料SAPNO"
align=
"center"
prop=
"sapNo"
width=
"200"
/>
<el-table-column
label=
"物料名称"
align=
"center"
prop=
"materialName"
width=
"200"
/>
...
...
ruoyi-admin-vue/src/views/inventory/inbound/index.vue
View file @
92fcc23b
...
...
@@ -177,9 +177,9 @@
</el-tag>
</
template
>
</el-table-column>
<el-table-column
label=
"
创建时间"
align=
"center"
prop=
"createTim
e"
width=
"200"
>
<el-table-column
label=
"
入库时间"
align=
"center"
prop=
"inboundDat
e"
width=
"200"
>
<
template
slot-scope=
"scope"
>
<span>
{{
parseTime
(
scope
.
row
.
createTime
)
}}
</span>
<span>
{{
parseTime
(
scope
.
row
.
inboundDate
,
'{y
}
-{m
}
-{d
}
'
)
}}
<
/span
>
<
/template
>
<
/el-table-column
>
<
el
-
table
-
column
...
...
@@ -903,7 +903,7 @@ export default {
const queryForm = {
pageNum: 1,
pageSize: 9999,
orderId
:
row
.
orderI
d
inboundOrderId: row.i
d
}
const response = await listInbound_itemsAndMname(queryForm)
row.inboundOrderItemsList = response.rows.map(item => {
...
...
@@ -915,7 +915,8 @@ export default {
ownerId: row.ownerId,
quantity: item.actualQuantity,
unitWeight: item.unitWeight,
isUsed
:
1
isUsed: 1,
unitPrice: item.unitPrice
}
;
}
)
// 第三步:确保数据存在后调用入库接口
...
...
ruoyi-admin-vue/src/views/inventory/return_op/index.vue
View file @
92fcc23b
...
...
@@ -183,7 +183,6 @@
type
=
"text"
icon
=
"el-icon-more"
@
click
=
"handleDetail(scope.row)"
v
-
hasPermi
=
"['inventory:return_orders:view']"
v
-
show
=
"(scope.row.orderStatus === 2)"
>
详情
<
/el-button
>
<
/template
>
...
...
ruoyi-admin-vue/vue.config.js
View file @
92fcc23b
...
...
@@ -7,9 +7,9 @@ function resolve(dir) {
const
CompressionPlugin
=
require
(
'compression-webpack-plugin'
)
const
name
=
process
.
env
.
VUE_APP_TITLE
||
'
若依
管理系统'
// 网页标题
const
name
=
process
.
env
.
VUE_APP_TITLE
||
'管理系统'
// 网页标题
const
baseUrl
=
'http://localhost:
8080
'
// 后端接口
const
baseUrl
=
'http://localhost:
9001
'
// 后端接口
const
port
=
process
.
env
.
port
||
process
.
env
.
npm_config_port
||
80
// 端口
...
...
ruoyi-admin/src/main/java/com/ruoyi/web/controller/inventory/InboundOrdersController.java
View file @
92fcc23b
package
com
.
ruoyi
.
web
.
controller
.
inventory
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.stream.Collectors
;
import
com.ruoyi.common.core.domain.entity.Materials
;
import
com.ruoyi.common.utils.uuid.UUID
;
...
...
@@ -11,6 +13,7 @@ import com.ruoyi.inventory.domain.vo.InboundMaterialTotalVO;
import
com.ruoyi.inventory.domain.vo.InboundTemplateVO
;
import
org.springframework.security.access.prepost.PreAuthorize
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.util.CollectionUtils
;
import
org.springframework.web.bind.annotation.*
;
import
com.ruoyi.common.annotation.Log
;
import
com.ruoyi.common.core.controller.BaseController
;
...
...
@@ -127,13 +130,63 @@ public class InboundOrdersController extends BaseController
@RequestParam
(
"updateSupport"
)
Integer
updateSupport
,
@RequestParam
(
value
=
"orderType"
,
required
=
false
)
Integer
orderType
)
throws
Exception
{
// 防护1:校验文件非空
if
(
file
==
null
||
file
.
isEmpty
())
{
return
error
(
"导入文件不能为空!"
);
}
// 防护2:校验文件格式
String
fileName
=
file
.
getOriginalFilename
();
if
(!
fileName
.
endsWith
(
".xlsx"
)
&&
!
fileName
.
endsWith
(
".xls"
))
{
return
error
(
"仅支持Excel格式文件(.xlsx/.xls)!"
);
}
// 第二步:校验Excel列名是否匹配模板(核心!拦截非模板数据)
List
<
String
>
templateColumns
=
Arrays
.
asList
(
"入库日期"
,
"SAP No"
,
"物料名称"
,
"TS Code"
,
"货主"
,
"批号"
,
"计划数量"
,
"单号"
,
"系统编号"
,
"件重"
,
"约数"
,
"实际件数"
,
"实发数量"
,
"仓库"
,
"库位"
,
"标签颜色"
,
"凭证号"
,
"单价"
,
"备注"
,
"订单类型"
,
"收货人"
,
"物料备注"
);
List
<
String
>
excelColumns
=
ExcelUtil
.
getExcelHeader
(
file
.
getInputStream
());
// 自定义方法读取表头
if
(
CollectionUtils
.
isEmpty
(
excelColumns
)
||
!
excelColumns
.
containsAll
(
templateColumns
))
{
return
AjaxResult
.
error
(
"导入文件不是标准模板!请下载官方模板后填写(缺失列:"
+
getMissingColumns
(
templateColumns
,
excelColumns
)
+
")"
);
}
ExcelUtil
<
InboundTemplateVO
>
util
=
new
ExcelUtil
<
InboundTemplateVO
>(
InboundTemplateVO
.
class
);
List
<
InboundTemplateVO
>
inboundOrders
=
util
.
importExcel
(
file
.
getInputStream
());
// 防护3:拦截空列表,避免 Service 层处理空数据
if
(
CollectionUtils
.
isEmpty
(
inboundOrders
))
{
return
error
(
"Excel中未解析到有效数据,请检查模板是否正确!"
);
}
String
operName
=
getUsername
();
String
message
=
inboundOrdersService
.
importInboundOrders
(
inboundOrders
,
updateSupport
,
operName
,
orderType
);
return
success
(
message
);
}
// 辅助方法:获取缺失的列名
private
String
getMissingColumns
(
List
<
String
>
template
,
List
<
String
>
excel
)
{
return
template
.
stream
()
.
filter
(
col
->
!
excel
.
contains
(
col
))
.
collect
(
Collectors
.
joining
(
"、"
));
}
/**
* 首页入库次数统计api
...
...
ruoyi-admin/src/main/resources/application-druid.yml
View file @
92fcc23b
...
...
@@ -2,11 +2,17 @@
spring
:
datasource
:
type
:
com.alibaba.druid.pool.DruidDataSource
driverClassName
:
com.mysql.cj.jdbc.Driver
#driverClassName: com.mysql.cj.jdbc.Driver
driver-class-name
:
com.mysql.cj.jdbc.Driver
# 配置MySQL的驱动程序类
druid
:
# 主库数据源
master
:
# 数据库连接地址
#生產
#url: jdbc:mysql://172.19.1.150:9012/inventory_manager?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&useSSL=false
#username: root # 数据库用户名
#password: 'Aa123456'
#測試
url
:
jdbc:mysql://demo.docmis.cn:23500/inventory_manager?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&useSSL=false
username
:
root
password
:
'
!QAZ2wsx#EDC2022'
...
...
ruoyi-admin/src/main/resources/application.yml
View file @
92fcc23b
...
...
@@ -16,7 +16,7 @@ ruoyi:
# 开发环境配置
server
:
# 服务器的HTTP端口,默认为8080
port
:
8080
port
:
9001
servlet
:
# 应用的访问路径
context-path
:
/
...
...
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
View file @
92fcc23b
...
...
@@ -12,17 +12,9 @@ import java.math.BigDecimal;
import
java.text.DecimalFormat
;
import
java.time.LocalDate
;
import
java.time.LocalDateTime
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Comparator
;
import
java.util.Date
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.UUID
;
import
java.util.*
;
import
java.util.stream.Collectors
;
import
java.util.stream.IntStream
;
import
javax.servlet.http.HttpServletResponse
;
import
org.apache.commons.lang3.ArrayUtils
;
import
org.apache.commons.lang3.RegExUtils
;
...
...
@@ -1909,4 +1901,34 @@ public class ExcelUtil<T>
}
return
method
;
}
/**
* 读取Excel表头(第一行)
*/
public
static
List
<
String
>
getExcelHeader
(
InputStream
inputStream
)
{
try
(
Workbook
workbook
=
WorkbookFactory
.
create
(
inputStream
))
{
Sheet
sheet
=
workbook
.
getSheetAt
(
0
);
Row
headerRow
=
sheet
.
getRow
(
0
);
// 第一行是表头
if
(
headerRow
==
null
)
{
return
Collections
.
emptyList
();
}
return
IntStream
.
range
(
0
,
headerRow
.
getLastCellNum
())
.
mapToObj
(
headerRow:
:
getCell
)
.
map
(
cell
->
{
// 单元格类型容错(数字/字符串统一转字符串)
if
(
cell
==
null
)
{
return
""
;
}
cell
.
setCellType
(
CellType
.
STRING
);
return
StringUtils
.
trim
(
cell
.
getStringCellValue
());
})
.
filter
(
StringUtils:
:
isNotBlank
)
.
collect
(
Collectors
.
toList
());
}
catch
(
Exception
e
)
{
log
.
error
(
"读取Excel表头失败"
,
e
);
return
Collections
.
emptyList
();
}
}
}
ruoyi-inventory/src/main/java/com/ruoyi/inventory/controller/InventoryController.java
View file @
92fcc23b
...
...
@@ -68,7 +68,7 @@ public class InventoryController extends BaseController
/**
* 查询库存列表(按物料汇总统计)
*/
@PreAuthorize
(
"@ss.hasPermi('inventory:inventory:list')"
)
//
@PreAuthorize("@ss.hasPermi('inventory:inventory:list')")
@GetMapping
(
"/listCount"
)
public
TableDataInfo
listCount
(
Inventory
inventory
)
{
...
...
@@ -80,7 +80,7 @@ public class InventoryController extends BaseController
/**
* 查询库存明细列表(根据物料标识及检索条件)
*/
@PreAuthorize
(
"@ss.hasPermi('inventory:inventory:list')"
)
//
@PreAuthorize("@ss.hasPermi('inventory:inventory:list')")
@GetMapping
(
"/detailList"
)
public
TableDataInfo
detailList
(
Inventory
inventory
)
{
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/controller/InventoryStatisticsController.java
View file @
92fcc23b
...
...
@@ -31,7 +31,7 @@ public class InventoryStatisticsController extends BaseController {
/**
* 出入库统计列表
*/
@PreAuthorize
(
"@ss.hasPermi('inventory:statistics:list')"
)
//
@PreAuthorize("@ss.hasPermi('inventory:statistics:list')")
@GetMapping
(
"/inboundOutbound"
)
public
TableDataInfo
list
(
InboundOutboundStatisticsVO
query
)
{
startPage
();
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/controller/ReturnOrderItemsController.java
View file @
92fcc23b
...
...
@@ -43,7 +43,7 @@ public class ReturnOrderItemsController extends BaseController
/**
* 查询退库单明细列表
*/
@PreAuthorize
(
"@ss.hasPermi('inventory:return_order_items:count_detail')"
)
//
@PreAuthorize("@ss.hasPermi('inventory:return_order_items:count_detail')")
@GetMapping
(
"/count_detail"
)
public
TableDataInfo
list
(
ReturnOrderItems
returnOrderItems
)
{
...
...
@@ -69,7 +69,7 @@ public class ReturnOrderItemsController extends BaseController
/**
* 获取退库单明细详细信息
*/
@PreAuthorize
(
"@ss.hasPermi('inventory:return_order_items:query')"
)
//
@PreAuthorize("@ss.hasPermi('inventory:return_order_items:query')")
@GetMapping
(
value
=
"/{id}"
)
public
AjaxResult
getInfo
(
@PathVariable
(
"id"
)
String
id
)
{
...
...
@@ -112,7 +112,7 @@ public class ReturnOrderItemsController extends BaseController
/**
* 查询退库单明细列表
*/
@PreAuthorize
(
"@ss.hasPermi('inventory:return_order_items:count')"
)
//
@PreAuthorize("@ss.hasPermi('inventory:return_order_items:count')")
@PostMapping
(
"/count"
)
public
TableDataInfo
count
(
ReturnOrderItems
returnOrderItems
)
{
...
...
ruoyi-inventory/src/main/java/com/ruoyi/inventory/domain/Inventory.java
View file @
92fcc23b
...
...
@@ -178,4 +178,6 @@ public class Inventory extends BaseEntity
/** 特殊存储要求 */
// @Excel(name = "特殊存储要求")
private
String
specialRequirements
;
private
Double
unitPrice
;
}
ruoyi-inventory/src/main/java/com/ruoyi/inventory/service/impl/InboundOrdersServiceImpl.java
View file @
92fcc23b
...
...
@@ -9,6 +9,7 @@ import com.ruoyi.inventory.domain.*;
import
com.ruoyi.inventory.domain.vo.InboundMaterialTotalVO
;
import
com.ruoyi.inventory.mapper.*
;
import
org.apache.commons.lang3.SystemUtils
;
import
org.springframework.transaction.interceptor.TransactionAspectSupport
;
import
org.springframework.util.CollectionUtils
;
import
com.ruoyi.common.exception.ServiceException
;
import
com.ruoyi.common.utils.DateUtils
;
...
...
@@ -186,15 +187,38 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
StringBuilder
failureMsg
=
new
StringBuilder
();
Date
now
=
DateUtils
.
getNowDate
();
Long
userId
=
SecurityUtils
.
getUserId
();
String
operId
=
userId
.
toString
();
Warehouses
warehouses
=
new
Warehouses
();
Owners
owners
=
new
Owners
();
StorageLocations
storageLocations
=
new
StorageLocations
();
String
operId
=
Optional
.
ofNullable
(
userId
).
map
(
String:
:
valueOf
).
orElse
(
""
);
// 预加载物料SAP-ID映射
Map
<
String
,
Map
<
String
,
String
>>
sapAndIdMap
=
Optional
.
ofNullable
(
materialsMapper
.
selectMaterialIdAndSapMap
())
.
orElse
(
Collections
.
emptyMap
());
// 预加载仓库名称-ID映射
Map
<
String
,
String
>
warehouseNameIdMap
=
loadWarehouseNameIdMap
();
// 预加载库位名称-ID映射
Map
<
String
,
String
>
storageLocationNameIdMap
=
loadStorageLocationNameIdMap
();
// 3. 按入库单号分组(核心:同一入库单的多条明细归为一组)
Map
<
String
,
List
<
InboundTemplateVO
>>
orderGroupMap
=
inboundOrdersList
.
stream
()
.
filter
(
vo
->
StringUtils
.
isNotBlank
(
vo
.
getOrderId
()))
// 过滤无入库单号的无效行
.
collect
(
Collectors
.
groupingBy
(
InboundTemplateVO:
:
getOrderId
));
Map
<
String
,
Map
<
String
,
String
>>
sapAndIdMap
=
materialsMapper
.
selectMaterialIdAndSapMap
();
Map
<
String
,
List
<
InboundTemplateVO
>>
orderGroupMap
=
Optional
.
ofNullable
(
inboundOrdersList
)
.
orElse
(
Collections
.
emptyList
())
// 空列表兜底,避免NPE
.
stream
()
.
filter
(
Objects:
:
nonNull
)
// 过滤null的VO对象
.
map
(
vo
->
{
// 预处理:入库单号去空格,统一格式
String
orderId
=
Optional
.
ofNullable
(
vo
.
getOrderId
())
.
map
(
String:
:
trim
)
// 去除首尾空格
.
orElse
(
""
);
vo
.
setOrderId
(
orderId
);
// 把处理后的单号回写,保证后续使用一致
return
vo
;
})
.
filter
(
vo
->
StringUtils
.
isNotBlank
(
vo
.
getOrderId
()))
// 过滤无/空白入库单号的无效行
.
collect
(
Collectors
.
groupingBy
(
InboundTemplateVO:
:
getOrderId
,
// 分组Key:处理后的入库单号
Collectors
.
collectingAndThen
(
Collectors
.
toList
(),
list
->
Collections
.
unmodifiableList
(
list
)
// 生成不可变列表,防止后续篡改
)
));
// 兜底:若分组结果为空,返回空的不可变Map(避免后续判空)
orderGroupMap
=
Optional
.
ofNullable
(
orderGroupMap
).
orElse
(
Collections
.
emptyMap
());
// 4. 遍历每个入库单分组处理
for
(
Map
.
Entry
<
String
,
List
<
InboundTemplateVO
>>
entry
:
orderGroupMap
.
entrySet
())
{
String
orderId
=
entry
.
getKey
();
...
...
@@ -207,6 +231,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
InboundTemplateVO
firstVO
=
voList
.
get
(
0
);
// 取第一条VO的主表信息
// 检查入库单是否已存在
InboundOrders
existMain
=
inboundOrdersMapper
.
selectInboundOrdersByOrderId
(
orderId
);
if
(
existMain
!=
null
)
{
if
(
isUpdateSupport
==
0
)
{
// 不支持更新,跳过该入库单
...
...
@@ -222,7 +247,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
mainDO
.
setUpdateBy
(
operId
);
mainDO
.
setUpdateTime
(
now
);
mainDO
.
setUpdateUserCode
(
operId
);
mainDO
.
setOrderTypeId
(
orderType
+
""
);
mainDO
.
setOrderTypeId
(
Optional
.
ofNullable
(
orderType
).
map
(
String:
:
valueOf
).
orElse
(
""
)
);
// 更新主表
inboundOrdersMapper
.
updateInboundOrders
(
mainDO
);
totalMainSuccess
++;
...
...
@@ -235,20 +260,12 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
"sapNo"
,
"materialName"
,
"plannedQuantity"
,
"actualQuantity"
,
"plannedPackages"
,
"materialUnit"
,
"materialRemark"
);
// 排除子表字段
// 填充主表必填字段
mainDO
.
setId
(
UUID
.
randomUUID
().
toString
());
mainDO
.
setOrderId
(
orderId
);
mainDO
.
setCreateBy
(
operId
);
mainDO
.
setCreateTime
(
now
);
mainDO
.
setCreateUserCode
(
operId
);
// mainDO.setUpdateBy(operId);
// mainDO.setUpdateTime(now);
// mainDO.setUpdateUserCode(operId);
owners
.
setOwnerName
(
mainDO
.
getOwnerName
());
List
<
Owners
>
olist
=
ownersMapper
.
selectOwnersList
(
owners
);
mainDO
.
setId
(
UUID
.
randomUUID
().
toString
());
mainDO
.
setOrderId
(
orderId
);
mainDO
.
setOrderTypeId
(
orderType
+
""
);
mainDO
.
setOwnerId
(
olist
.
get
(
0
).
getId
());
mainDO
.
setOrderTypeId
(
Optional
.
ofNullable
(
orderType
).
map
(
String:
:
valueOf
).
orElse
(
""
));
// 设置默认值
if
(
mainDO
.
getSortNo
()
==
null
)
{
mainDO
.
setSortNo
(
0L
);
...
...
@@ -256,6 +273,17 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
if
(
mainDO
.
getOrderStatus
()
==
null
)
{
mainDO
.
setOrderStatus
(
1L
);
// 默认草稿状态
}
// ========== 货主查询 ==========
Owners
owners
=
new
Owners
();
owners
.
setOwnerName
(
mainDO
.
getOwnerId
());
List
<
Owners
>
olist
=
ownersMapper
.
selectOwnersList
(
owners
);
if
(
CollectionUtils
.
isEmpty
(
olist
))
{
// 抛业务异常,携带具体单号/条件,方便排查
throw
new
ServiceException
(
"入库单【"
+
orderId
+
"】关联的货主不存在,请检查模板数据!"
);
}
mainDO
.
setOwnerId
(
olist
.
get
(
0
).
getId
());
// 插入主表
inboundOrdersMapper
.
insertInboundOrders
(
mainDO
);
totalMainSuccess
++;
...
...
@@ -264,42 +292,69 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
// 4.2 处理子表明细(每条VO对应一条明细)
for
(
InboundTemplateVO
vo
:
voList
)
{
try
{
InboundOrderItems
itemDO
=
new
InboundOrderItems
();
// 复制子表字段(物料相关)
BeanUtils
.
copyProperties
(
vo
,
itemDO
,
"orderId"
,
"systemNo"
,
"orderTypeId"
,
"batchId"
);
// 排除主表字段
// 填充明细必填字段
itemDO
.
setId
(
UUID
.
randomUUID
().
toString
());
itemDO
.
setCreateBy
(
operId
);
itemDO
.
setCreateTime
(
now
);
itemDO
.
setCreateUserCode
(
operId
);
List
<
Warehouses
>
wlist
=
warehousesMapper
.
selectWarehousesList
(
warehouses
);
List
<
StorageLocations
>
slist
=
storageLocationsMapper
.
selectStorageLocationsList
(
storageLocations
);
itemDO
.
setId
(
UUID
.
randomUUID
().
toString
());
Map
<
String
,
String
>
sapAndId
=
sapAndIdMap
.
get
(
vo
.
getSapNo
());
itemDO
.
setMaterialId
(
sapAndId
.
get
(
"id"
));
itemDO
.
setOrderId
(
orderId
);
// 关联入库单号
itemDO
.
setBatchId
(
mainDO
.
getBatchId
());
itemDO
.
setInboundOrderId
(
mainDO
.
getId
());
// 关联主表ID(核心!)
itemDO
.
setWarehouseId
(
wlist
.
get
(
0
).
getId
());
itemDO
.
setLocationId
(
slist
.
get
(
0
).
getId
());
itemDO
.
setSortNo
(
0L
);
// 校验物料字段(示例:必填sapNo)
if
(
StringUtils
.
isBlank
(
vo
.
getSapNo
()))
{
throw
new
ServiceException
(
String
.
format
(
"入库单号【%s】的物料SAP号为空,明细导入失败"
,
orderId
));
itemDO
.
setRemark
(
vo
.
getRemark2
());
// ========== 物料SAPNO校验 ==========
String
sapNo
=
Optional
.
ofNullable
(
vo
.
getSapNo
()).
map
(
String:
:
trim
).
orElse
(
""
);
if
(
StringUtils
.
isBlank
(
sapNo
))
{
throw
new
ServiceException
(
"物料SAP号为空"
);
}
Map
<
String
,
String
>
sapAndId
=
sapAndIdMap
.
get
(
sapNo
);
if
(
CollectionUtils
.
isEmpty
(
sapAndId
)
||
StringUtils
.
isBlank
(
sapAndId
.
get
(
"id"
)))
{
throw
new
ServiceException
(
"物料SAP号【"
+
sapNo
+
"】不存在"
);
}
itemDO
.
setMaterialId
(
sapAndId
.
get
(
"id"
));
// ========== 仓库/库位查询 ==========
String
warehouseName
=
Optional
.
ofNullable
(
itemDO
.
getWarehouseId
()).
map
(
String:
:
trim
).
orElse
(
""
);
String
warehouseId
=
warehouseNameIdMap
.
get
(
warehouseName
);
if
(
StringUtils
.
isBlank
(
warehouseId
))
{
throw
new
ServiceException
(
"仓库【"
+
warehouseName
+
"】不存在"
);
}
itemDO
.
setWarehouseId
(
warehouseId
);
String
locationName
=
Optional
.
ofNullable
(
itemDO
.
getLocationId
()).
map
(
String:
:
trim
).
orElse
(
""
);
String
locationId
=
storageLocationNameIdMap
.
get
(
locationName
);
if
(
StringUtils
.
isBlank
(
locationId
))
{
throw
new
ServiceException
(
"库位【"
+
locationName
+
"】不存在"
);
}
itemDO
.
setLocationId
(
locationId
);
itemDOList
.
add
(
itemDO
);
}
catch
(
Exception
e
)
{
// 单个明细失败:仅统计,不影响整单
totalItemFailure
++;
failureMsg
.
append
(
String
.
format
(
"入库单号【%s】的物料明细【%s】处理失败:%s;\n"
,
orderId
,
vo
.
getSapNo
(),
e
.
getMessage
()));
log
.
error
(
"导入明细失败-入库单【{}】-SAP【{}】"
,
orderId
,
vo
.
getSapNo
(),
e
);
}
}
// 4.3 批量插入明细
if
(!
CollectionUtils
.
isEmpty
(
itemDOList
))
{
int
itemSuccess
=
inboundOrdersMapper
.
batchInboundOrderItems
(
itemDOList
);
if
(
itemSuccess
!=
itemDOList
.
size
())
{
// 批量插入部分失败,主动回滚事务
TransactionAspectSupport
.
currentTransactionStatus
().
setRollbackOnly
();
throw
new
ServiceException
(
"明细批量插入失败,成功"
+
itemSuccess
+
"条,总"
+
itemDOList
.
size
()
+
"条"
);
}
totalItemSuccess
+=
itemSuccess
;
totalItemFailure
+=
(
itemDOList
.
size
()
-
itemSuccess
);
successMsg
.
append
(
String
.
format
(
"入库单号【%s】成功导入%d条物料明细;\n"
,
orderId
,
itemSuccess
));
if
(
itemDOList
.
size
()
-
itemSuccess
>
0
)
{
failureMsg
.
append
(
String
.
format
(
"入库单号【%s】有%d条物料明细导入失败;\n"
,
orderId
,
itemDOList
.
size
()
-
itemSuccess
));
}
}
}
catch
(
Exception
e
)
{
...
...
@@ -309,27 +364,63 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
failureMsg
.
append
(
String
.
format
(
"入库单号【%s】处理失败:%s;\n"
,
orderId
,
e
.
getMessage
()));
// 打印异常栈,方便调试
log
.
error
(
"导入入库单【{}】失败"
,
orderId
,
e
);
// 整单失败回滚
TransactionAspectSupport
.
currentTransactionStatus
().
setRollbackOnly
();
}
}
// 5. 结果汇总
if
(
totalMainFailure
>
0
||
totalItemFailure
>
0
)
{
// 有失败数据,抛出异常提示
String
final
Failure
Msg
=
String
.
format
(
String
finalMsg
=
String
.
format
(
"导入结果:成功新增/更新%d个入库单,失败%d个;成功导入%d条明细,失败%d条。失败详情:%s"
,
totalMainSuccess
,
totalMainFailure
,
totalItemSuccess
,
totalItemFailure
,
failureMsg
.
toString
()
totalMainSuccess
,
totalMainFailure
,
totalItemSuccess
,
totalItemFailure
,
failureMsg
);
throw
new
ServiceException
(
final
Failure
Msg
);
throw
new
ServiceException
(
finalMsg
);
}
else
{
// 全部成功
String
finalSuccessMsg
=
String
.
format
(
"
恭喜您,数据已全部导入成功!共处理%d个入库单,成功导入%d条物料明细。详情:%s
"
,
totalMainSuccess
,
totalItemSuccess
,
successMsg
.
toString
()
return
String
.
format
(
"
导入成功!共处理%d个入库单,成功导入%d条物料明细。
"
,
totalMainSuccess
,
totalItemSuccess
);
return
finalSuccessMsg
;
}
}
// ========== 辅助方法:预加载仓库名称-ID映射 ==========
private
Map
<
String
,
String
>
loadWarehouseNameIdMap
()
{
Warehouses
query
=
new
Warehouses
();
query
.
setIsUsed
(
1L
);
// 只查可用仓库
List
<
Warehouses
>
warehouseList
=
warehousesMapper
.
selectWarehousesList
(
query
);
if
(
CollectionUtils
.
isEmpty
(
warehouseList
))
{
return
Collections
.
emptyMap
();
}
Map
<
String
,
String
>
nameIdMap
=
new
HashMap
<>();
for
(
Warehouses
warehouse
:
warehouseList
)
{
String
name
=
Optional
.
ofNullable
(
warehouse
.
getWarehousesName
()).
map
(
String:
:
trim
).
orElse
(
""
);
if
(
StringUtils
.
isNotBlank
(
name
))
{
nameIdMap
.
put
(
name
,
warehouse
.
getId
());
}
}
return
Collections
.
unmodifiableMap
(
nameIdMap
);
// 不可变Map,防止篡改
}
// ========== 辅助方法:预加载库位名称-ID映射 ==========
private
Map
<
String
,
String
>
loadStorageLocationNameIdMap
()
{
StorageLocations
query
=
new
StorageLocations
();
query
.
setIsUsed
(
1L
);
// 只查可用库位
List
<
StorageLocations
>
locationList
=
storageLocationsMapper
.
selectStorageLocationsList
(
query
);
if
(
CollectionUtils
.
isEmpty
(
locationList
))
{
return
Collections
.
emptyMap
();
}
Map
<
String
,
String
>
nameIdMap
=
new
HashMap
<>();
for
(
StorageLocations
location
:
locationList
)
{
String
name
=
Optional
.
ofNullable
(
location
.
getLocationName
()).
map
(
String:
:
trim
).
orElse
(
""
);
if
(
StringUtils
.
isNotBlank
(
name
))
{
nameIdMap
.
put
(
name
,
location
.
getId
());
}
}
return
Collections
.
unmodifiableMap
(
nameIdMap
);
}
/**
* 统计本月入库数量
*
...
...
ruoyi-inventory/src/main/resources/mapper/inventory/InboundOrderItemsMapper.xml
View file @
92fcc23b
...
...
@@ -401,6 +401,6 @@
ioi.unit_price,
ioi.remark,
io.inbound_date
order by ioi.order_id asc,ioi.material_id asc
order by i
nbound_date desc,i
oi.order_id asc,ioi.material_id asc
</select>
</mapper>
\ No newline at end of file
ruoyi-inventory/src/main/resources/mapper/inventory/InboundOrdersMapper.xml
View file @
92fcc23b
...
...
@@ -93,6 +93,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if
test=
"createUserCode != null and createUserCode != ''"
>
and create_user_code = #{createUserCode}
</if>
<if
test=
"updateUserCode != null and updateUserCode != ''"
>
and update_user_code = #{updateUserCode}
</if>
</where>
order by inbound_date desc,create_time desc
</select>
<select
id=
"selectInboundOrdersById"
parameterType=
"String"
resultMap=
"InboundOrdersInboundOrderItemsResult"
>
...
...
ruoyi-inventory/src/main/resources/mapper/inventory/InventoryMapper.xml
View file @
92fcc23b
...
...
@@ -459,7 +459,8 @@ and inventory_status = '1'
<if
test=
"createUserCode != null"
>
create_user_code,
</if>
<if
test=
"updateTime != null"
>
update_time,
</if>
<if
test=
"updateUserCode != null"
>
update_user_code,
</if>
<if
test=
"warehousesId != null"
>
warehouses_id,
</if>
<if
test=
"warehousesId != null"
>
warehouses_id,
</if>
<if
test=
"unitPrice != null"
>
unit_price,
</if>
</trim>
<trim
prefix=
"values ("
suffix=
")"
suffixOverrides=
","
>
<if
test=
"id != null"
>
#{id},
</if>
...
...
@@ -486,6 +487,7 @@ and inventory_status = '1'
<if
test=
"updateTime != null"
>
#{updateTime},
</if>
<if
test=
"updateUserCode != null"
>
#{updateUserCode},
</if>
<if
test=
"warehousesId != null"
>
#{warehousesId},
</if>
<if
test=
"unitPrice != null"
>
#{unitPrice},
</if>
</trim>
</insert>
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论