// +---------------------------------------------------------------------- declare (strict_types=1); namespace app\common\service\commission; use app\api\model\ConfigPercent; use app\api\model\Order; use app\api\model\User; use app\api\model\user\CommissionsDetail; use app\common\library\helper; use app\common\model\GoodsSku; use app\common\model\ShopIdentity; use app\common\model\Shops; use app\common\model\store\Setting; use app\common\service\BaseService; use app\common\service\Message; use think\db\exception\DataNotFoundException; use think\db\exception\DbException; use think\db\exception\ModelNotFoundException; use think\facade\Db; use think\facade\Log; use think\facade\Queue; /** * 待结算佣金服务类 * 1.待分佣记录是用队列写入,2.分佣结算是定时任务,30秒清算一次,3.每日奖励金是执行计划crontab,每天凌晨一次,4.每月奖励金结算是执行计划crontab在次月的20日凌晨结算 * Class Complete * @package app\common\service\order */ class RecordWaitCommission extends BaseService { /** * 获取门店参与分佣的人与角色 * @param $shopId * @param $staffId * @return array * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ private function getShopUser($shopId,$staffId): ?array { $md = new ShopIdentity(); $pers = $md->field('shop_id,role_id as role,fc_percent as percent')->where('shop_id',$shopId)->select(); $pers = $pers?$pers->toArray():[]; $shop = Shops::find($shopId); if(!$shop)return null; $roles = array_column($pers,'role'); //获取新增身份的用户 $cUsers = \app\common\model\User::getCommissionUsers($shopId,$roles); //加入店老板分佣比例 $pers[] = ['shop_id'=>$shopId,'role'=>User::SHOP_BOSS,'percent'=>$shop->boss_percent]; $pers[] = ['shop_id'=>$shopId,'role'=>User::SHOP_MG,'percent'=>$shop->manager_percent]; //加入店员分佣比例 $pers[] = ['shop_id'=>$shopId,'role'=>User::SHOP_SELLER,'percent'=>$shop->staff_percent]; $pers = array_column($pers,null,'role'); //加入店员 $cUsers[] = ['user_id'=>$shop->boss_user_id,'role'=>User::SHOP_BOSS]; if ($shop->manager_user_id){ $cUsers[] = ['user_id'=>$shop->manager_user_id,'role'=>User::SHOP_MG]; } $cUsers[] = ['user_id'=>$staffId,'role'=>User::SHOP_SELLER]; return ['pers'=>$pers,'cUsers'=>$cUsers]; } /** * Notes:计算商品预计分佣 * Author: zhangs * DateTime: 2021/10/9 11:09 * @param $goodsInfo * @param $userInfo * @return string */ public function calcFcYjAmount($goodsInfo, $userInfo) { if (!$userInfo) { return '0.00'; } $goodsSku = GoodsSku::where('goods_id', $goodsInfo['goods_id'])->order('goods_price', 'asc')->find(); $percents = ConfigPercent::find(1); $ticketRate = $percents->ticket_rate; $platformRate = $goodsSku['platform_rate'] > 0 ? $goodsSku['platform_rate'] : $percents->platform_rate; $staff_percent = $percents->getAttr('staff_percent'); // 店员 $manager_percent = $percents->getAttr('manager_percent'); // 店长 $boss_percent = $percents->getAttr('boss_percent'); // 店老板 $pool = ($goodsInfo['goods_price_min'] - $goodsSku['clearing_price'] - $goodsInfo['goods_price_min']*$ticketRate/100)*$platformRate/100; $money = '0.00'; if ($userInfo->role == User::SHOP_SELLER) { // 推荐人是店员 $money = helper::bcadd($pool * $staff_percent/100,0,2); } elseif ($userInfo->role == User::SHOP_MG) { // 推荐人是店长 // $money = helper::bcadd($pool * $manager_percent/100,0,2); } elseif ($userInfo->role == User::SHOP_BOSS) { // 推荐人是店老板 // $money = helper::bcadd($pool * $boss_percent/100,0,2); } return $money; } /** * 待结算店员分销佣金 * @param $pool * @param $cUsers * @param $percents * @param $orderId * @param $shopId * @param $buyerId * @param $orderCreateTime * @return bool * @throws \Exception */ private function handleWaitShopSellerCommission($pool,$cUsers,$percents,$orderId,$shopId,$buyerId,$orderCreateTime): bool { $data = []; foreach ($cUsers as $cUser){ $percent = $percents[$cUser['role']]['percent']; $money = helper::bcadd($pool * $percent/100,0,4); $data[] = ['user_id'=>$cUser['user_id'], 'clearing_money'=>$money, 'order_id'=>$orderId, 'shop_id'=>$shopId, 'role'=>$cUser['role'], 'buyer_user_id'=>$buyerId, 'commission_percent'=>$percent, 'order_create_time'=>$orderCreateTime, 'order_sale_volume'=>$pool ]; } if (count($data)){ $model = new \app\common\model\user\CommissionsDetail(); $model->saveAll($data); } return true; } /** * 订单支付回调成功时,将待分佣明细job加入队列执行 * @param $orderId * @return bool */ public function recordWaitCommission($orderId){ $jobHandlerClassName = 'app\job\recordWaitCommissionsDetail'; $jobQueueName = "orderWaitCommissionsQueue"; //数组数据 $orderData = [ 'orderId' => $orderId, ]; $isPushed = Queue::push($jobHandlerClassName, $orderData ,$jobQueueName); //$isPushed = Queue::later(5, $jobHandlerClassName, $orderData, $jobQueueName); if( $isPushed !== false ){ return true; }else{ log_record('加队列失败,orderId:'.$orderId); return false; } } //todo 1.下单时订单商品快照表需要记录分享者的id, //todo 2.需要在下单成功后变更用户的推荐人信息和门店信息 /** * 待结算佣金计算,需要在支付回调完成时调用这个方法去记录待结算的佣金详情,在售后期结束后调用结算 * @param $orderId * @return bool * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ public function waitGiveOutCommission(int $orderId){ Log::error('Job wait orderId:'.$orderId); $order = Order::where(['order_id'=>$orderId,'commission_settlement_status'=>10])->find(); if (empty($order)){ return true; } $orderCreateTime = strtotime($order['create_time']); //如果订单退单退货,不执行分佣 $buyerId = $order->user_id; $ratios = Setting::getItem('distributor_grade', 10001)['distributor']; $buyerUser = User::where('is_delete',0)->find($buyerId); Db::startTrans(); try{ //商品加入购物车时需要记录分享者id,商品推荐人 $staffUserId = (int)$order['staff_user_id']; if ($staffUserId == $buyerId){ throwError('自分享无分佣'); } //pool = 实付金额 + 现金米卡抵扣金额 - 运费 $poolTemp = helper::bcadd($order['pay_price'] , $order['rice_card_money'],4); $pool = helper::bcsub($poolTemp , $order['express_price'],4); if ($pool <= 0){ throwError('金额有误'); } Log::info('Job orderId:'.$orderId.',pool:'.$pool.',staffUserId:'.$staffUserId); //如果商品有推荐人 $staffUser = null; $shopId = 0; if ($staffUserId > 0){ $shopId = $order['staff_shop_id']; $staffUser = User::where('is_delete',0) ->field('user_id,role,shop_id,upper_user_id,seller_grade') ->find($staffUserId); if (!$staffUser){ throwError('推广人用户不存在A'); } }else{ //店长、店老板、店内新增身份,店员自己购买都不分佣 if (in_array($buyerUser->role,[2,3,4,5])){ throwError('买家角色不支持分佣'); } if (in_array($buyerUser->role,[1,99]) && isset($buyerUser->upper_user_id) && $buyerUser->upper_user_id > 0 ){ $staffUser = User::where('is_delete',0) ->field('user_id,role,shop_id,upper_user_id,seller_grade') ->find($buyerUser->upper_user_id); if (!$staffUser){ throwError('推广人用户不存在B'); } if (!in_array($staffUser->role,[User::COMMISSION_USER,User::SHOP_SELLER])){ throwError('推广人不支持分佣'); } $shopId = $staffUser['shop_id']; }else{ throwError('买家无上级A'); } } if (empty($staffUser)){ throwError('买家无上级B'); } if ($staffUser->role == User::SHOP_SELLER){ $flag = $this->getShopUser($shopId,$staffUser->user_id); if (!$flag){ throwError('门店员工获取异常,shopId:'.$shopId); } $cUsers = $flag['cUsers']; $percents = $flag['pers']; $flag1 = $this->handleWaitShopSellerCommission($pool,$cUsers,$percents,$orderId,$shopId,$buyerId,$orderCreateTime); if (!$flag1){ throwError('写门店分佣失败'); } } if ($staffUser->role == User::COMMISSION_USER){ $levelOneRate = $ratios[$staffUser->seller_grade-1]['first_commission']??0; $money = helper::bcadd($pool * $levelOneRate/100,0,4); //dd($order); if (CommissionsDetail::addNewCommission($staffUser->user_id,$orderId,$orderCreateTime,$money,0,$buyerId,User::COMMISSION_USER,1,$staffUser->seller_grade,$levelOneRate,$pool) === false){ throwError('写分销员一级分佣失败'); } //二级佣金 if ($staffUser->upper_user_id >0){ $secondUser = \app\common\model\User::where('is_delete',0)->find($staffUser->upper_user_id); if ($secondUser && $secondUser->role == User::COMMISSION_USER){ $levelTwoRate = $ratios[$secondUser->seller_grade-1]['second_commission']??0; $money1 = helper::bcadd($pool * $levelTwoRate/100,0,4); if ( CommissionsDetail::addNewCommission($staffUser->upper_user_id,$orderId,$orderCreateTime,$money1,0,$buyerId,User::COMMISSION_USER,2,$secondUser->seller_grade,$levelTwoRate,$pool) === false){ throwError('写分销员二级分佣失败'); } } } } //订单佣金结算状态改为待结算 $order->commission_settlement_status = 0; $order->save(); Db::commit(); }catch(\Exception $e){ Db::rollback(); $msg = 'Job orderId:'.$orderId.':'.$e->getMessage(); Log::error($msg); Message::wxRobot($msg); return false; } return true; } }