策略模式+工厂方法

1.定义策略接口

该处以实际开发中仓库库存变更为例

import com.gtown.cloud.erp.vo.request.ChangeStockRequest;
import com.gtown.cloud.erp.vo.response.stock.StockInfoResponse;
import org.springframework.beans.factory.InitializingBean;

public interface ChangeStockHandler extends InitializingBean {
    /**
     * 更新库存表库存
     * param spuId
     * param skuId
     * param companyId
     * param warehouseId
     * param quantity
     * param createUserId
     * return
     */
    StockInfoResponse changeStock(ChangeStockRequest changeStockRequest);
}

2.定义工厂类

import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author smile.zhao
 * @version 1.0
 * @date 2024/3/25 17:12
 */
public class ChangeStockFactory {

    private static Map<String, ChangeStockHandler> strategyMap = new ConcurrentHashMap<>();

    /**
     * 根据不同的入库的类型获取对应的handler实现
     *
     * @param type 入库类型:GtWarehouseStockEnum 中对应的类型:SALE & TRANSFER & PURCHASE
     * @return
     */
    public static ChangeStockHandler getInvokeStockHandler(String type) {
        return strategyMap.get(type);
    }

    /**
     * 注册
     *
     * @param type
     * @param handler
     */
    public static void register(String type, ChangeStockHandler handler) {
        if (StringUtils.isEmpty(type) || null == handler) {
            return;
        }
        strategyMap.put(type, handler);
    }

}

3.策略实现类

3.1 采购变更库存

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.gtown.cloud.erp.config.enums.GtWarehouseStockEnum;
import com.gtown.cloud.erp.config.enums.StockSnapshotEnum;
import com.gtown.cloud.erp.entity.GtWarehouse;
import com.gtown.cloud.erp.entity.GtWarehouseStock;
import com.gtown.cloud.erp.entity.GtWarehouseStockSnapshot;
import com.gtown.cloud.erp.mapper.GtPurchaserMapper;
import com.gtown.cloud.erp.mapper.GtWareHouseMapper;
import com.gtown.cloud.erp.mapper.GtWareHouseStockMapper;
import com.gtown.cloud.erp.service.ChangeStockFactory;
import com.gtown.cloud.erp.service.ChangeStockHandler;
import com.gtown.cloud.erp.util.BigDecimalUtils;
import com.gtown.cloud.erp.util.RedisKeySplitUtil;
import com.gtown.cloud.erp.util.RedisUtil;
import com.gtown.cloud.erp.vo.request.ChangeStockRequest;
import com.gtown.cloud.erp.vo.response.stock.RollBackStockRecord;
import com.gtown.cloud.erp.vo.response.stock.StockInfoResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;

/**
 * @author smile.zhao
 * @version 1.0
 * @date 2024/3/26 19:20
 */
@Service
@Slf4j
public class PurchaserHandler implements ChangeStockHandler {
    @Autowired
    GtWareHouseStockMapper gtWareHouseStockMapper;

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    GtPurchaserMapper gtPurchaserMapper;

    @Autowired
    GtWareHouseMapper gtWareHouseMapper;

