feat(inventory): 添加库存锁定检查功能

- 在 ApiResult 中新增 errorWithExtras 方法用于返回额外信息
- 新增 InventoryLockVO 数据传输对象
- 在 IWmsInventoryService 中添加 getLockList 方法查询锁定库存列表
- 修改 IWmsReturnRequestService 的 addPurchaseReturns 方法返回库存锁定信息
- 在多个控制器中添加 Redisson 分布式锁确保库存操作一致性
- 实现库存不足时返回锁定详情的功能
- 在数据库映射中添加 getLockList 查询方法
- 更新 STATE 枚举添加 OutOfStock 状态码 120
- 在多个出库和调拨流程中集成库存锁定检查逻辑
This commit is contained in:
曹鹏飞 2026-03-23 16:12:50 +08:00
parent a021013ef2
commit 78dad56dbe
17 changed files with 389 additions and 90 deletions

View File

@ -15,6 +15,8 @@ import com.nflg.wms.admin.util.PdfGeneratorUtil;
import com.nflg.wms.admin.util.QRCodeUtil; import com.nflg.wms.admin.util.QRCodeUtil;
import com.nflg.wms.admin.util.ThymeleafUtil; import com.nflg.wms.admin.util.ThymeleafUtil;
import com.nflg.wms.common.constant.BarCodeProcessStage; import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.constant.STATE;
import com.nflg.wms.common.exception.NflgException;
import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.PageData; import com.nflg.wms.common.pojo.PageData;
import com.nflg.wms.common.pojo.document.OutMaterialScanRecord; import com.nflg.wms.common.pojo.document.OutMaterialScanRecord;
@ -32,6 +34,8 @@ import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -39,6 +43,7 @@ import java.math.BigDecimal;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -76,15 +81,15 @@ public class OutAssistanceController extends BaseController {
@Resource @Resource
private InventoryForOutRepository inventoryForOutRepository; private InventoryForOutRepository inventoryForOutRepository;
@Resource
private IParamConfigService paramConfigService;
@Resource @Resource
private IWmsBomService bomService; private IWmsBomService bomService;
@Resource @Resource
private IWmsQrCodeMasterService qrCodeMasterService; private IWmsQrCodeMasterService qrCodeMasterService;
@Resource
private RedissonClient redissonClient;
/** /**
* 查询SAP领料订单数据 * 查询SAP领料订单数据
*/ */
@ -328,6 +333,34 @@ public class OutAssistanceController extends BaseController {
}); });
} }
}); });
List<InventoryLockVO> lockVOS = new ArrayList<>();
records.stream()
.collect(Collectors.groupingBy(OutMaterialScanRecord::getKey10))
.forEach((key, vs) -> {
OutMaterialScanRecord info = vs.get(0);
BigDecimal lockNum = vs.stream().map(OutMaterialScanRecord::getNum).reduce(BigDecimal.ZERO, BigDecimal::add);
RLock lock = redissonClient.getLock(StrUtil.format("lock:inventory:{}:{}:{}", info.getFactoryNo(), info.getWarehouseNo(), info.getMaterialNo()));
try {
// 等待5秒获取锁10秒后自动释放
if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
if (inventoryService.getNumOne(info.getFactoryNo(), info.getWarehouseNo(), info.getMaterialNo()).compareTo(lockNum) < 0) {
lockVOS.addAll(inventoryService.getLockList(info.getFactoryNo(), info.getWarehouseNo(), info.getMaterialNo()));
}
} else {
throw new NflgException(STATE.BusinessError, "获取锁失败");
}
} catch (Exception e) {
log.error("保存生产领料单出错", e);
throw new NflgException(STATE.BusinessError, e.getMessage());
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
});
if (CollectionUtil.isNotEmpty(lockVOS)) {
return ApiResult.errorWithExtras(STATE.OutOfStock, lockVOS);
}
outAssistanceItemService.updateBatchById(datas); outAssistanceItemService.updateBatchById(datas);
outAssistanceTicketItemService.saveBatch(ticketItems); outAssistanceTicketItemService.saveBatch(ticketItems);
outAssistanceTicketService.save(ticket); outAssistanceTicketService.save(ticket);
@ -468,38 +501,37 @@ public class OutAssistanceController extends BaseController {
.in(WmsQrCodeMaster::getBarcodeCode, records.stream().map(OutMaterialScanRecord::getUniqNo).toList()) .in(WmsQrCodeMaster::getBarcodeCode, records.stream().map(OutMaterialScanRecord::getUniqNo).toList())
.list(); .list();
qrCodeMasters.forEach(p -> { qrCodeMasters.forEach(p -> {
OutMaterialScanRecord record = records.stream() OutMaterialScanRecord record = records.stream()
.filter(r -> r.getUniqNo().equals(p.getBarcodeCode())) .filter(r -> r.getUniqNo().equals(p.getBarcodeCode()))
.findFirst() .findFirst()
.get(); .get();
p.setFactoryCode(record.getFactoryNo()); p.setFactoryCode(record.getFactoryNo());
p.setStorageLocation(record.getWarehouseNo()); p.setStorageLocation(record.getWarehouseNo());
p.setBinLocation(record.getBinNo()); p.setBinLocation(record.getBinNo());
p.setProcessStage(BarCodeProcessStage.OutBound.getState()); p.setProcessStage(BarCodeProcessStage.OutBound.getState());
p.setLastScanBy(UserUtil.getUserId()); p.setLastScanBy(UserUtil.getUserId());
p.setLastScanByname(UserUtil.getUserName()); p.setLastScanByname(UserUtil.getUserName());
p.setLastScanTime(LocalDateTime.now()); p.setLastScanTime(LocalDateTime.now());
if (p.getPackagingType() == 1) { if (p.getPackagingType() == 1) {
List<WmsQrCodeMaster> children = qrCodeMasterService.lambdaQuery() List<WmsQrCodeMaster> children = qrCodeMasterService.lambdaQuery()
.eq(WmsQrCodeMaster::getParentBarcodeId, p.getId()) .eq(WmsQrCodeMaster::getParentBarcodeId, p.getId())
.list(); .list();
if (CollectionUtil.isNotEmpty(children)) { if (CollectionUtil.isNotEmpty(children)) {
children.forEach(c -> { children.forEach(c -> {
c.setProcessStage(p.getProcessStage()); c.setProcessStage(p.getProcessStage());
c.setFactoryCode(p.getFactoryCode()); c.setFactoryCode(p.getFactoryCode());
c.setStorageLocation(p.getStorageLocation()); c.setStorageLocation(p.getStorageLocation());
c.setBinLocation(p.getBinLocation()); c.setBinLocation(p.getBinLocation());
c.setProcessStage(p.getProcessStage()); c.setProcessStage(p.getProcessStage());
c.setLastScanBy(p.getLastScanBy()); c.setLastScanBy(p.getLastScanBy());
c.setLastScanByname(p.getLastScanByname()); c.setLastScanByname(p.getLastScanByname());
c.setLastScanTime(p.getLastScanTime()); c.setLastScanTime(p.getLastScanTime());
}); });
} }
} }
}); });
qrCodeMasterService.updateBarCode(qrCodeMasters); qrCodeMasterService.updateBarCode(qrCodeMasters);
submitSap(order, ticket, records, outAssistanceItemService.getList(order.getId()), qrCodeMasters submitSap(order, ticket, records, outAssistanceItemService.getList(order.getId()), qrCodeMasters);
);
} else { } else {
List<OutAssistanceItemVO> items = outAssistanceTicketItemService.getList(qo.getId()); List<OutAssistanceItemVO> items = outAssistanceTicketItemService.getList(qo.getId());
items.forEach(it -> { items.forEach(it -> {

View File

@ -15,6 +15,8 @@ import com.nflg.wms.admin.util.PdfGeneratorUtil;
import com.nflg.wms.admin.util.QRCodeUtil; import com.nflg.wms.admin.util.QRCodeUtil;
import com.nflg.wms.admin.util.ThymeleafUtil; import com.nflg.wms.admin.util.ThymeleafUtil;
import com.nflg.wms.common.constant.BarCodeProcessStage; import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.constant.STATE;
import com.nflg.wms.common.exception.NflgException;
import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.PageData; import com.nflg.wms.common.pojo.PageData;
import com.nflg.wms.common.pojo.document.OutMaterialScanRecord; import com.nflg.wms.common.pojo.document.OutMaterialScanRecord;
@ -32,6 +34,8 @@ import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -39,6 +43,7 @@ import java.math.BigDecimal;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -85,6 +90,9 @@ public class OutCostCenterController extends BaseController {
@Resource @Resource
private IWmsQrCodeMasterService qrCodeMasterService; private IWmsQrCodeMasterService qrCodeMasterService;
@Resource
private RedissonClient redissonClient;
/** /**
* 查询SAP领料订单数据 * 查询SAP领料订单数据
*/ */
@ -319,6 +327,34 @@ public class OutCostCenterController extends BaseController {
}); });
} }
}); });
List<InventoryLockVO> lockVOS = new ArrayList<>();
records.stream()
.collect(Collectors.groupingBy(OutMaterialScanRecord::getKey10))
.forEach((key, vs) -> {
OutMaterialScanRecord info = vs.get(0);
BigDecimal lockNum = vs.stream().map(OutMaterialScanRecord::getNum).reduce(BigDecimal.ZERO, BigDecimal::add);
RLock lock = redissonClient.getLock(StrUtil.format("lock:inventory:{}:{}:{}", info.getFactoryNo(), info.getWarehouseNo(), info.getMaterialNo()));
try {
// 等待5秒获取锁10秒后自动释放
if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
if (inventoryService.getNumOne(info.getFactoryNo(), info.getWarehouseNo(), info.getMaterialNo()).compareTo(lockNum) < 0) {
lockVOS.addAll(inventoryService.getLockList(info.getFactoryNo(), info.getWarehouseNo(), info.getMaterialNo()));
}
} else {
throw new NflgException(STATE.BusinessError, "获取锁失败");
}
} catch (Exception e) {
log.error("保存生产领料单出错", e);
throw new NflgException(STATE.BusinessError, e.getMessage());
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
});
if (CollectionUtil.isNotEmpty(lockVOS)) {
return ApiResult.errorWithExtras(STATE.OutOfStock, lockVOS);
}
outCostcenterItemService.updateBatchById(datas); outCostcenterItemService.updateBatchById(datas);
outCostcenterTicketItemService.saveBatch(ticketItems); outCostcenterTicketItemService.saveBatch(ticketItems);
outCostcenterTicketService.save(ticket); outCostcenterTicketService.save(ticket);

