并发扣减库存不使用分布式锁用幂等性怎么实现

发布时间 2023-07-05 16:28:26作者: 孙龙-程序员

扣减库存接口

 /**
     * Notes:修改或删除库存信息  复核提交,移位完成,调拨完成 后触发
     * 原始库位扣减操作
     * User: sl
     * Date: 2023-04-11 17:31
     * @param $data
     * @param $type 1,复核完成   2调拨出库完成后扣减   3,源库位移位完成后
     * 移位不记录日志
     * @return bool
     **************二位数组*************************
     * [["id"=>"库存id","reduceTotalQty"=>"减少的库存总数量"]]
     *
     * [["id"=>"1","reduceTotalQty"=>"50"]]
     */
    public static function updateOrDelStock($operator,$type,$data=[])
    {
        \Log::channel("stockLock")->info("---------------修改库存信息------------------");
        \Log::channel("stockLock")->info(sprintf("操作人:%s",json_encode($operator,JSON_UNESCAPED_UNICODE)));
        \Log::channel("stockLock")->info(sprintf("参数:%s",json_encode($data)));
        $arr = [];
        foreach($data as $item){
            if(!isset($arr[$item["id"]])){
                $arr[$item["id"]] = 0;
            }
            $arr[$item["id"]] += $item["reduceTotalQty"];
        }
        $stockIds = array_keys($arr);
        $stockList = StockModel::getStockListByids($stockIds);
        $stockList = arrayChangeKeyByField($stockList,"id");
        try{
            self::startTransaction();
            foreach($stockList as $id=>$stockInfo){
                $reduceQty = $arr[$id] ?? 0;
                if($reduceQty <= 0){
                    continue;
                }
                if($stockInfo["total_qty"] < $reduceQty){
                    throw new InvalidRequestException(sprintf("库存id:%s,扣减库存失败,扣减数量不能大于库存总数量",$id));
                }

                if($stockInfo["useable_qty"] < $reduceQty){
                    throw new InvalidRequestException(sprintf("库存id:%s,扣减库存失败,扣减数量不能大于库存可用数量",$id));
                }

                $totalQty = $stockInfo["total_qty"] - $reduceQty;
                $useableQty = $stockInfo["useable_qty"] - $reduceQty;
                //开始扣减库存
                $update=[];
                $update["total_qty"] = $totalQty;
                $update["useable_qty"] = $useableQty;
                $update["update_uid"] = $operator["operator_id"] ?? 0;
                $update["update_name"] = $operator["operator_name"] ?? "";
                $update["update_time"] = time();
                $update["amount"] = \DB::raw("ROUND(purchase_prices*total_qty,2)");
                $update["standard_money_amount"] = \DB::raw("ROUND(standard_money_prices*total_qty,2)");
                $update["purchase_withoutamount"] = \DB::raw("ROUND(purchase_without_tax_price*total_qty,2)");
                StockModel::where("id",$id)->update($update);
//                if($totalQty ==  0  && $useableQty == 0){
//                    //进行库存汇总
//                    self::updateOrCreateStockSummary([$id]);
//                    //删除
//                    StockModel::where("id",$id)->delete();
//
//                }

                //进行库存汇总
                self::updateOrCreateStockSummary([$id]);
                //删除
                if($totalQty ==  0  && $useableQty == 0){
                    StockModel::where("id",$id)->delete();
                }

            }
            self::commitTransaction();
        }catch (\Throwable $e){
            \Log::channel("stockLock")->info(json_encode(ErrMsg::getExceptionInfo($e)));
            self::rollBackTransaction();
            throw new InvalidRequestException($e->getMessage());
        }

    }

上述红色代码是扣减库存,在并发或者重复请求,或者请求超时情况下会出现重复扣减库存的情况,

库存原来只有型号A 8个  现在要扣减8个库存,第一次扣减8个可能请求超时或者其他原因,扣减接口重试了一次,第二次请求又继续扣减库存,上述接口两个扣减都成功了;

 

修改后代码:

/**
     * Notes:修改或删除库存信息  复核提交,移位完成,调拨完成 后触发
     * 原始库位扣减操作
     * User: sl
     * Date: 2023-04-11 17:31
     * @param $data
     * @param $type 1,复核完成   2调拨出库完成后扣减   3,源库位移位完成后
     * 移位不记录日志
     * @return bool
     **************二位数组*************************
     * [["id"=>"库存id","reduceTotalQty"=>"减少的库存总数量"]]
     *
     * [["id"=>"1","reduceTotalQty"=>"50"]]
     */
    public static function updateOrDelStock($operator,$type,$data=[])
    {
        \Log::channel("stockLock")->info("---------------修改库存信息------------------");
        \Log::channel("stockLock")->info(sprintf("操作人:%s",json_encode($operator,JSON_UNESCAPED_UNICODE)));
        \Log::channel("stockLock")->info(sprintf("参数:%s",json_encode($data)));
        $arr = [];
        foreach($data as $item){
            if(!isset($arr[$item["id"]])){
                $arr[$item["id"]] = 0;
            }
            $arr[$item["id"]] += $item["reduceTotalQty"];
        }
        $stockIds = array_keys($arr);
        $stockList = StockModel::getStockListByids($stockIds);
        $stockList = arrayChangeKeyByField($stockList,"id");
        try{
            self::startTransaction();
            foreach($stockList as $id=>$stockInfo){
                $reduceQty = $arr[$id] ?? 0;
                if($reduceQty <= 0){
                    continue;
                }
                if($stockInfo["total_qty"] < $reduceQty){
                    throw new InvalidRequestException(sprintf("库存id:%s,扣减库存失败,扣减数量不能大于库存总数量",$id));
                }

                if($stockInfo["useable_qty"] < $reduceQty){
                    throw new InvalidRequestException(sprintf("库存id:%s,扣减库存失败,扣减数量不能大于库存可用数量",$id));
                }

                $totalQty = $stockInfo["total_qty"] - $reduceQty;
                $useableQty = $stockInfo["useable_qty"] - $reduceQty;
                //开始扣减库存
                $update=[];
                $update["total_qty"] = \DB::raw("total_qty-{$reduceQty}");
                $update["useable_qty"] = \DB::raw("useable_qty-{$reduceQty}");;
                $update["update_uid"] = $operator["operator_id"] ?? 0;
                $update["update_name"] = $operator["operator_name"] ?? "";
                $update["update_time"] = time();
                $update["amount"] = \DB::raw("ROUND(purchase_prices*total_qty,2)");
                $update["standard_money_amount"] = \DB::raw("ROUND(standard_money_prices*total_qty,2)");
                $update["purchase_withoutamount"] = \DB::raw("ROUND(purchase_without_tax_price*total_qty,2)");
                $bk = StockModel::where("id",$id)->where("total_qty",$stockInfo["total_qty"])->where("useable_qty",$stockInfo["useable_qty"])->update($update);
                if(!$bk){
                    throw new InvalidRequestException(sprintf("库存id:%s,扣减库存失败:可能存在相同库存数据重复扣减或者并发扣减情况",$id));
                }

                //进行库存汇总
                self::updateOrCreateStockSummary([$id]);
                //删除
                if($totalQty ==  0  && $useableQty == 0){
                    StockModel::where("id",$id)->delete();
                }

            }
            self::commitTransaction();
        }catch (\Throwable $e){
            \Log::channel("stockLock")->info(json_encode(ErrMsg::getExceptionInfo($e)));
            self::rollBackTransaction();
            throw new InvalidRequestException($e->getMessage());
        }

    }