    /**
     * 接收采购单审批结果回调使用该类
     * 采购商品,变更库存信息,此处不会更新库,需要在其他业务代码的同一个事务中变更数据
     * 采购需要变更在途库存:
     * <p>
     * param spuId
     * param skuId
     * param companyId
     * param warehouseId
     * param quantity
     * return
     */
    @Override
    public StockInfoResponse changeStock(ChangeStockRequest changeStockRequest) {
        log.info("采购单审批结果回调handler,ChangeStockRequest:{}", changeStockRequest);
        Long spuId = changeStockRequest.getSpuId();
        Long skuId = changeStockRequest.getSkuId();
        Long companyId = changeStockRequest.getCompanyId();
        Long warehouseId = changeStockRequest.getWarehouseId();
        BigDecimal quantity = changeStockRequest.getQuantity();
        Long createUserId = changeStockRequest.getCreateUserId();
        RollBackStockRecord rollBackStockRecord = new RollBackStockRecord();
        //先查询缓存有无,若缓存无,则查询数据库,并添加到缓存
        GtWarehouseStock stock = null;
        //redisUtil.set(RedisKeySplitUtil.stockRollBackKey(spuId, skuId, companyId, warehouseId),new StockInfoResponse());
        rollBackStockRecord.setRedisKey(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId)).setStock(stock).setSnapshot(null);
        if (redisUtil.hasKey(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId))) {
            log.info("缓存有库存信息key:{}", RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId));
            StockInfoResponse response = (StockInfoResponse) redisUtil.get(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId));
            if (Objects.nonNull(response)) {
                stock = response.getStock();
                //缓存中库存便跟改动备份,做库存回滚时使用
                rollBackStockRecord.setStock(stock)
                        .setSnapshot(Objects.isNull(response.getSnapshot()) ? null : response.getSnapshot().setOperationType(StockSnapshotEnum.PURCHASE_INIT.getRollBackCode()));
                //redisUtil.set(RedisKeySplitUtil.stockRollBackKey(spuId, skuId, companyId, warehouseId),response);
            }
        }
        //缓存没有,查数据库,处理好数量后重新放回缓存,
        if (Objects.isNull(stock)) {
            stock = gtWareHouseStockMapper.selectBySkuInfoAndWarehouseId(skuId, spuId, companyId, warehouseId);
            rollBackStockRecord.setStock(stock);
            log.info("缓存没有库存信息,查询数据库库存{}", stock);
        }
        log.info("采购处理库存策略结果 信息stock:{}", stock);
        //数据库没有 ,为null,则为首次入库,首次入库创建一条记录
        if (Objects.isNull(stock)) {
            log.info("数据库没有库存信息,首次入库");
            GtWarehouse gtWarehouse = gtWareHouseMapper.selectByPrimaryKey(warehouseId);
            if (Objects.isNull(gtWarehouse)) {
                log.error("仓库不存在");
                return null;
            }
            stock = new GtWarehouseStock().setCompanyId(companyId)
                    .setWarehouseId(warehouseId)
                    //回调时需以创建为准
                    .setCreatedUserId(createUserId)
                    .setSkuId(skuId).setSpuId(spuId).setAvgCostPrice(BigDecimal.ZERO.stripTrailingZeros())
                    //总库存实时计算,默认为0
                    .setTotalStock(BigDecimal.ZERO.stripTrailingZeros())
                    //可用库存,初始为0
                    .setAvailableStock(BigDecimal.ZERO.stripTrailingZeros())
                    //待出库存初始为0
                    .setPreOutStock(BigDecimal.ZERO.stripTrailingZeros())
                    //待入库存(在途库存)
                    .setPreInStock(quantity)
                    //调拨出库在途库存
                    .setPreOutTransferStock(BigDecimal.ZERO.stripTrailingZeros())
                    //销售出库在途库存
                    .setPreOutSaleStock(BigDecimal.ZERO.stripTrailingZeros())
                    //锁定库存
                    .setLockStock(BigDecimal.ZERO.stripTrailingZeros())
                    //次品库存
                    .setDefectiveStock(BigDecimal.ZERO.stripTrailingZeros())
                    .setUpThreshold(0L).setLowThreshold(0L).setVersion(0L).setStatus(GtWarehouseStockEnum.NORMAL.getCode())
                    .setProductUserId(createUserId)
                    .setRemark("").setUpdateById(createUserId).setWarehouseType(gtWarehouse.getWarehouseType());
            stock.setArchive(false);
            stock.setCreatedAt(new Date());
            stock.setUpdatedAt(new Date());

            //需要将库存信息变更记录一份到快照表中
            //处理快照表,忽略"id"属性的拷贝,需要库存入库后再处理快照表中关联的库存表的id
            CopyOptions options = CopyOptions.create().setIgnoreProperties("id");
            GtWarehouseStockSnapshot gtWarehouseStockSnapshot = new GtWarehouseStockSnapshot();
            BeanUtil.copyProperties(stock, gtWarehouseStockSnapshot, options);
            gtWarehouseStockSnapshot.setStockId(stock.getId())
                    .setOperationType(StockSnapshotEnum.PURCHASE_INIT.getCode())
                    .setOperationAmount(quantity);
            gtWarehouseStockSnapshot.setCreatedAt(new Date());
            gtWarehouseStockSnapshot.setUpdatedAt(new Date());
            gtWarehouseStockSnapshot.setCompanyId(companyId).setCreatedUserId(createUserId);
            StockInfoResponse stockInfoResponse = new StockInfoResponse().setStock(stock).setSnapshot(gtWarehouseStockSnapshot);
            redisUtil.set(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId), stockInfoResponse);
            stockInfoResponse.setRollBackStockRecord(rollBackStockRecord);
            return stockInfoResponse;
        }
        //需要将库存信息变更记录一份到快照表中
        //处理快照表,忽略"id"属性的拷贝 TODO   加锁
        CopyOptions options = CopyOptions.create().setIgnoreProperties("id");
        GtWarehouseStockSnapshot gtWarehouseStockSnapshot = new GtWarehouseStockSnapshot();
        BeanUtil.copyProperties(stock, gtWarehouseStockSnapshot, options);
        gtWarehouseStockSnapshot.setStockId(stock.getId())
                .setOperationType(StockSnapshotEnum.PURCHASE_MODIFY_PRE_IN.getCode())
                .setOperationAmount(quantity);
        gtWarehouseStockSnapshot.setCreatedAt(new Date());
        gtWarehouseStockSnapshot.setUpdatedAt(new Date());
        gtWarehouseStockSnapshot.setCompanyId(companyId).setCreatedUserId(createUserId);
        gtWarehouseStockSnapshot.setStockId(stock.getId());
        //采购单审核过后变更 待入库存(在途库存)
        //stock.setPreInStock(stock.getPreInStock() + quantity);
        stock.setPreInStock(BigDecimalUtils.add(stock.getPreInStock(), quantity));
        stock.setUpdatedAt(new Date());
        StockInfoResponse stockInfoResponse = new StockInfoResponse().setStock(stock);
        redisUtil.set(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId), stockInfoResponse);
        stockInfoResponse.setRollBackStockRecord(rollBackStockRecord);
        return stockInfoResponse;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ChangeStockFactory.register(GtWarehouseStockEnum.PURCHASE.getCode(), this);
    }
}

