feat(oms,wms,system): OMS下单冻结库存(防超卖)——仓库级库存锁定模式
- 仓库新增stock_lock_mode开关(1-下单冻结 2-分配库存冻结默认),查询/独立切换接口+在途冻结单切换守卫 - WMS新增out_order_freeze_record冻结记录表:下单冻结/编辑原子重锁/取消解冻/分配承接/交接扣减/反审还原全状态机,条件更新防并发超卖 - OMS下单即冻结(失败阻断+落库失败补偿解冻)、已下发禁编辑、cancelIssueOrder补调WMS清理(修存量不同步)、cancelAuditState镜像还原修复 - 达梦SQL脚本3个(WAREHOUSE加列/冻结记录建表/主键列名修复),测试库已执行并全流程实测通过
This commit is contained in:
+70
@@ -12,7 +12,9 @@ import com.mhd.basic.domain.warehouse.repository.todo.WarehouseDO;
|
||||
import com.mhd.basic.domain.warehouse.service.WarehouseDomainService;
|
||||
import com.mhd.common.core.enums.DictCode;
|
||||
import com.mhd.common.core.exception.ServiceException;
|
||||
import com.mhd.common.core.web.domain.AjaxResult;
|
||||
import com.mhd.common.security.utils.SecurityUtils;
|
||||
import com.mhd.system.api.WmsServiceFeign;
|
||||
import com.mhd.system.api.domain.SysDictData;
|
||||
import com.mhd.system.api.model.LoginUser;
|
||||
import com.mhd.system.service.ISysDictTypeService;
|
||||
@@ -43,6 +45,8 @@ public class WarehouseApplicationService {
|
||||
private ISysDictTypeService dictTypeService;
|
||||
@Autowired
|
||||
private IAssociationWarehouseService associationWarehouseService;
|
||||
@Autowired
|
||||
private WmsServiceFeign wmsServiceFeign;
|
||||
|
||||
|
||||
/**
|
||||
@@ -239,12 +243,78 @@ public class WarehouseApplicationService {
|
||||
/**
|
||||
* 修改仓库
|
||||
*/
|
||||
/**
|
||||
* @description 查询仓库库存锁定模式(OMS下单冻结库存/防超卖功能的仓库级开关)
|
||||
* 1-下单冻结:OMS出库业务单「下单」即冻结WMS库存(可用-、冻结+,库存数量不变)
|
||||
* 2-分配库存冻结:现有模式,WMS「分配库存」时才冻结
|
||||
* 仓库不存在或未配置(null)时返回默认值 2(与代码侧降级口径一致)
|
||||
* @author gen
|
||||
* @date 2026-09-12
|
||||
*/
|
||||
public Integer getStockLockMode(Long warehouseId) {
|
||||
WarehousePO warehousePO = warehouseDomainService.getInfo(warehouseId);
|
||||
return warehousePO == null ? 2
|
||||
: cn.hutool.core.util.ObjectUtil.defaultIfNull(warehousePO.getStockLockMode(), 2);
|
||||
}
|
||||
|
||||
public Boolean update(WarehouseDO warehouseDO) {
|
||||
// 库存锁定模式切换守卫:模式发生变化时,存在在途冻结单(OMS下单冻结未释放)则禁止切换,
|
||||
// 否则在途单的解冻/承接逻辑会按新模式错乱。fail-closed:校验服务不可用时拒绝切换
|
||||
if (warehouseDO.getWarehouseId() != null && warehouseDO.getStockLockMode() != null) {
|
||||
WarehousePO existingPO = warehouseDomainService.getInfo(warehouseDO.getWarehouseId());
|
||||
if (existingPO != null) {
|
||||
checkStockLockModeSwitchable(warehouseDO.getWarehouseId(), warehouseDO.getStockLockMode(), existingPO);
|
||||
}
|
||||
}
|
||||
//设置数据字典键值
|
||||
setDataDict(warehouseDO);
|
||||
return warehouseDomainService.update(warehouseDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 单独切换仓库库存锁定模式(不改动仓库其他信息)
|
||||
* 1-下单冻结:OMS出库业务单「下单」即冻结WMS库存
|
||||
* 2-分配库存冻结(默认):WMS「分配库存」时才冻结
|
||||
* 切换守卫与通用编辑一致:模式变化且存在在途冻结单时禁止切换,fail-closed
|
||||
* @author gen
|
||||
* @date 2026-09-14
|
||||
*/
|
||||
public Boolean switchStockLockMode(Long warehouseId, Integer stockLockMode) {
|
||||
if (warehouseId == null) {
|
||||
throw new ServiceException("仓库ID不能为空");
|
||||
}
|
||||
if (stockLockMode == null || (stockLockMode.intValue() != 1 && stockLockMode.intValue() != 2)) {
|
||||
throw new ServiceException("库存锁定模式不合法,仅支持 1-下单冻结 2-分配库存冻结");
|
||||
}
|
||||
WarehousePO existingPO = warehouseDomainService.getInfo(warehouseId);
|
||||
if (existingPO == null) {
|
||||
throw new ServiceException("仓库不存在");
|
||||
}
|
||||
checkStockLockModeSwitchable(warehouseId, stockLockMode, existingPO);
|
||||
return warehouseDomainService.switchStockLockMode(warehouseId, stockLockMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 库存锁定模式切换守卫:模式未变化直接放行;模式变化时查询WMS是否存在
|
||||
* 在途下单冻结单(freeze_status 1/2),存在则禁止切换。
|
||||
* fail-closed:WMS服务不可用/返回异常时一律拒绝切换,防止切换后冻结语义错乱
|
||||
* @author gen
|
||||
* @date 2026-09-14
|
||||
*/
|
||||
private void checkStockLockModeSwitchable(Long warehouseId, Integer newMode, WarehousePO existingPO) {
|
||||
if (newMode.compareTo(cn.hutool.core.util.ObjectUtil.defaultIfNull(existingPO.getStockLockMode(), 2)) == 0) {
|
||||
return;
|
||||
}
|
||||
AjaxResult freezeCheck = wmsServiceFeign.hasActiveFreezeByWarehouse(warehouseId);
|
||||
if (freezeCheck == null || !"200".equals(String.valueOf(freezeCheck.get("code")))) {
|
||||
throw new ServiceException("库存锁定模式切换校验失败,请稍后重试:"
|
||||
+ (freezeCheck == null ? "WMS服务不可用" : freezeCheck.get("msg")));
|
||||
}
|
||||
if (Boolean.TRUE.equals(freezeCheck.get("data"))) {
|
||||
throw new ServiceException("该仓库存在下单冻结中的出库单,不可切换库存锁定模式");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 设置数据字典键值
|
||||
* @author ZhouGY
|
||||
|
||||
+4
@@ -130,6 +130,10 @@ public class Warehouse extends BaseVOEntity{
|
||||
@Excel(name = "作业模式")
|
||||
private String workMode;
|
||||
|
||||
@ApiModelProperty("库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||
@Excel(name = "库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||
private Integer stockLockMode;
|
||||
|
||||
@ApiModelProperty("订单号生成规则0日期累加1历史累加")
|
||||
@Excel(name = "单号生成规则")
|
||||
private String orderNoGenerateRule;
|
||||
|
||||
+4
@@ -132,6 +132,10 @@ public class WarehousePO extends BaseVOEntity{
|
||||
@Excel(name = "作业模式")
|
||||
private String workMode;
|
||||
|
||||
@ApiModelProperty("库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||
@Excel(name = "库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||
private Integer stockLockMode;
|
||||
|
||||
|
||||
@ApiModelProperty("订单号生成规则")
|
||||
@Excel(name = "单号生成规则")
|
||||
|
||||
+4
@@ -140,6 +140,10 @@ public class WarehouseDO extends BaseVOEntity{
|
||||
@Excel(name = "作业模式")
|
||||
private String workMode;
|
||||
|
||||
@ApiModelProperty("库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||
@Excel(name = "库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||
private Integer stockLockMode;
|
||||
|
||||
|
||||
@ApiModelProperty("订单号生成规则")
|
||||
@Excel(name = "单号生成规则")
|
||||
|
||||
+23
@@ -142,6 +142,29 @@ public class WarehouseDomainService {
|
||||
.eq(Warehouse::getDelFlag, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 单独切换仓库库存锁定模式(仅更新 STOCK_LOCK_MODE 与审计字段,不触碰仓库其他数据)
|
||||
* 1-下单冻结:OMS出库业务单「下单」即冻结WMS库存
|
||||
* 2-分配库存冻结(默认):WMS「分配库存」时才冻结
|
||||
* @author gen
|
||||
* @date 2026-09-14
|
||||
*/
|
||||
public Boolean switchStockLockMode(Long warehouseId, Integer stockLockMode) {
|
||||
Long updateBy = null;
|
||||
try {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
updateBy = loginUser == null ? null : loginUser.getUserid();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
Long finalUpdateBy = updateBy;
|
||||
return warehouseService.update(new UpdateWrapper<Warehouse>().lambda()
|
||||
.set(Warehouse::getStockLockMode, stockLockMode)
|
||||
.set(finalUpdateBy != null, Warehouse::getUpdateBy, finalUpdateBy)
|
||||
.set(Warehouse::getUpdateTime, new Date())
|
||||
.eq(Warehouse::getWarehouseId, warehouseId)
|
||||
.eq(Warehouse::getDelFlag, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取指定仓库下的所有库区及库区下所有库位
|
||||
* @author ZhouGY
|
||||
|
||||
+4
@@ -145,6 +145,10 @@ public class WarehouseDTO extends BaseVOEntity{
|
||||
@Excel(name = "作业模式")
|
||||
private String workMode;
|
||||
|
||||
@ApiModelProperty("库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||
@Excel(name = "库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||
private Integer stockLockMode;
|
||||
|
||||
|
||||
@ApiModelProperty("订单号生成规则")
|
||||
@Excel(name = "单号生成规则")
|
||||
|
||||
+27
@@ -104,6 +104,33 @@ public class WarehouseApi extends BaseController{
|
||||
return AjaxResult.success(warehouseApplicationService.getInfo(warehouseId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询仓库库存锁定模式(OMS下单冻结库存/防超卖功能的仓库级开关):
|
||||
* 1-下单冻结(OMS出库业务单「下单」即冻结WMS库存:可用-、冻结+,库存数量不变;
|
||||
* 分配不动数量;编辑=原子重锁;取消=释放;交接=库存-冻结-)
|
||||
* 2-分配库存冻结(默认,现有行为:WMS「分配库存」时才 可用-冻结+)
|
||||
* 仓库不存在或未配置时返回 2(与 OMS/WMS 代码侧降级口径一致)
|
||||
*/
|
||||
@ApiOperation("查询仓库库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||
@GetMapping(value = "/getStockLockMode/{warehouseId}")
|
||||
public AjaxResult getStockLockMode(@PathVariable("warehouseId") Long warehouseId)
|
||||
{
|
||||
return AjaxResult.success(warehouseApplicationService.getStockLockMode(warehouseId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单独切换仓库库存锁定模式(不改动仓库其他信息):
|
||||
* 模式变化且该仓库存在在途下单冻结出库单(freeze_status 1/2)时拒绝切换;
|
||||
* WMS校验服务不可用时同样拒绝(fail-closed)
|
||||
*/
|
||||
@ApiOperation("切换仓库库存锁定模式: 1-下单冻结 2-分配库存冻结")
|
||||
@PostMapping(value = "/switchStockLockMode/{warehouseId}/{stockLockMode}")
|
||||
public AjaxResult switchStockLockMode(@PathVariable("warehouseId") Long warehouseId,
|
||||
@PathVariable("stockLockMode") Integer stockLockMode)
|
||||
{
|
||||
return toAjax(warehouseApplicationService.switchStockLockMode(warehouseId, stockLockMode));
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation("批量作废仓库")
|
||||
@GetMapping("/nullifyByIds/{warehouseIds}")
|
||||
|
||||
+2
-1
@@ -43,6 +43,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="orderNoGenerateRule" column="ORDER_NO_GENERATE_RULE" />
|
||||
<result property="inOrderNoPrefix" column="IN_ORDER_NO_PREFIX" />
|
||||
<result property="outOrderNoPrefix" column="OUT_ORDER_NO_PREFIX" />
|
||||
<result property="stockLockMode" column="STOCK_LOCK_MODE" />
|
||||
</resultMap>
|
||||
|
||||
|
||||
@@ -51,7 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
warehouse_type, warehouse_type_name, warehouse_structure, warehouse_nature, warm_layer_code, warm_layer_name,
|
||||
director_name, director_phone, region_code, region_name, detail_address, longitude, latitude, building_region,
|
||||
usage_region, remark, opening_up, nullify, create_time, create_by, create_by_name, update_time, update_by,
|
||||
update_by_name, del_flag, IS_ENABLE ,WORK_MODE,TASK_MODE,SAMPLE_OUT_ORDER_NO_PREFIX,ORDER_NO_GENERATE_RULE,IN_ORDER_NO_PREFIX,
|
||||
update_by_name, del_flag, IS_ENABLE ,WORK_MODE,TASK_MODE,STOCK_LOCK_MODE,SAMPLE_OUT_ORDER_NO_PREFIX,ORDER_NO_GENERATE_RULE,IN_ORDER_NO_PREFIX,
|
||||
OUT_ORDER_NO_PREFIX
|
||||
from warehouse
|
||||
</sql>
|
||||
|
||||
Reference in New Issue
Block a user