// +---------------------------------------------------------------------- namespace app\services\order; use app\dao\order\StoreOrderDao; use app\services\activity\lottery\LuckLotteryServices; use app\services\activity\combination\StorePinkServices; use app\services\BaseServices; use app\services\pay\PayServices; use app\services\user\UserServices; use app\services\product\product\StoreProductGiftGroupService; use app\services\user\UserBrokerageServices; use crmeb\exceptions\ApiException; /** * Class StoreOrderSuccessServices * @package app\services\order * @method getOne(array $where, ?string $field = '*', array $with = []) 获取去一条数据 */ class StoreOrderSuccessServices extends BaseServices { /** * * StoreOrderSuccessServices constructor. * @param StoreOrderDao $dao */ public function __construct(StoreOrderDao $dao) { $this->dao = $dao; } /** * 0元支付 * @param array $orderInfo * @param int $uid * @return bool * @throws \think\Exception * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public function zeroYuanPayment(array $orderInfo, int $uid, string $payType = PayServices::YUE_PAY) { if ($orderInfo['paid']) { throw new ApiException(410265); } return $this->paySuccess($orderInfo, $payType);//余额支付成功 } /** * 支付成功 * @param array $orderInfo * @param string $paytype * @param array $other * @return bool * @throws \Psr\SimpleCache\InvalidArgumentException * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public function paySuccess(array $orderInfo, string $paytype = PayServices::WEIXIN_PAY, array $other = []) { $updata = ['paid' => 1, 'pay_type' => $paytype, 'pay_time' => time()]; $orderInfo['pay_time'] = $updata['pay_time']; $orderInfo['pay_type'] = $paytype; if ($other && isset($other['trade_no'])) { $updata['trade_no'] = $other['trade_no']; } /** @var StoreOrderCartInfoServices $orderInfoServices */ $orderInfoServices = app()->make(StoreOrderCartInfoServices::class); $orderInfo['storeName'] = $orderInfoServices->getCarIdByProductTitle((int)$orderInfo['id']); $res1 = $this->dao->update($orderInfo['id'], $updata); $resPink = true; if ($orderInfo['combination_id'] && $res1 && !$orderInfo['refund_status']) { /** @var StorePinkServices $pinkServices */ $pinkServices = app()->make(StorePinkServices::class); /** @var StoreOrderServices $orderServices */ $orderServices = app()->make(StoreOrderServices::class); $resPink = $pinkServices->createPink($orderServices->tidyOrder($orderInfo, true));//创建拼团 } //缓存抽奖次数 除过线下支付 if (isset($orderInfo['pay_type']) && $orderInfo['pay_type'] != 'offline') { /** @var LuckLotteryServices $luckLotteryServices */ $luckLotteryServices = app()->make(LuckLotteryServices::class); $luckLotteryServices->setCacheLotteryNum((int)$orderInfo['uid'], 'order'); } $orderInfo['send_name'] = $orderInfo['real_name']; //订单支付成功后置事件 event('OrderPaySuccessListener', [$orderInfo]); //用户推送消息事件 event('NoticeListener', [$orderInfo, 'order_pay_success']); //支付成功给客服发送消息 event('NoticeListener', [$orderInfo, 'admin_pay_success_code']); // 推送订单 event('OutPushListener', ['order_pay_push', ['order_id' => (int)$orderInfo['id']]]); //自定义消息-订单支付成功 $orderInfo['time'] = date('Y-m-d H:i:s'); $orderInfo['phone'] = $orderInfo['user_phone']; event('CustomNoticeListener', [$orderInfo['uid'], $orderInfo, 'order_pay_success']); //自定义事件-订单支付 event('CustomEventListener', ['order_pay', [ 'uid' => $orderInfo['uid'], 'id' => (int)$orderInfo['id'], 'order_id' => $orderInfo['order_id'], 'real_name' => $orderInfo['real_name'], 'user_phone' => $orderInfo['user_phone'], 'user_address' => $orderInfo['user_address'], 'total_num' => $orderInfo['total_num'], 'pay_price' => $orderInfo['pay_price'], 'pay_postage' => $orderInfo['pay_postage'], 'deduction_price' => $orderInfo['deduction_price'], 'coupon_price' => $orderInfo['coupon_price'], 'store_name' => $orderInfo['storeName'], 'add_time' => date('Y-m-d H:i:s', $orderInfo['add_time']), ]]); // 获取购物车信息 $orderCartInfo = $orderInfoServices->getCartColunm( ['oid' => $orderInfo['id']], 'cart_info', 'unique' ); $orderInfo['cart_info'] = []; foreach ($orderCartInfo as $k => $v) { $cart_info = is_string($v) ? json_decode($v, true) : $v; $orderInfo['cart_info'][] = $cart_info; } if ($orderInfo['lb_spread_uid'] > 0) { //建立礼包推荐关系 @file_put_contents('quanju.txt', $orderInfo['lb_spread_uid'] . "-礼包推荐人2\r\n", 8); self::createGiftRecommendationRelationship($orderInfo); self::giftRecommendationBonus($orderInfo); } // if ($orderInfo['is_lb'] == 1) { //赠送礼包推荐奖金 // @file_put_contents('quanju.txt', $orderInfo['is_lb'] . "-礼包订单2\r\n", 8); // // } $res = $res1 && $resPink; return false !== $res; } // 创建礼包推荐关系 public function createGiftRecommendationRelationship($order) { // @file_put_contents('quanju.txt', json_encode($order) . "-创建礼包推荐关系\r\n", 8); try { // 获取用户信息 $userServices = app()->make(UserServices::class); $user = $userServices->getUserInfo($order['uid']); @file_put_contents('quanju.txt', $user['spread_uid'] . "-创建礼包推荐关系\r\n", 8); if ($user['spread_uid'] > 0) { // 如果用户有推广人,说明是通过推广链接支付的,需要创建礼包推荐关系,关系表是store_product_gift_group,需要保存product_id,uid,group_pid(小组上级),pid(真直推上级),fake_pid(假直推上级(直推上级的下级小组满人下放下来的人)),一个商品加一个pid推荐上级会形成一个小组,第一个加进小组的会是第二个加入的小组上级(group_pid),当一个小组满10人后,就会让第十人作为fake_pid直推上级建立新的10人小组,但pid还是真正的直推上级 // 获取商品ID $product_id = $order['cart_info'][0]['product_id'] ?? 0; if (!$product_id) return false; // 获取推广人UID $spread_uid = $user['spread_uid']; // 获取礼包服务 $giftGroupService = app()->make(StoreProductGiftGroupService::class); // 检查是否已有该商品的推荐关系 $existRelation = $giftGroupService->getOne([ 'product_id' => $product_id, 'uid' => $order['uid'] ]); if ($existRelation) return true; // 检查推广人是否是自己的下级团队成员(防止循环推荐) if ($this->isSubordinate($order['uid'], $spread_uid, $product_id, $giftGroupService)) { return false; } // 获取直推上级的推荐关系 $parentRelation = $giftGroupService->getOne([ 'product_id' => $product_id, 'uid' => $spread_uid ]); // 设置关系数据 $data = [ 'product_id' => $product_id, 'uid' => $order['uid'], 'pid' => $spread_uid, // 真直推上级 'create_time' => time() ]; // 处理小组关系 // 获取小组人数上限配置 $groupMaxNum = sys_config('gift_group_num', 10); // 判断直推上级在该商品下是否有下级 $hasSubordinate = $giftGroupService->getOne([ 'product_id' => $product_id, 'pid' => $spread_uid ]); if ($hasSubordinate) { // 直推上级已有下级,获取所有下级 $groupMembers = $giftGroupService->getList([ 'product_id' => $product_id, 'pid' => $spread_uid ], '*', 0, 0, 'create_time ASC'); // 判断小组是否满人 if (count($groupMembers) >= $groupMaxNum) { // 小组满人,从后往前查找未满的直推下级小组 $foundGroup = false; // 倒序遍历小组成员,查找未满的直推下级小组 for ($i = count($groupMembers) - 1; $i >= 0; $i--) { $member = $groupMembers[$i]; // 查找该成员的直推下级 $subGroupMembers = $giftGroupService->getList([ 'product_id' => $product_id, 'pid' => $member['uid'] ], '*', 0, 0, 'create_time ASC'); // 如果该成员没有下级,或者下级小组未满,可以加入 if (count($subGroupMembers) < $groupMaxNum) { $data['group_pid'] = $member['uid']; $data['fake_pid'] = $spread_uid; $foundGroup = true; break; } } // 如果所有成员的直推下级小组都满了,建立新小组 if (!$foundGroup) { $lastMember = $groupMembers[count($groupMembers) - 1]; $data['group_pid'] = 0; $data['fake_pid'] = $lastMember['uid']; } } else { // 小组未满,新成员的group_pid是最后一个下级 $lastMember = $groupMembers[count($groupMembers) - 1]; $data['group_pid'] = $lastMember['uid']; $data['fake_pid'] = $spread_uid; } } else { // 直推上级没有下级,这是该推广人的第一个下级,建立新小组 $data['group_pid'] = 0; $data['fake_pid'] = $spread_uid; } // 保存推荐关系 return $giftGroupService->save($data); } return false; } catch (\Exception $e) { @file_put_contents('quanju4.txt', $e->getMessage() . "-创建订单报错内容\r\n", 8); @file_put_contents('quanju4.txt', $e->getFile() . "-文件\r\n", 8); @file_put_contents('quanju4.txt', $e->getLine() . "-位置\r\n", 8); @file_put_contents('quanju4.txt', $e->getTraceAsString() . "-堆栈\r\n", 8); } } // 礼包上级分红 public function giftRecommendationBonus($order) { try { @file_put_contents('quanju.txt', json_encode($order) . "-礼包上级分红\r\n", 8); // 获取礼包服务 $giftGroupService = app()->make(StoreProductGiftGroupService::class); $userServices = app()->make(UserServices::class); // 检查 cart_info 是否存在且非空 @file_put_contents('quanju4.txt', '开始检查cart_info' . "\r\n", 8); if (!isset($order['cart_info']) || empty($order['cart_info'])) { @file_put_contents('quanju4.txt', 'cart_info不存在或为空' . "\r\n", 8); return false; } @file_put_contents('quanju4.txt', 'cart_info存在: ' . json_encode($order['cart_info'][0]) . "\r\n", 8); // 获取商品ID和商品价格作为佣金计算基数 $product_id = $order['cart_info'][0]['product_id'] ?? 0; $brokerage_price = $order['cart_info'][0]['truePrice'] ?? 0; @file_put_contents('quanju4.txt', 'product_id=' . $product_id . ', brokerage_price=' . $brokerage_price . "\r\n", 8); if (!$product_id || !$brokerage_price) { @file_put_contents('quanju4.txt', 'product_id或brokerage_price为0,返回false' . "\r\n", 8); return false; } @file_put_contents('quanju4.txt', $product_id . "-商品id\r\n", 8); // 获取用户的推荐关系 $userRelation = $giftGroupService->getOne([ 'product_id' => $product_id, 'uid' => $order['uid'] ]); if (!$userRelation) return false; $userInfo = $userServices->getOne(['uid' => $order['uid']]); // 获取佣金服务(一次性创建,复用) /** @var UserBrokerageServices $userBrokerageServices */ $userBrokerageServices = app()->make(UserBrokerageServices::class); //冻结时间 $frozenTime = time() + intval(sys_config('extract_time')) * 86400; // 直推上级分成 $directBrokerage = $brokerage_price * (sys_config('gift_direct_referral', 10) / 100); @file_put_contents('quanju4.txt', json_encode($userRelation) . "-礼包推荐关系表\r\n", 8); if ($userRelation['pid'] == $userRelation['fake_pid']) { // 真直推上级和假直推上级相同,全部给直推上级 $pid_brokerage = $userServices->getOne(['uid' => $userRelation['pid']]); $userServices->bcInc($userRelation['pid'], 'brokerage_price', $directBrokerage, 'uid'); @file_put_contents('quanju4.txt', $directBrokerage . "-直推奖励\r\n", 8); $balance = bcadd($pid_brokerage['brokerage_price'], $directBrokerage, 2); $userBrokerageServices->income('get_direct_referral_brokerage', $userRelation['pid'], [ 'nickname' => $userInfo['nickname'], 'pay_price' => floatval($order['pay_price']), 'number' => $directBrokerage, 'frozen_time' => $frozenTime ], $balance, $order['id']); } else { // 真直推上级和假直推上级不同,各分50% $halfBrokerage = $directBrokerage * (sys_config('gift_spread_spot_bonus', 50) / 100); // 真上级分得50% $pid_brokerage = $userServices->getOne(['uid' => $userRelation['pid']]); $userServices->bcInc($userRelation['pid'], 'brokerage_price', $halfBrokerage, 'uid'); $balance = bcadd($pid_brokerage['brokerage_price'], $halfBrokerage, 2); $userBrokerageServices->income('get_direct_referral_brokerage', $userRelation['pid'], [ 'nickname' => $userInfo['nickname'], 'pay_price' => floatval($order['pay_price']), 'number' => $halfBrokerage, 'frozen_time' => $frozenTime ], $balance, $order['id']); // 假上级分得50% $fake_pid_brokerage = $userServices->getOne(['uid' => $userRelation['fake_pid']]); $userServices->bcInc($userRelation['fake_pid'], 'brokerage_price', $halfBrokerage, 'uid'); $balance = bcadd($fake_pid_brokerage['brokerage_price'], $halfBrokerage, 2); $userBrokerageServices->income('get_direct_referral_brokerage', $userRelation['fake_pid'], [ 'nickname' => $userInfo['nickname'], 'pay_price' => floatval($order['pay_price']), 'number' => $halfBrokerage, 'frozen_time' => $frozenTime ], $balance, $order['id']); } // 见点奖处理 $spotBrokerage = $brokerage_price * (sys_config('gift_spot_bonus', 2.5) / 100); $totalSpots = 0; // 1. 真直推链上找15人 $currentUid = $userRelation['pid']; for ($i = 0; $i < sys_config('gift_spot_bonus_up', 15); $i++) { $parentRelation = $giftGroupService->getOne([ 'product_id' => $product_id, 'uid' => $currentUid ]); if (!$parentRelation) break; $currentBrokerage = $userServices->getOne(['uid' => $currentUid]); $userServices->bcInc($currentUid, 'brokerage_price', $spotBrokerage, 'uid'); $balance = bcadd($currentBrokerage['brokerage_price'], $spotBrokerage); $userBrokerageServices->income('get_spot_brokerage', $currentUid, [ 'nickname' => $userInfo['nickname'], 'pay_price' => floatval($order['pay_price']), 'number' => $spotBrokerage, 'frozen_time' => $frozenTime ], $balance, $order['id']); $totalSpots++; $currentUid = $parentRelation['pid']; } // 2. 同组下找5人(根据pid查找链式成员) $chainMembers = $giftGroupService->getGroupMembersByPid($product_id, $userRelation['pid']); $currentIndex = array_search($order['uid'], array_column($chainMembers, 'uid')); if ($currentIndex !== false) { @file_put_contents('quanju4.txt', '找到当前用户在链中的位置: ' . $currentIndex . ', 链成员总数: ' . count($chainMembers) . "\r\n", 8); // 从当前用户的下一个位置开始找,最多找5人 for ($i = $currentIndex + 1; $i < count($chainMembers) && $totalSpots < sys_config('gift_spot_bonus_down', 5); $i++) { $memberBrokerage = $userServices->getOne(['uid' => $chainMembers[$i]['uid']]); $userServices->bcInc($chainMembers[$i]['uid'], 'brokerage_price', $spotBrokerage, 'uid'); $balance = bcadd($memberBrokerage['brokerage_price'], $spotBrokerage); $userBrokerageServices->income('get_spot_brokerage', $chainMembers[$i]['uid'], [ 'nickname' => $userInfo['nickname'], 'pay_price' => floatval($order['pay_price']), 'number' => $spotBrokerage, 'frozen_time' => $frozenTime ], $balance, $order['id']); @file_put_contents('quanju4.txt', '发放见点奖给链成员: uid=' . $chainMembers[$i]['uid'] . ', 金额=' . $spotBrokerage . "\r\n", 8); $totalSpots++; } @file_put_contents('quanju4.txt', '链式发放完成,共发放: ' . $totalSpots . '人' . "\r\n", 8); } else { @file_put_contents('quanju4.txt', '未找到当前用户在链中的位置' . "\r\n", 8); } // 3. 如果同组下级不足5人,找自己的直推下级 if ($totalSpots < sys_config('gift_spot_bonus_down', 5)) { $directSubordinates = $giftGroupService->getList([ 'product_id' => $product_id, 'pid' => $order['uid'] ], '*', 0, sys_config('gift_spot_bonus_down', 5) - $totalSpots, 'create_time asc'); foreach ($directSubordinates as $subordinate) { $subBrokerage = $userServices->getOne(['uid' => $subordinate['uid']]); $userServices->bcInc($subordinate['uid'], 'brokerage_price', $spotBrokerage, 'uid'); $balance = bcadd($subBrokerage['brokerage_price'], $spotBrokerage); $userBrokerageServices->income('get_spot_brokerage', $subordinate['uid'], [ 'nickname' => $userInfo['nickname'], 'pay_price' => floatval($order['pay_price']), 'number' => $spotBrokerage, 'frozen_time' => $frozenTime ], $balance, $order['id']); $totalSpots++; } } return true; } catch (\Exception $e) { @file_put_contents('quanju4.txt', $e->getMessage() . "-上级分红报错内容\r\n", 8); @file_put_contents('quanju4.txt', $e->getFile() . "-文件\r\n", 8); @file_put_contents('quanju4.txt', $e->getLine() . "-位置\r\n", 8); @file_put_contents('quanju4.txt', $e->getTraceAsString() . "-堆栈\r\n", 8); } } /** * 检查某个用户是否是另一个用户的下级团队成员 * @param int $uid 当前用户 * @param int $targetUid 要检查的目标用户 * @param int $productId 商品ID * @param StoreProductGiftGroupService $giftGroupService 礼包服务 * @param array $checked 已检查的用户(防止循环) * @return bool 如果是下级返回true,否则返回false */ protected function isSubordinate($uid, $targetUid, $productId, $giftGroupService, $checked = []) { // 如果已经检查过这个用户,跳过(防止循环) if (isset($checked[$uid])) { return false; } $checked[$uid] = true; // 查找该用户的所有直推下级 $subordinates = $giftGroupService->getList([ 'product_id' => $productId, 'pid' => $uid ], 'uid', 0, 0, 'create_time asc'); // 检查目标用户是否在直推下级中 foreach ($subordinates as $sub) { if ($sub['uid'] == $targetUid) { return true; // 找到了,是下级 } } // 递归检查每个下级的下级 foreach ($subordinates as $sub) { if ($this->isSubordinate($sub['uid'], $targetUid, $productId, $giftGroupService, $checked)) { return true; // 在下级的下级中找到了 } } return false; // 没找到,不是下级 } }