3.2 入库变更库存

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.gtown.cloud.erp.config.enums.GtWarehouseStockEnum;
import com.gtown.cloud.erp.config.enums.StockSnapshotEnum;
import com.gtown.cloud.erp.entity.GtWarehouseStock;
import com.gtown.cloud.erp.entity.GtWarehouseStockSnapshot;
import com.gtown.cloud.erp.mapper.GtGoodsWarehousingMapper;
import com.gtown.cloud.erp.mapper.GtPurchaserMapper;
import com.gtown.cloud.erp.mapper.GtWareHouseStockMapper;
import com.gtown.cloud.erp.service.ChangeStockFactory;
import com.gtown.cloud.erp.service.ChangeStockHandler;
import com.gtown.cloud.erp.util.BigDecimalUtils;
import com.gtown.cloud.erp.util.RedisKeySplitUtil;
import com.gtown.cloud.erp.util.RedisUtil;
import com.gtown.cloud.erp.vo.request.ChangeStockRequest;
import com.gtown.cloud.erp.vo.response.instorage.PurchaserInfo;
import com.gtown.cloud.erp.vo.response.stock.RollBackStockRecord;
import com.gtown.cloud.erp.vo.response.stock.StockInfoResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author smile.zhao
 * @version 1.0
 * @date 2024/3/26 15:21
 */
