ownerGetIds($params["owner_id"]); if ($ids)$builder->whereIn("id",$ids); unset($params["owner_id"]); } $columnQueryRules = [ "name" => ["like"=>""] ]; return app(QueryService::class)->query($params, $builder, $columnQueryRules); } public function paginate(array $params, array $withs = []) { return $this->query(OwnerPriceOperation::query()->orderByDesc('id')->with($withs),$params) ->paginate($params["paginate"] ?? 50); } private function ownerGetIds(string $owner_id) { if (!$owner_id)return []; $arr = DB::select(DB::raw("SELECT owner_price_operation_id AS id FROM owner_price_operation_owner WHERE owner_id in (".$owner_id.")")); return array_column($arr,"id"); } public function destroy($id) { OwnerPriceOperationItem::query()->where("owner_price_operation_id",$id)->delete(); DB::table("owner_price_operation_owner")->where("owner_price_operation_id",$id)->delete(); return OwnerPriceOperation::destroy($id); } /** * @param array $params * @return Model */ public function create(array $params) { return OwnerPriceOperation::query()->create($params); } public function insertItem(array $params) { OwnerPriceOperationItem::query()->insert($params); } public function find($id, $withs = []) { $query = OwnerPriceOperation::query()->with($withs)->find($id); return $query; } public function destroyItem($id) { return OwnerPriceOperationItem::query()->where("owner_price_operation_id",$id)->delete(); } public function findUpdate(OwnerPriceOperation $model, array $params) { return $model->update($params); } /** 参数顺序: 数量 匹配对象 列映射 货主ID 单位ID 类型 SKU . * 匹配顺序: 类型 货主 策略 单位 特征 ..多对多匹配规则废弃,1对1,设单位必定为件,对应规则必然只有一项存在 * 单位匹配: 件,箱 由小到大,依次换算匹配 . * * 2:没有总数量存在,都为子项内数量 * * @param array|object $matchObject key-val * @param array $columnMapping key-val * @param string $owner_id * @param string $type * @return double|array * 错误代码: -1:无匹配对象 -2:无计费模型 -3:未知单位 -4:sku为空 -5:货主未找到 -6:无箱规 -7:未匹配到计费模型 * * 一. 2020-10-10 zzd * 二. 2021-01-08 zzd * 三. 2021-01-28 zzd * 增加满减策略:子策略匹配时不再考虑单,仅件箱换算,满减满足后标记模型修改历史对账单 * 增加按订单计价策略:主匹配模型增加字段量价,该字段存在时视为按单计价,价格为该值 */ public function matching($matchObject, $columnMapping, $owner_id, $type = '出库') { $unitModels = Unit::query()->whereIn("name",["件","箱"])->get(); $units = []; foreach ($unitModels as $unitModel)$units[$unitModel->id] = $unitModel->name; $rules = OwnerPriceOperation::query()->with(["items"=>function($query){ /** @var Builder $query */ $query->orderByRaw("CASE strategy WHEN '起步' THEN 1 WHEN '默认' THEN 2 WHEN '特征' THEN 3 END DESC,priority"); }])->where("operation_type",$type)->whereHas("ownerPriceOperationOwners",function ($query)use($owner_id){ /** @var Builder $query */ $query->where("id",$owner_id); })->orderByRaw("strategy desc,priority desc")->get(); //货主下的全部规则 if (!$rules)return -2; //规则不存在跳出 $total = Cache::get(date("Y-m")."_".$matchObject["owner_id"]); //获取该货主本月单量 foreach ($rules as $rule){ if (!$rule->items)continue; //不存在子规则跳出 $isDiscount = false; //是否存在满减 if ($type=='出库' && $rule->discount_count > 0 && $total >= $rule->discount_count)$isDiscount = true;//满足满减条件 //满减存在且未被标记过处理时间或处理时间不为本月,处理历史即时账单 if ($isDiscount && (!$rule->discount_date || substr($rule->discount_date,0,7)!=date("Y-m"))){ try{ DB::beginTransaction(); $month = date("Y-m"); $day = date("t",strtotime($month)); foreach (OwnerFeeDetail::query()->with(["order.logistic","order.shop","order.packages.commodities.commodity"]) ->where("owner_id",$owner_id) ->whereBetween("worked_at",[$month."-01",$month."-".$day])->get() as $detail){ $order = $detail->order; $logistic_fee = 0; $commodities = []; foreach ($order->packages as &$package){ // 四维转二维 foreach($package->commodities as &$commodity){ $commodity["commodity_name"] = $commodity->commodity ? $commodity->commodity->name : ''; $commodity["sku"] = $commodity->commodity ? $commodity->commodity->sku : ''; } $commodities = array_merge($commodities,$package->commodities->toArray()); } if ($logistic_fee!==null && $logistic_fee<0)$logistic_fee = null; $object = ["commodities"=>$commodities, "logistic_name"=>($order->logistic ? $order->logistic->name : ''), "shop_name"=>($order->shop ? $order->shop->name : ''), "order_type"=>$order->order_type, "owner_id"=>$order->owner_id]; $mapping = ["packages"=>"commodities","商品名称"=>"commodity_name", "承运商"=>"logistic_name", "店铺类型"=>"shop_name", "订单类型"=>"order_type","订单数"=>"amount"]; $money = $this->matchItem($rule->items,$mapping,$object,$units,$owner_id,'出库',$isDiscount); if ($money>0)$detail->update(["work_fee"=>$money]); else LogService::log(__CLASS__,"处理历史即时账单时发生匹配错误","账单主键:".$detail->id."; 错误代码".$money); }; $rule->update(["discount_date"=>date("Y-m-d")]); DB::commit(); }catch (\Exception $e){ DB::rollBack(); LogService::log(__CLASS__,"处理历史即时账单时发生系统错误","计费模型主键:".$rule->id."; 错误信息".$e->getMessage()); } } if ($rule->strategy == '特征'){//特征策略匹配 $bool = app("FeatureService")->matchFeature($rule->feature,$columnMapping,$matchObject); if ($bool === true){ if ($rule->total_price)return $rule->total_price; //按单计价存在,直接返回单总价 $money = $this->matchItem($rule->items,$columnMapping,$matchObject,$units,$owner_id,$type=='入库' ? true : false,$isDiscount); if ($money>0)return ["id"=>$rule->id,"money"=>$money]; }; }else{//默认策略匹配 if ($rule->total_price)return $rule->total_price; //按单计价存在,直接返回单总价 $money = $this->matchItem($rule->items,$columnMapping,$matchObject,$units,$owner_id,$type=='入库' ? true : false,$isDiscount); if ($money>0)return ["id"=>$rule->id,"money"=>$money]; }; } return $money ?? -7; } /** * 根据货主 sku寻找箱规并将指定数量切换为箱 * 不满一箱视为一箱 * * @param integer $amount * @param integer $owner_id * @param string $sku * * @return int */ private function changeUnit($amount,$owner_id,$sku) { if (!$sku)return -4; $pack = app("CommodityService")->getPack($owner_id,$sku); if (!$pack)return -6; return ceil($amount/$pack); } /** * 匹配子策略 * * @param array $rules 策略对象组 * @param array $columnMapping 映射对象 * @param array $matchObject 被匹配对象 * @param array $units 单位集 * @param integer $owner_id 货主ID * @param bool $isIn 是否为入库单 * @param bool $isDiscount 是否为满减单 * * @return double */ private function matchItem($rules, $columnMapping, $matchObject, $units, $owner_id, $isIn, $isDiscount) { $amountColumn = $columnMapping["amount"] ?? "amount"; $packageColumn = $columnMapping["packages"] ?? "packages"; $packages = $matchObject[$packageColumn] ?? false; $commodityColumn = $columnMapping["商品名称"] ?? 'commodity'; if (!$packages)return -1; $unitName = ""; foreach ($rules as $rule){ if ($isDiscount)$rule->unit_price = $rule->discount_price; //满足满减条件,单价调整为满减单价 switch ($rule->strategy){ case "特征": $inMoney = 0; foreach ($packages as &$package){ if ($package["price"] ?? false)continue; if (!app("FeatureService")->matchFeature($rule->feature,["商品名称"=>$commodityColumn],["commodity"=>$package[$commodityColumn] ?? ''])) continue; if (!$unitName)$unitName = $units[$rule->unit_id]; else { if ($unitName != $units[$rule->unit_id]) return -3; } $package["price"] = $rule->unit_price; if (!isset($units[$rule->unit_id]))return -3; if ($units[$rule->unit_id] == '箱'){ //为箱时同步商品寻找箱规 $sumTemp = 0; $packageColumn = $columnMapping["packages"] ?? "packages"; foreach ($matchObject[$packageColumn] as $commodity){ $sumTemp += $this->changeUnit($package[$amountColumn],$owner_id,$commodity["sku"]); } $amount = $sumTemp; if ($amount<0)return $amount; $package[$amountColumn] = $amount; } $inMoney += $package[$amountColumn] * $package["price"]; } if ($isIn && $inMoney !== 0){ return $inMoney; } break; case "默认": $inMoney = 0; foreach ($packages as &$package){ if ($package["price"] ?? false)continue; //校验是否已匹配到 if (!$unitName)$unitName = $units[$rule->unit_id]; //校验单位是否一致 else { if ($unitName != $units[$rule->unit_id]) return -3; } $package["price"] = $rule->unit_price; if (!isset($units[$rule->unit_id]))return -3; if ($units[$rule->unit_id] == '箱'){ //为箱时同步商品寻找箱规 $sumTemp = 0; $packageColumn = $columnMapping["packages"] ?? "packages"; foreach ($matchObject[$packageColumn] as $commodity){ $sumTemp += $this->changeUnit($package[$amountColumn],$owner_id,$commodity["sku"]); } $amount = $sumTemp; if ($amount<0)return $amount; $package[$amountColumn] = $amount; } $inMoney += $package[$amountColumn] * $package["price"]; } if ($isIn && $inMoney !== 0){ return $inMoney; } break; default: if ($isIn)break; //入库不计算起步 if ($rule->amount){ //起步数+起步费 if ($unitName && $unitName != $units[$rule->unit_id])return -3; //校验单位是否一致 $money = $rule->unit_price; $startNumber = $rule->amount; $packages = $this->settingCount($packages,$amountColumn,$startNumber); if ($packages){ foreach ($packages as $package){ $money += $package[$amountColumn] * $package["price"]; } } }else{//单起步费 $money = 0; if ($packages){ foreach ($packages as $package){ $money += $package[$amountColumn] * $package["price"]; } } if ($money<$rule->unit_price)$money = $rule->unit_price; } return $money; } } return -7; } //递归式重新设置数量 private function settingCount($packages,$amountColumn,$startNumber) { if (!$packages) return null; $maxPrice = 0; $index = null; foreach ($packages as $i => $package){ if ($package[$amountColumn] <= 0){ unset($packages[$i]);continue; } if ($package["price"] > $maxPrice){ $maxPrice = $package["price"]; $index = $i; } } if ($packages[$index][$amountColumn] >= $startNumber){ $packages[$index][$amountColumn] -= $startNumber; return $packages; }else{ $startNumber -= $packages[$index][$amountColumn]; unset($packages[$index]); $this->settingCount($packages,$amountColumn,$startNumber); } } }