View File

@ -143,6 +143,7 @@ public class OutProduceController extends BaseController {
VUtil.trueThrowBusinessError(CollectionUtil.isNotEmpty(materialNos)) VUtil.trueThrowBusinessError(CollectionUtil.isNotEmpty(materialNos))
.throwMessage("以下物料的申请数量超出限制:" + StrUtil.join(",", materialNos)); .throwMessage("以下物料的申请数量超出限制:" + StrUtil.join(",", materialNos));
Map<String, List<Zwm3a07VO>> maps = datas.stream().collect(Collectors.groupingBy(Zwm3a07VO::getKey2)); Map<String, List<Zwm3a07VO>> maps = datas.stream().collect(Collectors.groupingBy(Zwm3a07VO::getKey2));
List<InventoryLockVO> lockVOS = new ArrayList<>();
maps.forEach((key, items) -> { maps.forEach((key, items) -> {
WmsOutProduce order = Convert.convert(WmsOutProduce.class, items.get(0)); WmsOutProduce order = Convert.convert(WmsOutProduce.class, items.get(0));
order.setNo(serialNumberControllerService.generateSerialNumber(9)); order.setNo(serialNumberControllerService.generateSerialNumber(9));
@ -155,10 +156,16 @@ public class OutProduceController extends BaseController {
try { try {
// 等待5秒获取锁10秒后自动释放 // 等待5秒获取锁10秒后自动释放
if (lock.tryLock(5, 10, TimeUnit.SECONDS)) { if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
WmsOutProduceItem data = Convert.convert(WmsOutProduceItem.class, item); if (inventoryService.getNumOne(item.getDwerk(), item.getLgort2(), item.getMatnr())
data.setLockNum(item.getSqsl()); .compareTo(item.getSqsl()) < 0
data.setOrderId(order.getId()); ) {
outProduceItemService.save(data); lockVOS.addAll(inventoryService.getLockList(item.getDwerk(), item.getLgort2(), item.getMatnr()));
} else {
WmsOutProduceItem data = Convert.convert(WmsOutProduceItem.class, item);
data.setLockNum(item.getSqsl());
data.setOrderId(order.getId());
outProduceItemService.save(data);
}
} else { } else {
throw new NflgException(STATE.BusinessError, "获取锁失败"); throw new NflgException(STATE.BusinessError, "获取锁失败");
} }
@ -166,14 +173,17 @@ public class OutProduceController extends BaseController {
log.error("保存生产领料单出错", e); log.error("保存生产领料单出错", e);
throw new NflgException(STATE.BusinessError, e.getMessage()); throw new NflgException(STATE.BusinessError, e.getMessage());
} finally { } finally {
// 确保只释放自己持有的锁
if (lock.isHeldByCurrentThread()) { if (lock.isHeldByCurrentThread()) {
lock.unlock(); lock.unlock();
} }
} }
}); });
}); });
return ApiResult.success(); if (CollectionUtil.isEmpty(lockVOS)) {
return ApiResult.success();
} else {
return ApiResult.errorWithExtras(STATE.OutOfStock, lockVOS);
}
} }
/** /**
@ -192,6 +202,7 @@ public class OutProduceController extends BaseController {
// VUtil.trueThrowBusinessError(CollectionUtil.isNotEmpty(materialNos)) // VUtil.trueThrowBusinessError(CollectionUtil.isNotEmpty(materialNos))
// .throwMessage("以下物料的申请数量超出限制:" + StrUtil.join(",", materialNos)); // .throwMessage("以下物料的申请数量超出限制:" + StrUtil.join(",", materialNos));
Map<String, List<Zwm3a07VO>> maps = datas.stream().collect(Collectors.groupingBy(Zwm3a07VO::getKey2)); Map<String, List<Zwm3a07VO>> maps = datas.stream().collect(Collectors.groupingBy(Zwm3a07VO::getKey2));
List<InventoryLockVO> lockVOS = new ArrayList<>();
maps.forEach((key, items) -> { maps.forEach((key, items) -> {
WmsOutProduce order = Convert.convert(WmsOutProduce.class, items.get(0)); WmsOutProduce order = Convert.convert(WmsOutProduce.class, items.get(0));
order.setNo(serialNumberControllerService.generateSerialNumber(9)); order.setNo(serialNumberControllerService.generateSerialNumber(9));
@ -200,13 +211,39 @@ public class OutProduceController extends BaseController {
order.setCreateTime(LocalDateTime.now()); order.setCreateTime(LocalDateTime.now());
outProduceService.save(order); outProduceService.save(order);
items.forEach(item -> { items.forEach(item -> {
WmsOutProduceItem data = Convert.convert(WmsOutProduceItem.class, item); RLock lock = redissonClient.getLock(StrUtil.format("lock:inventory:{}:{}:{}", item.getDwerk(), item.getLgort2(), item.getMatnr()));
data.setLockNum(BigDecimal.ZERO); try {
data.setOrderId(order.getId()); // 等待5秒获取锁10秒后自动释放
outProduceItemService.save(data); if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
if (inventoryService.getNumOne(item.getDwerk(), item.getLgort2(), item.getMatnr())
.compareTo(item.getSqsl()) < 0
) {
lockVOS.addAll(inventoryService.getLockList(item.getDwerk(), item.getLgort2(), item.getMatnr()));
} else {
WmsOutProduceItem data = Convert.convert(WmsOutProduceItem.class, item);
data.setLockNum(data.getSqsl());
data.setOrderId(order.getId());
outProduceItemService.save(data);
}
} else {
throw new NflgException(STATE.BusinessError, "获取锁失败");
}
} catch (Exception e) {
log.error("保存生产领料单出错", e);
throw new NflgException(STATE.BusinessError, e.getMessage());
} finally {
// 确保只释放自己持有的锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}); });
}); });
return ApiResult.success(); if (CollectionUtil.isEmpty(lockVOS)) {
return ApiResult.success();
} else {
return ApiResult.errorWithExtras(STATE.OutOfStock, lockVOS);
}
} }
/** /**

View File

@ -14,6 +14,7 @@ import com.nflg.wms.admin.util.PdfGeneratorUtil;
import com.nflg.wms.admin.util.QRCodeUtil; import com.nflg.wms.admin.util.QRCodeUtil;
import com.nflg.wms.admin.util.ThymeleafUtil; import com.nflg.wms.admin.util.ThymeleafUtil;
import com.nflg.wms.common.constant.BarCodeProcessStage; import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.constant.STATE;
import com.nflg.wms.common.constant.UserType; import com.nflg.wms.common.constant.UserType;
import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.PageData; import com.nflg.wms.common.pojo.PageData;
@ -127,8 +128,12 @@ public class PurchaseReturnController extends BaseController {
VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(request)) VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(request))
.throwMessage("申请数据不可以为空"); .throwMessage("申请数据不可以为空");
List<PurchaseReturnDTO> purchaseReturnDTOList = Convert.toList(PurchaseReturnDTO.class, request); List<PurchaseReturnDTO> purchaseReturnDTOList = Convert.toList(PurchaseReturnDTO.class, request);
SaveApply(purchaseReturnDTOList, Short.valueOf("0")); List<InventoryLockVO> inventoryLockVOS = SaveApply(purchaseReturnDTOList, Short.valueOf("0"));
return ApiResult.success(); if (CollectionUtil.isEmpty(inventoryLockVOS)) {
return ApiResult.success();
} else {
return ApiResult.errorWithExtras(STATE.OutOfStock, inventoryLockVOS);
}
} }
/** /**
@ -171,7 +176,7 @@ public class PurchaseReturnController extends BaseController {
.throwMessage("无效申请单"); .throwMessage("无效申请单");
User user = userService.getById(UserUtil.getUserId()); User user = userService.getById(UserUtil.getUserId());
VUtil.trueThrowBusinessError(StrUtil.equals(user.getPurchasingGroup(), returnRequest.getPurchaseGroup())) VUtil.trueThrowBusinessError(StrUtil.equals(user.getPurchasingGroup(), returnRequest.getPurchaseGroup()))
.throwMessage("无权限审核此单"); .throwMessage("无权限审核此单");
// VUtil.trueThrowBusinessError(returnRequest.getApprovalStatus() == 1) // VUtil.trueThrowBusinessError(returnRequest.getApprovalStatus() == 1)
// .throwMessage("此单已审核通过,不可以再此审核"); // .throwMessage("此单已审核通过,不可以再此审核");
@ -285,7 +290,7 @@ public class PurchaseReturnController extends BaseController {
* 保存申请单 * 保存申请单
* @return * @return
*/ */
private void SaveApply(List<PurchaseReturnDTO> purchaseReturnDTOList, Short dataSource) { private List<InventoryLockVO> SaveApply(List<PurchaseReturnDTO> purchaseReturnDTOList, Short dataSource) {
//根据采购组编号采购单编号供应商id进行去除 //根据采购组编号采购单编号供应商id进行去除
List<String> disGroupCode = purchaseReturnDTOList.stream() List<String> disGroupCode = purchaseReturnDTOList.stream()
.map(PurchaseReturnDTO::getGroupCode) .map(PurchaseReturnDTO::getGroupCode)
@ -334,7 +339,7 @@ public class PurchaseReturnController extends BaseController {
returnRequestItems.add(returnRequestItem); returnRequestItems.add(returnRequestItem);
} }
} }
returnRequestService.addPurchaseReturns(returnRequests, returnRequestItems); return returnRequestService.addPurchaseReturns(returnRequests, returnRequestItems);
} }
/** /**
@ -372,6 +377,7 @@ public class PurchaseReturnController extends BaseController {
for (WmsQrCodeMaster qrCodeMaster : qrCodeMasters) { for (WmsQrCodeMaster qrCodeMaster : qrCodeMasters) {
qrCodeVOS.add(BarcodeValidation(qrCodeMaster)); qrCodeVOS.add(BarcodeValidation(qrCodeMaster));
} }
List<InventoryLockVO> inventoryLockVOS = null;
if (CollectionUtil.isNotEmpty(qrCodeVOS)) { if (CollectionUtil.isNotEmpty(qrCodeVOS)) {
List<PurchaseReturnDTO> purchaseReturnDTOList = new ArrayList<>(); List<PurchaseReturnDTO> purchaseReturnDTOList = new ArrayList<>();
for (QrCodeVO qrCodeVO : qrCodeVOS) { for (QrCodeVO qrCodeVO : qrCodeVOS) {
@ -430,10 +436,14 @@ public class PurchaseReturnController extends BaseController {
); );
purchaseReturnDTO.setGroupCode(groupCode); purchaseReturnDTO.setGroupCode(groupCode);
} }
SaveApply(purchaseReturnDTOList, Short.valueOf("1")); inventoryLockVOS = SaveApply(purchaseReturnDTOList, Short.valueOf("1"));
} }
} }
return ApiResult.success(); if (CollectionUtil.isEmpty(inventoryLockVOS)) {
return ApiResult.success();
} else {
return ApiResult.errorWithExtras(STATE.OutOfStock, inventoryLockVOS);
}
} }
@ -463,8 +473,8 @@ public class PurchaseReturnController extends BaseController {
public void exportTicket(HttpServletResponse response, @Valid @RequestParam @NotNull Long id) throws Exception { public void exportTicket(HttpServletResponse response, @Valid @RequestParam @NotNull Long id) throws Exception {
WmsReturnRequest order = returnRequestService.getById(id); WmsReturnRequest order = returnRequestService.getById(id);
VUtil.trueThrowBusinessError(Objects.isNull(order)).throwMessage("申请单不存在"); VUtil.trueThrowBusinessError(Objects.isNull(order)).throwMessage("申请单不存在");
VUtil.trueThrowBusinessError(order.getApprovalStatus()!=1).throwMessage("申请单未审核通过"); VUtil.trueThrowBusinessError(order.getApprovalStatus() != 1).throwMessage("申请单未审核通过");
UserSupplier supplier=userSupplierService.getByCode(order.getSupplierCode()); UserSupplier supplier = userSupplierService.getByCode(order.getSupplierCode());
List<WmsReturnRequestItem> list = returnRequestItemService.lambdaQuery() List<WmsReturnRequestItem> list = returnRequestItemService.lambdaQuery()
.eq(WmsReturnRequestItem::getApplicationId, id) .eq(WmsReturnRequestItem::getApplicationId, id)
.list(); .list();

View File

@ -17,6 +17,8 @@ import com.nflg.wms.admin.service.BasdeSerialNumberControllerService;
import com.nflg.wms.admin.service.SapService; import com.nflg.wms.admin.service.SapService;
import com.nflg.wms.admin.util.*; import com.nflg.wms.admin.util.*;
import com.nflg.wms.common.constant.BarCodeProcessStage; import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.constant.STATE;
import com.nflg.wms.common.exception.NflgException;
import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.PageData; import com.nflg.wms.common.pojo.PageData;
import com.nflg.wms.common.pojo.document.InMaterialScanRecord; import com.nflg.wms.common.pojo.document.InMaterialScanRecord;
@ -37,6 +39,8 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -44,6 +48,7 @@ import java.math.BigDecimal;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -87,9 +92,6 @@ public class TransferCompanyController extends BaseController {
@Resource @Resource
private InventoryForOutRepository inventoryForOutRepository; private InventoryForOutRepository inventoryForOutRepository;
@Resource
private IParamConfigService paramConfigService;
@Resource @Resource
private IWmsWarehouseService warehouseService; private IWmsWarehouseService warehouseService;
@ -99,6 +101,9 @@ public class TransferCompanyController extends BaseController {
@Resource @Resource
private IWmsQrCodeMasterService qrCodeMasterService; private IWmsQrCodeMasterService qrCodeMasterService;
@Resource
private RedissonClient redissonClient;
/** /**
* 查询SAP订单数据 * 查询SAP订单数据
*/ */
@ -124,6 +129,7 @@ public class TransferCompanyController extends BaseController {
VUtil.trueThrowBusinessError(CollectionUtil.isNotEmpty(materialNos)) VUtil.trueThrowBusinessError(CollectionUtil.isNotEmpty(materialNos))
.throwMessage("以下物料的申请数量超出限制:" + StrUtil.join(",", materialNos)); .throwMessage("以下物料的申请数量超出限制:" + StrUtil.join(",", materialNos));
Map<String, List<AllocationOrderDTO>> maps = datas.stream().collect(Collectors.groupingBy(AllocationOrderDTO::getGroup1)); Map<String, List<AllocationOrderDTO>> maps = datas.stream().collect(Collectors.groupingBy(AllocationOrderDTO::getGroup1));
List<InventoryLockVO> lockVOS = new ArrayList<>();
maps.forEach((key, items) -> { maps.forEach((key, items) -> {
WmsTransferCompany order = Convert.convert(WmsTransferCompany.class, items.get(0)); WmsTransferCompany order = Convert.convert(WmsTransferCompany.class, items.get(0));
order.setNo(serialNumberControllerService.generateSerialNumber(22)); order.setNo(serialNumberControllerService.generateSerialNumber(22));
@ -131,16 +137,38 @@ public class TransferCompanyController extends BaseController {
order.setCreateTime(LocalDateTime.now()); order.setCreateTime(LocalDateTime.now());
transferCompanyService.save(order); transferCompanyService.save(order);
items.forEach(item -> { items.forEach(item -> {
BigDecimal max = inventoryService.getNumOne(item.getReswk(), item.getLgfsb1(), item.getMatnr()); RLock lock = redissonClient.getLock(StrUtil.format("lock:inventory:{}:{}:{}", item.getReswk(), item.getLgfsb1(), item.getMatnr()));
VUtil.trueThrowBusinessError(item.getNum().compareTo(max) > 0) try {
.throwMessage("物料" + item.getMatnr() + "的申请数量超出库存限制"); // 等待5秒获取锁10秒后自动释放
WmsTransferCompanyItem data = Convert.convert(WmsTransferCompanyItem.class, item); if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
data.setOrderId(order.getId()); if (inventoryService.getNumOne(item.getReswk(), item.getLgfsb1(), item.getMatnr())
data.setLeft(data.getNum()); .compareTo(item.getNum()) < 0
transferCompanyItemService.save(data); ) {
lockVOS.addAll(inventoryService.getLockList(item.getReswk(), item.getLgfsb1(), item.getMatnr()));
} else {
WmsTransferCompanyItem data = Convert.convert(WmsTransferCompanyItem.class, item);
data.setOrderId(order.getId());
data.setLeft(data.getNum());
transferCompanyItemService.save(data);
}
} else {
throw new NflgException(STATE.BusinessError, "获取锁失败");
}
} catch (Exception e) {
log.error("保存生产领料单出错", e);
throw new NflgException(STATE.BusinessError, e.getMessage());
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}); });
}); });
return ApiResult.success(); if (CollectionUtil.isEmpty(lockVOS)) {
return ApiResult.success();
} else {
return ApiResult.errorWithExtras(STATE.OutOfStock, lockVOS);
}
} }
/** /**

View File

@ -15,6 +15,8 @@ import com.nflg.wms.admin.util.PdfGeneratorUtil;
import com.nflg.wms.admin.util.QRCodeUtil; import com.nflg.wms.admin.util.QRCodeUtil;
import com.nflg.wms.admin.util.ThymeleafUtil; import com.nflg.wms.admin.util.ThymeleafUtil;
import com.nflg.wms.common.constant.BarCodeProcessStage; import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.constant.STATE;
import com.nflg.wms.common.exception.NflgException;
import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.PageData; import com.nflg.wms.common.pojo.PageData;
import com.nflg.wms.common.pojo.document.InMaterialScanRecord; import com.nflg.wms.common.pojo.document.InMaterialScanRecord;
@ -35,6 +37,8 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -42,6 +46,7 @@ import java.math.BigDecimal;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -85,9 +90,6 @@ public class TransferFactoryController extends BaseController {
@Resource @Resource
private InventoryForOutRepository inventoryForOutRepository; private InventoryForOutRepository inventoryForOutRepository;
@Resource
private IParamConfigService paramConfigService;
@Resource @Resource
private IWmsBomService bomService; private IWmsBomService bomService;
@ -97,6 +99,9 @@ public class TransferFactoryController extends BaseController {
@Resource @Resource
private IWmsQrCodeMasterService qrCodeMasterService; private IWmsQrCodeMasterService qrCodeMasterService;
@Resource
private RedissonClient redissonClient;
/** /**
* 查询SAP领料订单数据 * 查询SAP领料订单数据
*/ */
@ -122,6 +127,7 @@ public class TransferFactoryController extends BaseController {
VUtil.trueThrowBusinessError(CollectionUtil.isNotEmpty(materialNos)) VUtil.trueThrowBusinessError(CollectionUtil.isNotEmpty(materialNos))
.throwMessage("以下物料的申请数量超出限制:" + StrUtil.join(",", materialNos)); .throwMessage("以下物料的申请数量超出限制:" + StrUtil.join(",", materialNos));
Map<String, List<TransferOrderDTO>> maps = datas.stream().collect(Collectors.groupingBy(TransferOrderDTO::getGroup1)); Map<String, List<TransferOrderDTO>> maps = datas.stream().collect(Collectors.groupingBy(TransferOrderDTO::getGroup1));
List<InventoryLockVO> lockVOS = new ArrayList<>();
maps.forEach((key, items) -> { maps.forEach((key, items) -> {
WmsTransferFactory order = Convert.convert(WmsTransferFactory.class, items.get(0)); WmsTransferFactory order = Convert.convert(WmsTransferFactory.class, items.get(0));
order.setNo(serialNumberControllerService.generateSerialNumber(18)); order.setNo(serialNumberControllerService.generateSerialNumber(18));
@ -129,16 +135,38 @@ public class TransferFactoryController extends BaseController {
order.setCreateTime(LocalDateTime.now()); order.setCreateTime(LocalDateTime.now());
transferFactoryService.save(order); transferFactoryService.save(order);
items.forEach(item -> { items.forEach(item -> {
BigDecimal max = inventoryService.getNumOne(item.getWerks(), item.getLgort(), item.getMatnr()); RLock lock = redissonClient.getLock(StrUtil.format("lock:inventory:{}:{}:{}", item.getWerks(), item.getLgort(), item.getMatnr()));
VUtil.trueThrowBusinessError(item.getNum().compareTo(max) > 0) try {
.throwMessage("物料" + item.getMatnr() + "的申请数量超出库存限制"); // 等待5秒获取锁10秒后自动释放
WmsTransferFactoryItem data = Convert.convert(WmsTransferFactoryItem.class, item); if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
data.setOrderId(order.getId()); if (inventoryService.getNumOne(item.getWerks(), item.getLgort(), item.getMatnr())
data.setLeft(data.getNum()); .compareTo(item.getNum()) < 0
transferFactoryItemService.save(data); ) {
lockVOS.addAll(inventoryService.getLockList(item.getWerks(), item.getLgort(), item.getMatnr()));
} else {
WmsTransferFactoryItem data = Convert.convert(WmsTransferFactoryItem.class, item);
data.setOrderId(order.getId());
data.setLeft(data.getNum());
transferFactoryItemService.save(data);
}
} else {
throw new NflgException(STATE.BusinessError, "获取锁失败");
}
} catch (Exception e) {
log.error("保存生产领料单出错", e);
throw new NflgException(STATE.BusinessError, e.getMessage());
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}); });
}); });
return ApiResult.success(); if (CollectionUtil.isEmpty(lockVOS)) {
return ApiResult.success();
} else {
return ApiResult.errorWithExtras(STATE.OutOfStock, lockVOS);
}
} }
/** /**

View File

@ -26,7 +26,8 @@ public enum STATE {
RequestMethodError(116, "请求方式错误"), RequestMethodError(116, "请求方式错误"),
InconsistentDataError(117, "需要用户确认"), InconsistentDataError(117, "需要用户确认"),
NoOrderData(118, "订单不存在"), NoOrderData(118, "订单不存在"),
SAPErr(119, "SAP错误"); SAPErr(119, "SAP错误"),
OutOfStock(120, "库存不足");
@Getter @Getter
private final Integer state; private final Integer state;

View File

@ -27,16 +27,16 @@ public class ApiResult<T> implements Serializable {
ApiResult<T> vo = new ApiResult<>(); ApiResult<T> vo = new ApiResult<>();
vo.result = value; vo.result = value;
vo.code = STATE.Success.getState(); vo.code = STATE.Success.getState();
vo.type=STATE.Success.getType(); vo.type = STATE.Success.getType();
return vo; return vo;
} }
public static <T> ApiResult<T> error(int state,String msg,T value) { public static <T> ApiResult<T> error(int state, String msg, T value) {
ApiResult<T> vo = new ApiResult<>(); ApiResult<T> vo = new ApiResult<>();
vo.result = value; vo.result = value;
vo.code = state; vo.code = state;
vo.type = msg; vo.type = msg;
vo.message=msg; vo.message = msg;
return vo; return vo;
} }
@ -49,7 +49,15 @@ public class ApiResult<T> implements Serializable {
return vo; return vo;
} }
public static <T> ApiResult<T> error(int state, String type,String msg) { public static <T> ApiResult<T> errorWithExtras(STATE state,Object extras) {
ApiResult<T> vo = new ApiResult<>();
vo.extras = extras;
vo.code = state.getState();
vo.type = state.getType();
return vo;
}
public static <T> ApiResult<T> error(int state, String type, String msg) {
ApiResult<T> vo = new ApiResult<T>(); ApiResult<T> vo = new ApiResult<T>();
vo.code = state; vo.code = state;
vo.type = type; vo.type = type;
@ -63,12 +71,13 @@ public class ApiResult<T> implements Serializable {
vo.type = STATE.Success.getType(); vo.type = STATE.Success.getType();
return vo; return vo;
} }
public static <T> ApiResult<T> error(STATE code, String msg) { public static <T> ApiResult<T> error(STATE code, String msg) {
return error(code.getState(),code.getType(), msg); return error(code.getState(), code.getType(), msg);
} }
public static <T> ApiResult<T> error(String msg) { public static <T> ApiResult<T> error(String msg) {
return error(STATE.Error.getState(),STATE.Error.getType(), msg); return error(STATE.Error.getState(), STATE.Error.getType(), msg);
} }
public static <T> ApiResult<T> error(STATE code) { public static <T> ApiResult<T> error(STATE code) {
@ -92,7 +101,7 @@ public class ApiResult<T> implements Serializable {
return vo; return vo;
} }
public static <T,D> ApiResult<PageData<D>> success(IPage<T> page, Function<T, D> converter) { public static <T, D> ApiResult<PageData<D>> success(IPage<T> page, Function<T, D> converter) {
ApiResult<PageData<D>> vo = new ApiResult<>(); ApiResult<PageData<D>> vo = new ApiResult<>();
PageData<D> pageData = new PageData<>(); PageData<D> pageData = new PageData<>();
pageData.setPage((int) page.getCurrent()); pageData.setPage((int) page.getCurrent());

View File

@ -201,4 +201,11 @@ public class OutMaterialScanRecord {
public String getKey9() { public String getKey9() {
return materialNo + "|" + getBatchNo() + "|" + this.getSerialNo() + "|" + this.getBinNo(); return materialNo + "|" + getBatchNo() + "|" + this.getSerialNo() + "|" + this.getBinNo();
} }
@Transient
private String key10;
public String getKey10() {
return materialNo + "|" + getFactoryNo() + "|" + this.getWarehouseNo();
}
} }

View File

@ -0,0 +1,39 @@
package com.nflg.wms.common.pojo.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class InventoryLockVO {
/**
* 订单类型
*/
private String type;
/**
* 订单编号
*/
private String no;
/**
* 工厂编号
*/
private String factoryNo;
/**
* 仓库编号
*/
private String warehouseNo;
/**
* 物料编号
*/
private String materialNo;
/**
* 锁定数量
*/
private BigDecimal lockNum;
}