@Service
@Slf4j
public class InStorageHandler implements ChangeStockHandler {

    @Autowired
    GtWareHouseStockMapper gtWareHouseStockMapper;

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    GtGoodsWarehousingMapper gtGoodsWarehousingMapper;

    @Autowired
    GtPurchaserMapper gtPurchaserMapper;

    /**
     * saveStorage接口调用该类,即采购入库
     * 商品入库,变更库存信息,此处不会更新库,需要在其他业务代码的同一个事务中变更数据
     * 入库后 增加可用库存,减少在途库存,总库存实时计算
     * <p>
     * 库存均价计算:原来(单价*数量)+入库(单价*数量)/ 总数量
     *
     * param spuId
     * param skuId
     * param companyId
     * param warehouseId
     * param quantity
     * @return
     */
    @Override
    public StockInfoResponse changeStock(ChangeStockRequest changeStockRequest) {
        Long spuId = changeStockRequest.getSpuId();
        Long skuId = changeStockRequest.getSkuId();
        Long companyId = changeStockRequest.getCompanyId();
        Long warehouseId = changeStockRequest.getWarehouseId();
        BigDecimal quantity = changeStockRequest.getQuantity();
        Long createUserId = changeStockRequest.getCreateUserId();
        Long purchaserId = changeStockRequest.getPurchaserId();
        //先查询缓存有无,若缓存无,则查询数据库,并添加到缓存
        GtWarehouseStock stock = null;
        RollBackStockRecord rollBackStockRecord = new RollBackStockRecord()
                .setRedisKey(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId))
                .setStock(stock).setSnapshot(null);
        //redisUtil.set(RedisKeySplitUtil.stockRollBackKey(spuId, skuId, companyId, warehouseId),new StockInfoResponse());
        if (redisUtil.hasKey(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId))) {
            log.info("缓存有库存信息key:{}", RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId));
            StockInfoResponse response = (StockInfoResponse) redisUtil.get(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId));
            if (Objects.nonNull(response)) {
                stock = response.getStock();
                rollBackStockRecord.setStock(response.getStock())
                        .setSnapshot(Objects.isNull(response.getSnapshot())? null : response.getSnapshot().setOperationType(StockSnapshotEnum.PURCHASE_IN_STORAGE.getRollBackCode()));
                //redisUtil.set(RedisKeySplitUtil.stockRollBackKey(spuId, skuId, companyId, warehouseId),response);
            }
        }
        //缓存没有,查数据库,处理好数量后重新放回缓存,
        if (Objects.isNull(stock)) {
            stock = gtWareHouseStockMapper.selectBySkuInfoAndWarehouseId(skuId, spuId, companyId, warehouseId);
            rollBackStockRecord.setStock(stock);
            log.info("缓存没有库存信息,查询数据库库存{}", stock);
        }
        //数据库也没有,为null,则为首次入库,无需审批,首次入库创建一条记录,因采购时就会缓存到缓存,故此处依正常流程不会走的到,可能为其他入库
        if (Objects.isNull(stock)) {
            log.error("数据库及缓存均没有库存信息,非正常入库!!!");
            return null;
        }
        //原库存表中可用库存数量
        BigDecimal availableStock = stock.getAvailableStock();
        //原库存表中的价格
        BigDecimal avgCostPrice = stock.getAvgCostPrice();
        //需要将库存信息变更记录一份到快照表中,处理快照表,忽略"id"属性的拷贝
        CopyOptions options = CopyOptions.create().setIgnoreProperties("id");
        GtWarehouseStockSnapshot gtWarehouseStockSnapshot = new GtWarehouseStockSnapshot();
        BeanUtil.copyProperties(stock, gtWarehouseStockSnapshot, options);
        gtWarehouseStockSnapshot.setStockId(stock.getId())
                //本次操作类型:采购入库,减少在途,增加可用
                .setOperationType(StockSnapshotEnum.PURCHASE_IN_STORAGE.getCode())
                //本次操作数量
                .setOperationAmount(quantity);
        gtWarehouseStockSnapshot.setCreatedAt(new Date());
        gtWarehouseStockSnapshot.setUpdatedAt(new Date());
        gtWarehouseStockSnapshot.setCompanyId(companyId);
        gtWarehouseStockSnapshot.setStockId(stock.getId());

        //可用库存需要加上本次采购数量,总库存不变,载入库存减去本次变更数量
        stock.setAvailableStock(BigDecimalUtils.add(stock.getAvailableStock(), quantity));
        //在途库存需要减去本次采购数量
        stock.setPreInStock(BigDecimalUtils.subtract(stock.getPreInStock(), quantity));
        stock.setUpdatedAt(new Date());
        /*
        采购入库才计算均价:(原来(单价*数量)+入库(单价*数量)) / 总数量
        1.由入库商品skuid加spuid加仓库id,查询采购入库记录,由采购记录id可获得每次的单价
         */
        //本次入库的单价:入库单对应的采购单id,采购单子表中的skuid,spuid对应的单价
        log.info("原库存表中可用库存数量:{};原库存表中的价格:{}", availableStock, avgCostPrice);
        BigDecimal unitPrice = BigDecimal.ZERO;
        PurchaserInfo purchaserInfo = gtGoodsWarehousingMapper.selectPurchaseAndQuality(spuId, skuId, companyId, warehouseId, GtWarehouseStockEnum.IN_STORAGE.getType(), purchaserId);
        log.info("查询采购单价信息{}", purchaserInfo);
        if (Objects.nonNull(purchaserInfo)){
            //查询采购单价
            unitPrice = gtPurchaserMapper.selectUnitPrice(purchaserInfo);
        }
        log.info("now采购单价{}", unitPrice);
        BigDecimal originalPrice = BigDecimalUtils.multiply(availableStock, avgCostPrice);
        log.info("原始采购总价:{}",originalPrice);
        BigDecimal nowPrice = BigDecimalUtils.multiply(quantity, unitPrice);
        log.info("本次采购总价:{}",nowPrice);
        BigDecimal newAvgCostPrice = BigDecimalUtils.divide(BigDecimalUtils.add(originalPrice, nowPrice), BigDecimalUtils.add(availableStock, quantity));
        log.info("采购入库计算新均价{}", newAvgCostPrice);
        stock.setAvgCostPrice(newAvgCostPrice);
        StockInfoResponse stockInfoResponse = new StockInfoResponse().setStock(stock).setSnapshot(gtWarehouseStockSnapshot);
        redisUtil.set(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId), stockInfoResponse);
        stockInfoResponse.setRollBackStockRecord(rollBackStockRecord);
        return stockInfoResponse;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ChangeStockFactory.register(GtWarehouseStockEnum.IN_STORAGE.getCode(), this);
    }
}

