where('user_id', $userId)->find(); if ($ext && !empty($ext['inviter_id']) && (int)$ext['inviter_id'] > 0) { return true; } $hasRelation = Db::name('fc_dist_relation')->where('user_id', $userId)->count(); if ($hasRelation > 0) { return true; } $parent1Id = 0; // 优先通过邀请码查询推荐人(忽略大小写) $inviteCode = strtoupper(trim((string)$inviteCode)); if ($inviteCode !== '') { $parentExt = Db::name('fc_user_ext')->where('invite_code', $inviteCode)->find(); if ($parentExt && (int)$parentExt['user_id'] > 0) { $parent1Id = (int)$parentExt['user_id']; } } // 次选通过直接传入的推荐人用户ID定位 if ($parent1Id <= 0 && (int)$inviterId > 0) { $parentUser = Db::name('user')->where('id', (int)$inviterId)->find(); if ($parentUser) { $parent1Id = (int)$parentUser['id']; } } // 推荐人不存在,或推荐人是用户本人,则不建立关系 if ($parent1Id <= 0 || $parent1Id === $userId) { return false; } // 防环校验:不能将自己的直接或间接下线绑定为自己的推荐人 $isDownline = Db::name('fc_dist_relation') ->where('user_id', $parent1Id) ->where('parent_id', $userId) ->count(); if ($isDownline > 0) { return false; } $now = time(); // 更新用户扩展表的直接推荐人ID Db::name('fc_user_ext')->where('user_id', $userId)->update([ 'inviter_id' => $parent1Id, 'updatetime' => $now, ]); // 1. 建立 1 级分销关系(直推) Db::name('fc_dist_relation')->insert([ 'user_id' => $userId, 'parent_id' => $parent1Id, 'level' => 1, 'createtime' => $now, ]); // 2. 递归追溯 2 级分销关系(间推:查找 1 级推荐人的 1 级推荐人) $parent2Row = Db::name('fc_dist_relation') ->where('user_id', $parent1Id) ->where('level', 1) ->find(); if ($parent2Row && (int)$parent2Row['parent_id'] > 0) { $parent2Id = (int)$parent2Row['parent_id']; // 避免循环推荐(2级推荐人不能为用户本人和1级推荐人) if ($parent2Id !== $userId && $parent2Id !== $parent1Id) { Db::name('fc_dist_relation')->insert([ 'user_id' => $userId, 'parent_id' => $parent2Id, 'level' => 2, 'createtime' => $now, ]); } } // 3. 若当前用户在补绑推荐人之前已经发展了 1 级下线,则为这些 1 级下线补齐以 $parent1Id 为 2 级推荐人的关系 $myL1Children = Db::name('fc_dist_relation') ->where('parent_id', $userId) ->where('level', 1) ->select(); if (!empty($myL1Children)) { foreach ($myL1Children as $childRow) { $childId = (int)$childRow['user_id']; if ($childId <= 0 || $childId === $parent1Id) { continue; } $hasL2 = Db::name('fc_dist_relation') ->where('user_id', $childId) ->where('level', 2) ->count(); if ($hasL2 <= 0) { Db::name('fc_dist_relation')->insert([ 'user_id' => $childId, 'parent_id' => $parent1Id, 'level' => 2, 'createtime' => $now, ]); } } } return true; } /** * 订单支付成功后执行自动拆单分佣结算 * * 适用于各类消费型订单(发布 post、会员开通 member、付费查看 payview 等) * * @param array $order 订单数据数组,必须包含 id, order_no, user_id, amount, type * @return array 结算明细数组 */ public static function settleOrder(array $order) { $orderId = (int)(isset($order['id']) ? $order['id'] : 0); $orderNo = isset($order['order_no']) ? (string)$order['order_no'] : ''; $payerId = (int)(isset($order['user_id']) ? $order['user_id'] : 0); $amount = round((float)(isset($order['amount']) ? $order['amount'] : 0), 2); $type = isset($order['type']) ? (string)$order['type'] : 'post'; // 充值订单不分佣(避免双重套现),金额必须大于 0 if ($amount <= 0 || $payerId <= 0 || $type === 'recharge') { return []; } // 读取分销全局配置 $enabled = self::getConfig('dist.enabled', 1); if (!$enabled) { return []; } // 幂等防御:已对此订单结算过佣金则直接跳过,防止并发重复发放 $alreadySettled = Db::name('fc_commission') ->where('order_no', $orderNo) ->find(); if ($alreadySettled) { return []; } $maxLevel = (int)self::getConfig('dist.level', 2); $rate1 = (float)self::getConfig('dist.level1Rate', 20); // 默认 20% $rate2 = (float)self::getConfig('dist.level2Rate', 10); // 默认 10% $settleDays = self::settleDays(); // 查询该付款用户的所有上级推广员(按层级正序) $relations = Db::name('fc_dist_relation') ->where('user_id', $payerId) ->where('level', '<=', $maxLevel) ->order('level asc') ->select(); if (empty($relations)) { return []; } $now = time(); $settledList = []; foreach ($relations as $rel) { $level = (int)$rel['level']; $parentId = (int)$rel['parent_id']; if ($parentId <= 0) { continue; } // 获取该层级的有效比例 $rate = ($level === 1) ? $rate1 : (($level === 2) ? $rate2 : 0); if ($rate <= 0) { continue; } $commissionAmount = round($amount * ($rate / 100), 2); if ($commissionAmount <= 0) { continue; } // 写入分销佣金明细表(结算周期内为「未结算」,到期由 fc:settle 置为已结算) $commId = Db::name('fc_commission')->insertGetId([ 'user_id' => $parentId, 'from_user_id' => $payerId, 'order_id' => $orderId, 'order_no' => $orderNo, 'order_type' => $type, 'amount' => $commissionAmount, 'rate' => $rate, 'level' => $level, 'status' => $settleDays > 0 ? 0 : 1, 'settle_due' => $now + $settleDays * 86400, 'settletime' => $settleDays > 0 ? null : $now, 'createtime' => $now, ]); // 发送分销佣金入账站内信通知(区分待结算周期与实时结算) $levelText = ($level === 1) ? '直接下线' : '间接下线'; $settleTip = ($settleDays > 0) ? '已计入待结算佣金(预计 T+' . $settleDays . ' 自动结算转入可提现资产)。' : '已计入可提现资产。'; $noticeContent = '您的 ' . $levelText . ' 产生了一笔消费(¥' . $amount . '),为您带来分销佣金 ¥' . $commissionAmount . ',' . $settleTip; Db::name('fc_message')->insert([ 'user_id' => $parentId, 'type' => 'interact', 'title' => '分销佣金到账提醒', 'content' => $noticeContent, 'isread' => 0, 'createtime' => $now, ]); $settledList[] = [ 'commissionId' => $commId, 'userId' => $parentId, 'level' => $level, 'amount' => $commissionAmount, ]; } return $settledList; } /** * 结算周期天数:解析 dist.settleCycle(支持 T+7 / T+1 / T+0 或纯数字 7 / 0),默认 7 天 * * @return int */ public static function settleDays() { $cycle = trim((string)self::getConfig('dist.settleCycle', 'T+7')); if (preg_match('/T\s*\+\s*(\d+)/i', $cycle, $m)) { return max(0, (int)$m[1]); } if (is_numeric($cycle)) { return max(0, (int)$cycle); } return 7; } /** * 读取业务配置项 * * @param string $key 配置键名 * @param mixed $default 默认回退值 * @return mixed */ protected static function getConfig($key, $default = null) { $row = Db::name('fc_config')->where('key', $key)->find(); if (!$row || !isset($row['value']) || $row['value'] === '') { return $default; } $val = $row['value']; $decoded = json_decode($val, true); return (json_last_error() === JSON_ERROR_NONE) ? $decoded : $val; } }