View File

@ -56,6 +56,12 @@
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId> <artifactId>spring-security-crypto</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.52.0</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nflg.wms.common.pojo.dto.InventoryInDTO; import com.nflg.wms.common.pojo.dto.InventoryInDTO;
import com.nflg.wms.common.pojo.qo.InventorySearchQO; import com.nflg.wms.common.pojo.qo.InventorySearchQO;
import com.nflg.wms.common.pojo.vo.InventoryLockVO;
import com.nflg.wms.common.pojo.vo.InventoryVO; import com.nflg.wms.common.pojo.vo.InventoryVO;
import com.nflg.wms.repository.entity.WmsInventory; import com.nflg.wms.repository.entity.WmsInventory;
@ -30,4 +31,6 @@ public interface WmsInventoryMapper extends BaseMapper<WmsInventory> {
BigDecimal getNumOne(String factoryNo, String warehouseNo, String materialNo); BigDecimal getNumOne(String factoryNo, String warehouseNo, String materialNo);
BigDecimal getLockedNumOne(String factoryNo, String warehouseNo, String materialNo); BigDecimal getLockedNumOne(String factoryNo, String warehouseNo, String materialNo);
List<InventoryLockVO> getLockList(String factoryNo, String warehouseNo, String materialNo);
} }

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.nflg.wms.common.pojo.dto.InventoryInDTO; import com.nflg.wms.common.pojo.dto.InventoryInDTO;
import com.nflg.wms.common.pojo.dto.InventoryOutDTO; import com.nflg.wms.common.pojo.dto.InventoryOutDTO;
import com.nflg.wms.common.pojo.qo.InventorySearchQO; import com.nflg.wms.common.pojo.qo.InventorySearchQO;
import com.nflg.wms.common.pojo.vo.InventoryLockVO;
import com.nflg.wms.common.pojo.vo.InventoryVO; import com.nflg.wms.common.pojo.vo.InventoryVO;
import com.nflg.wms.repository.entity.WmsInventory; import com.nflg.wms.repository.entity.WmsInventory;
import jakarta.validation.Valid; import jakarta.validation.Valid;
@ -48,4 +49,6 @@ public interface IWmsInventoryService extends IService<WmsInventory> {
BigDecimal getNumOne(String factoryNo, String warehouseNo, String materialNo); BigDecimal getNumOne(String factoryNo, String warehouseNo, String materialNo);
List<WmsInventory> getForOutFIFO(String factoryNo, String warehouseNo, Collection<String> materialNos); List<WmsInventory> getForOutFIFO(String factoryNo, String warehouseNo, Collection<String> materialNos);
List<InventoryLockVO> getLockList(String factoryNo, String warehouseNo, String materialNo);
} }