3.3 出库变更库存

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.gtown.cloud.erp.config.enums.GtWarehouseStockEnum;
import com.gtown.cloud.erp.config.enums.StockSnapshotEnum;
import com.gtown.cloud.erp.entity.GtWarehouseStock;
import com.gtown.cloud.erp.entity.GtWarehouseStockSnapshot;
import com.gtown.cloud.erp.mapper.GtWareHouseStockMapper;
import com.gtown.cloud.erp.service.ChangeStockFactory;
import com.gtown.cloud.erp.service.ChangeStockHandler;
import com.gtown.cloud.erp.util.BigDecimalUtils;
import com.gtown.cloud.erp.util.RedisKeySplitUtil;
import com.gtown.cloud.erp.util.RedisUtil;
import com.gtown.cloud.erp.vo.request.ChangeStockRequest;
import com.gtown.cloud.erp.vo.response.stock.RollBackStockRecord;
import com.gtown.cloud.erp.vo.response.stock.StockInfoResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;

/**
 * @author smile.zhao
 * @version 1.0
 * @date 2024/3/25 18:37
 */
@Service
@Slf4j
public class OutboundSaleHandler implements ChangeStockHandler {

    @Autowired
    GtWareHouseStockMapper gtWareHouseStockMapper;

    @Autowired
    RedisUtil redisUtil;

    /**
     * 销售出库api调用时变更库存信息:@PostMapping("/createDelivery")
     * @param spuId
     * @param skuId
     * @param companyId
     * @param warehouseId
     * @param quantity
     * @param userId
     * @return
     */
    @Override
    public StockInfoResponse changeStock(ChangeStockRequest changeStockRequest) {
        log.info("销售出库api调用时变更库存信息handler;ChangeStockRequest:{}", changeStockRequest);
        Long spuId = changeStockRequest.getSpuId();
        Long skuId = changeStockRequest.getSkuId();
        Long companyId = changeStockRequest.getCompanyId();
        Long warehouseId = changeStockRequest.getWarehouseId();
        BigDecimal quantity = changeStockRequest.getQuantity();
        Long createUserId = changeStockRequest.getCreateUserId();
        GtWarehouseStock stock = null;
        //redisUtil.set(RedisKeySplitUtil.stockRollBackKey(spuId, skuId, companyId, warehouseId), new StockInfoResponse());
        RollBackStockRecord rollBackStockRecord = new RollBackStockRecord()
                .setRedisKey(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId))
                .setStock(stock).setSnapshot(null);
        if (redisUtil.hasKey(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId))) {
            log.info("缓存有库存信息key:{}", RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId));
            StockInfoResponse response = (StockInfoResponse) redisUtil.get(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId));
            if (Objects.nonNull(response)) {
                stock = response.getStock();
                rollBackStockRecord.setStock(response.getStock())
                        .setSnapshot(Objects.isNull(response.getSnapshot()) ? null : response.getSnapshot().setOperationType(StockSnapshotEnum.SALES_OUTBOUND.getRollBackCode()));
                //库存记录回滚备份
                //redisUtil.set(RedisKeySplitUtil.stockRollBackKey(spuId, skuId, companyId, warehouseId),response);
            }
        }
        //缓存没有,查数据库,处理好数量后重新放回缓存,
        if (Objects.isNull(stock)) {
            stock = gtWareHouseStockMapper.selectBySkuInfoAndWarehouseId(skuId, spuId, companyId, warehouseId);
            rollBackStockRecord.setStock(stock);
            log.info("缓存没有库存信息,查询数据库库存{}", stock);
        }
        //若出库时为null,则表示该次为异常请求
        if (Objects.isNull(stock)) {
            log.info("出库时库存为空,请检查skuId,spuId,companyId,warehouseId是否正确");
            return null;
        }

        //需要将库存信息变更记录一份到快照表中
        //先处理快照表,忽略"id"属性的拷贝
        CopyOptions options = CopyOptions.create().setIgnoreProperties("id");
        GtWarehouseStockSnapshot gtWarehouseStockSnapshot = new GtWarehouseStockSnapshot();
        BeanUtil.copyProperties(stock, gtWarehouseStockSnapshot, options);
        gtWarehouseStockSnapshot.setStockId(stock.getId())
                .setOperationType(StockSnapshotEnum.SALES_OUTBOUND.getCode())
                .setOperationAmount(quantity);
        gtWarehouseStockSnapshot.setCreatedAt(new Date());
        gtWarehouseStockSnapshot.setUpdatedAt(new Date());
        gtWarehouseStockSnapshot.setCompanyId(companyId).setCreatedUserId(createUserId);
        //可用库存减少
        stock.setAvailableStock(BigDecimalUtils.subtract(stock.getAvailableStock(), quantity));
        //预待出库存(预占库存) 增加
        stock.setPreOutStock(BigDecimalUtils.add(stock.getPreOutStock(), quantity));
        stock.setUpdatedAt(new Date());
        StockInfoResponse stockInfoResponse = new StockInfoResponse().setStock(stock).setSnapshot(gtWarehouseStockSnapshot);
        redisUtil.set(RedisKeySplitUtil.stockKey(spuId, skuId, companyId, warehouseId), stockInfoResponse);
        stockInfoResponse.setRollBackStockRecord(rollBackStockRecord);
        return stockInfoResponse;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ChangeStockFactory.register(GtWarehouseStockEnum.SALE.getCode(), this);
    }
}