View File

@ -7,6 +7,7 @@ import com.nflg.wms.common.pojo.qo.ApplyReturnRequestSearchQO;
import com.nflg.wms.common.pojo.qo.GoodsReceiptSearchQO; import com.nflg.wms.common.pojo.qo.GoodsReceiptSearchQO;
import com.nflg.wms.common.pojo.vo.ApplyReturnRequestVO; import com.nflg.wms.common.pojo.vo.ApplyReturnRequestVO;
import com.nflg.wms.common.pojo.vo.GoodsReceiptVO; import com.nflg.wms.common.pojo.vo.GoodsReceiptVO;
import com.nflg.wms.common.pojo.vo.InventoryLockVO;
import com.nflg.wms.repository.entity.WmsReturnRequest; import com.nflg.wms.repository.entity.WmsReturnRequest;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.nflg.wms.repository.entity.WmsReturnRequestItem; import com.nflg.wms.repository.entity.WmsReturnRequestItem;
@ -25,7 +26,7 @@ import java.util.List;
*/ */
public interface IWmsReturnRequestService extends IService<WmsReturnRequest> { public interface IWmsReturnRequestService extends IService<WmsReturnRequest> {
void addPurchaseReturns(List<WmsReturnRequest> returnRequests, List<WmsReturnRequestItem> returnRequestItems); List<InventoryLockVO> addPurchaseReturns(List<WmsReturnRequest> returnRequests, List<WmsReturnRequestItem> returnRequestItems);
IPage<GoodsReceiptVO> getGoodsReceipts(@Valid GoodsReceiptSearchQO request); IPage<GoodsReceiptVO> getGoodsReceipts(@Valid GoodsReceiptSearchQO request);

View File

@ -11,6 +11,7 @@ import com.nflg.wms.common.exception.NflgException;
import com.nflg.wms.common.pojo.dto.InventoryInDTO; import com.nflg.wms.common.pojo.dto.InventoryInDTO;
import com.nflg.wms.common.pojo.dto.InventoryOutDTO; import com.nflg.wms.common.pojo.dto.InventoryOutDTO;
import com.nflg.wms.common.pojo.qo.InventorySearchQO; import com.nflg.wms.common.pojo.qo.InventorySearchQO;
import com.nflg.wms.common.pojo.vo.InventoryLockVO;
import com.nflg.wms.common.pojo.vo.InventoryVO; import com.nflg.wms.common.pojo.vo.InventoryVO;
import com.nflg.wms.common.util.UserUtil; import com.nflg.wms.common.util.UserUtil;
import com.nflg.wms.common.util.VUtil; import com.nflg.wms.common.util.VUtil;
@ -194,4 +195,9 @@ public class WmsInventoryServiceImpl extends ServiceImpl<WmsInventoryMapper, Wms
.orderByAsc(WmsInventory::getId) .orderByAsc(WmsInventory::getId)
.list(); .list();
} }
@Override
public List<InventoryLockVO> getLockList(String factoryNo, String warehouseNo, String materialNo) {
return baseMapper.getLockList(factoryNo, warehouseNo, materialNo);
}
} }