4.策略枚举类(自定义)

import lombok.Getter;
import org.apache.commons.lang3.StringUtils;

@Getter
public enum GtWarehouseStockEnum {

    NORMAL("库存正常", "0", ""),

    INVENTORY_BACKLOG("库存积压", "1", ""),

    INSUFFICIENT("库存不足", "2", ""),

    INVENTORY_CHECKING("库存盘点中", "3", ""),

    STOCK_SAVE("新增库存", "SAVE", ""),

    STOCK_MODIFY("库存变更", "MODIFY", ""),

    //采购入库,变更总库存,审核通过后变更可用库存
    IN_STORAGE("商品采购后入库", "IN_STORAGE", "PURCHASE"),

    IN_STORAGE_TRANSFER("调拨入库", "IN_STORAGE_TRANSFER", "ALLOCATION"),

    //销售出库,变更总库存
    SALE("销售出库", "SALE", ""),

    TRANSFER("调拨出库", "TRANSFER", ""),

    LOCK("锁定库存", "LOCK", ""),

    //采购入库,审核后需要变更在途库存
    PURCHASE("采购入库", "PURCHASE", ""),

    //拒绝出库
    REFUSE_SALE_OUT("拒绝订单销售出库", "REFUSE_SALE_OUT", "SALE"),

    //拒绝出库
    REFUSE_TRANSFER_OUT("拒绝订单调拨出库", "REFUSE_TRANSFER_OUT", "TRANSFER"),

    //代发入库
    DELIVERY_IN_STOCK("代发入库", "DELIVERY_IN_STOCK", "DELIVERY_IN_STOCK"),

    //代发出库
    DELIVERY_OUT_STOCK("代发出库", "DELIVERY_OUT_STOCK", "DELIVERY_OUT_STOCK"),

    ;
    /**
     * 名称
     */
    private String name;
    /**
     * 类型
     */
    private String code;

    private String type;


    GtWarehouseStockEnum(String name, String code, String type) {
        this.name = name;
        this.code = code;
        this.type = type;
    }

    public static GtWarehouseStockEnum getByCode(String code) {
        if (StringUtils.isBlank(code)) {
            return null;
        }
        for (GtWarehouseStockEnum stockEnum : values()) {
            if (StringUtils.equals(code, stockEnum.getCode())) {
                return stockEnum;
            }
        }
        return null;
    }

    public static GtWarehouseStockEnum getByType(String type) {
        if (StringUtils.isBlank(type)) {
            return null;
        }
        for (GtWarehouseStockEnum stockEnum : values()) {
            if (StringUtils.equals(type, stockEnum.getType())) {
                return stockEnum;
            }
        }
        return null;
    }
}