View File

@ -1,28 +1,39 @@
package com.nflg.wms.repository.service.impl; package com.nflg.wms.repository.service.impl;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nflg.wms.common.constant.STATE;
import com.nflg.wms.common.exception.NflgException;
import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.PageData; import com.nflg.wms.common.pojo.PageData;
import com.nflg.wms.common.pojo.qo.ApplyReturnRequestSearchQO; import com.nflg.wms.common.pojo.qo.ApplyReturnRequestSearchQO;
import com.nflg.wms.common.pojo.qo.GoodsReceiptSearchQO; import com.nflg.wms.common.pojo.qo.GoodsReceiptSearchQO;
import com.nflg.wms.common.pojo.vo.ApplyReturnRequestVO; import com.nflg.wms.common.pojo.vo.ApplyReturnRequestVO;
import com.nflg.wms.common.pojo.vo.GoodsReceiptVO; import com.nflg.wms.common.pojo.vo.GoodsReceiptVO;
import com.nflg.wms.common.pojo.vo.InventoryLockVO;
import com.nflg.wms.repository.entity.WmsOutProduceItem;
import com.nflg.wms.repository.entity.WmsReturnRequest; import com.nflg.wms.repository.entity.WmsReturnRequest;
import com.nflg.wms.repository.entity.WmsReturnRequestItem; import com.nflg.wms.repository.entity.WmsReturnRequestItem;
import com.nflg.wms.repository.mapper.WmsReturnRequestMapper; import com.nflg.wms.repository.mapper.WmsReturnRequestMapper;
import com.nflg.wms.repository.service.IWmsInventoryService;
import com.nflg.wms.repository.service.IWmsReturnRequestItemService; import com.nflg.wms.repository.service.IWmsReturnRequestItemService;
import com.nflg.wms.repository.service.IWmsReturnRequestService; import com.nflg.wms.repository.service.IWmsReturnRequestService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
/** /**
* <p> * <p>
@ -38,15 +49,49 @@ public class WmsReturnRequestServiceImpl extends ServiceImpl<WmsReturnRequestMap
@Resource @Resource
private IWmsReturnRequestItemService itemService; private IWmsReturnRequestItemService itemService;
@Resource
private RedissonClient redissonClient;
@Resource
private IWmsInventoryService inventoryService;
@Transactional @Transactional
@Override @Override
public void addPurchaseReturns(List<WmsReturnRequest> returnRequests, List<WmsReturnRequestItem> returnRequestItems) { public List<InventoryLockVO> addPurchaseReturns(List<WmsReturnRequest> returnRequests, List<WmsReturnRequestItem> returnRequestItems) {
if (CollectionUtil.isNotEmpty(returnRequests)) { if (CollectionUtil.isNotEmpty(returnRequests)) {
this.saveBatch(returnRequests); this.saveBatch(returnRequests);
} }
if (CollectionUtil.isNotEmpty(returnRequestItems)) { if (CollectionUtil.isNotEmpty(returnRequestItems)) {
itemService.saveBatch(returnRequestItems); List<InventoryLockVO> lockVOS = new ArrayList<>();
returnRequestItems.forEach(item->{
RLock lock = redissonClient.getLock(StrUtil.format("lock:inventory:{}:{}:{}", item.getFactoryCode(), item.getStorageLocation(), item.getMaterialCode()));
try {
// 等待5秒获取锁10秒后自动释放
if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
if (inventoryService.getNumOne(item.getFactoryCode(), item.getStorageLocation(), item.getMaterialCode())
.compareTo(item.getLeft()) < 0
) {
lockVOS.addAll(inventoryService.getLockList(item.getFactoryCode(), item.getStorageLocation(), item.getMaterialCode()));
} else {
itemService.save(item);
}
} else {
throw new NflgException(STATE.BusinessError, "获取锁失败");
}
} catch (Exception e) {
log.error("保存生产领料单出错", e);
throw new NflgException(STATE.BusinessError, e.getMessage());
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
});
if (CollectionUtil.isNotEmpty(lockVOS)) {
return lockVOS;
}
} }
return null;
} }
@Override @Override

View File

@ -39,7 +39,7 @@
</select> </select>
<select id="getLockedNum" resultType="com.nflg.wms.common.pojo.dto.InventoryInDTO"> <select id="getLockedNum" resultType="com.nflg.wms.common.pojo.dto.InventoryInDTO">
SELECT material_no,num SELECT material_no,sum(lock_num) as num
FROM v_inventory_lock FROM v_inventory_lock
WHERE factory_no=#{factoryNo} AND warehouse_no=#{warehouseNo} WHERE factory_no=#{factoryNo} AND warehouse_no=#{warehouseNo}
<if test="materialNos != null and materialNos.size() > 0"> <if test="materialNos != null and materialNos.size() > 0">
@ -48,18 +48,26 @@
#{item} #{item}
</foreach> </foreach>
</if> </if>
group by material_no
</select> </select>
<select id="getNumOne" resultType="java.math.BigDecimal"> <select id="getNumOne" resultType="java.math.BigDecimal">
SELECT SUM(num) num SELECT SUM(num) as num
FROM wms_inventory FROM wms_inventory
WHERE factory_no=#{factoryNo} AND warehouse_no=#{warehouseNo} AND material_no=#{materialNo} WHERE factory_no=#{factoryNo} AND warehouse_no=#{warehouseNo} AND material_no=#{materialNo}
GROUP BY factory_no,warehouse_no,material_no GROUP BY factory_no,warehouse_no,material_no
</select> </select>
<select id="getLockedNumOne" resultType="java.math.BigDecimal"> <select id="getLockedNumOne" resultType="java.math.BigDecimal">
SELECT num SELECT SUM(lock_num) as num
FROM v_inventory_lock FROM v_inventory_lock
WHERE factory_no=#{factoryNo} AND warehouse_no=#{warehouseNo} AND material_no=#{materialNo} WHERE factory_no=#{factoryNo} AND warehouse_no=#{warehouseNo} AND material_no=#{materialNo}
</select> </select>
<select id="getLockList" resultType="com.nflg.wms.common.pojo.vo.InventoryLockVO">
SELECT *
FROM v_inventory_lock
WHERE factory_no=#{factoryNo} AND warehouse_no=#{warehouseNo} AND material_no=#{materialNo}
ORDER BY id DESC
</select>
</mapper> </mapper>