好的,我了解了您的问题。下面是我对这个问题的回答: 首先,我们需要了解一下策略模式工厂模式和模板方法模式的概念和用法: - 策略模式:将一组算法封装起来,使它们可以互相替换,从而使得算法的变化不会影响到使用算法的客户端。在策略模式中,我们需要定义一个策略接口和具体的策略实现类。 - 工厂模式:将对象的创建与使用分离,通过工厂类来创建对象,从而使得客户端无需知道对象的具体实现。在工厂模式中,我们需要定义一个工厂接口和具体的工厂实现类。 - 模板方法模式:将一组相似的算法步骤封装起来,使得算法的变化只会影响到模板方法的具体实现,从而使得代码的复用性和可维护性更好。 在实现不同支付方式的接口时,我们可以使用策略模式来实现不同的支付策略,工厂模式来创建不同的支付策略对象,模板方法模式来实现支付的公共流程。 具体实现步骤如下: 1. 定义支付策略接口 PaymentStrategy,其中包含一个 pay 方法用来实现支付功能。 ```java public interface PaymentStrategy { void pay(double amount); } ``` 2. 定义具体的支付策略实现类,例如 AlipayPaymentStrategy 和 WechatPaymentStrategy,实现 PaymentStrategy 接口中的 pay 方法。 ```java public class AlipayPaymentStrategy implements PaymentStrategy { @Override public void pay(double amount) { // 实现支付宝支付逻辑 System.out.println("使用支付宝支付:" + amount + "元"); } } public class WechatPaymentStrategy implements PaymentStrategy { @Override public void pay(double amount) { // 实现微信支付逻辑 System.out.println("使用微信支付:" + amount + "元"); } } ``` 3. 定义工厂接口 PaymentFactory,其中包含一个 createPayment 方法用来创建支付策略对象。 ```java public interface PaymentFactory { PaymentStrategy createPayment(); } ``` 4. 定义具体的支付工厂实现类,例如 AlipayPaymentFactory 和 WechatPaymentFactory,实现 PaymentFactory 接口中的 createPayment 方法。 ```java public class AlipayPaymentFactory implements PaymentFactory { @Override public PaymentStrategy createPayment() { return new AlipayPaymentStrategy(); } } public class WechatPaymentFactory implements PaymentFactory { @Override public PaymentStrategy createPayment() { return new WechatPaymentStrategy(); } } ``` 5. 定义支付模板类 PaymentTemplate,其中包含一个 pay 方法用来实现支付的公共流程,该方法调用 PaymentFactory 创建支付策略对象,并调用支付策略的 pay 方法实现支付功能。 ```java public abstract class PaymentTemplate { public void pay(double amount) { PaymentStrategy paymentStrategy = createPaymentStrategy(); paymentStrategy.pay(amount); } protected abstract PaymentStrategy createPaymentStrategy(); } ``` 6. 定义具体的支付模板类实现类,例如 AlipayPaymentTemplate 和 WechatPaymentTemplate,实现 PaymentTemplate 中的 createPaymentStrategy 方法,返回对应的支付策略对象。 ```java public class AlipayPaymentTemplate extends PaymentTemplate { @Override protected PaymentStrategy createPaymentStrategy() { return new AlipayPaymentFactory().createPayment(); } } public class WechatPaymentTemplate extends PaymentTemplate { @Override protected PaymentStrategy createPaymentStrategy() { return new WechatPaymentFactory().createPayment(); } } ``` 7. 在 SpringBoot 项目中使用支付模板类实现不同支付方式的接口,例如 PaymentController,其中包含一个 pay 方法用来接收支付请求,根据支付方式调用对应的支付模板类实现支付功能。 ```java @RestController public class PaymentController { @GetMapping("/pay") public String pay(@RequestParam("amount") double amount, @RequestParam("type") String type) { PaymentTemplate paymentTemplate = null; if ("alipay".equals(type)) { paymentTemplate = new AlipayPaymentTemplate(); } else if ("wechat".equals(type)) { paymentTemplate = new WechatPaymentTemplate(); } else { return "不支持的支付方式:" + type; } paymentTemplate.pay(amount); return "支付成功:" + amount + "元"; } } ``` 这样,我们就可以通过策略模式工厂模式和模板方法模式来实现不同支付方式的接口,使得代码更加灵活、可维护性更好。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值