0,快速命中 if ($user) { $consume = floatval(is_array($user) ? ($user['total_consume'] ?? 0) : ($user->total_consume ?? 0)); if ($consume > 0) { return true; } } else { // 若未传入 user 对象,轻量获取 user 表的 total_consume 尝试命中 try { $userConsume = \think\Db::name('user')->where('id', $userId)->value('total_consume'); if (floatval($userConsume) > 0) { return true; } } catch (\Exception $e) { // 容错继续 } } // 2. 查询有效已支付订单(排除全额退款完成的订单) try { $hasOrder = \think\Db::name('shopro_order') ->where('user_id', $userId) ->whereIn('status', ['paid', 'completed']) ->where('apply_refund_status', '<>', 2) ->whereNull('deletetime') ->count() > 0; return $hasOrder; } catch (\Exception $e) { return false; } } public static function build($agent, $user = null) { $empty = [ 'identities' => ['guest'], 'identity_text' => '游客', 'is_guest' => true, 'has_purchased' => false, 'is_partner' => false, 'is_advanced' => false, 'maker_level_name' => '', 'role_names' => PartnerConfig::roleNames(), 'custom_role_names' => PartnerConfig::roleNames(), ]; if (!$agent) { return $empty; } $userId = intval(is_array($agent) ? ($agent['user_id'] ?? 0) : ($agent->user_id ?? 0)); $level = intval(is_array($agent) ? ($agent['level'] ?? 0) : ($agent->level ?? 0)); $isVenue = intval(is_array($agent) ? ($agent['is_venue'] ?? 0) : ($agent->is_venue ?? 0)); $isCounty = intval(is_array($agent) ? ($agent['is_county'] ?? 0) : ($agent->is_county ?? 0)); $isPartner = intval(is_array($agent) ? ($agent['is_partner'] ?? 0) : ($agent->is_partner ?? 0)); $roleNames = PartnerConfig::roleNames(); $tasterName = $roleNames['taster'] ?? '品酒师'; $makerBaseName = $roleNames['maker'] ?? '创客'; $venueName = $roleNames['venue'] ?? '酒道馆'; $countyName = $roleNames['county'] ?? '区县'; $partnerName = $roleNames['partner'] ?? '合伙股东'; $isAdvanced = ($level >= 2) || ($isVenue == 1) || ($isCounty == 1) || ($isPartner == 1); $hasPurchased = $userId > 0 ? self::hasPurchased($userId, $user ?: (is_array($agent) ? ($agent['user'] ?? null) : ($agent->user ?? null))) : false; $isGuest = ($level == 1 && !$hasPurchased && !$isAdvanced); $identities = []; $parts = []; $makerLevelName = ''; if ($level == 1) { if ($hasPurchased) { $identities[] = 'taster'; } elseif (!$isAdvanced) { $identities[] = 'guest'; } } elseif ($level >= 2) { $identities[] = 'maker'; $makerLevelName = $makerBaseName . 'V' . ($level - 1); $parts[] = $makerLevelName; } if ($isVenue) { $identities[] = 'venue'; $parts[] = $venueName; } if ($isCounty) { $identities[] = 'county'; $parts[] = $countyName; } if ($isPartner) { $identities[] = 'partner'; $parts[] = $partnerName; } // 若无进阶角色身份且等级为1,根据是否买单显示品酒师或游客 if (empty($parts) && $level == 1) { $parts[] = $hasPurchased ? $tasterName : '游客'; } return [ 'identities' => $identities, 'identity_text' => implode(' · ', $parts) ?: ($isGuest ? '游客' : ''), 'is_guest' => $isGuest, 'has_purchased' => $hasPurchased, 'is_partner' => (bool) $isPartner, 'is_advanced' => $isAdvanced, 'maker_level_name' => $makerLevelName, 'role_names' => $roleNames, 'custom_role_names' => $roleNames, ]; } public static function formatMoney($value, $scale = 2) { return number_format((float) $value, $scale, '.', ''); } public static function formatMadow($value) { $floatVal = (float) $value; // 当存在小于 1 枚且大于 0 的微量dmepa时,自适应保留有效小数(最多4位),避免直接抹零显示为 0 枚 if ($floatVal > 0 && $floatVal < 1) { return rtrim(rtrim(number_format($floatVal, 4, '.', ''), '0'), '.'); } return number_format((float) $value, 0, '.', ''); } public static function formatRate($value) { if ($value === null || $value === '') { $value = PartnerConfig::get('partner_madow_base_rate', '0.00000075'); } $floatVal = (float) $value; if ($floatVal <= 0) { return '0'; } // 高精度十进制展开,防止科学计数法 (如 7.15E-7) 并避免微小汇率被 8 位截断四舍五入 $formatted = number_format($floatVal, 12, '.', ''); $trimmed = rtrim(rtrim($formatted, '0'), '.'); return $trimmed !== '' ? $trimmed : '0'; } public static function attachAgent($agent, $rate = null, $user = null) { if (!$agent) { return null; } $identity = self::build($agent, $user); $madow = is_array($agent) ? ($agent['madow'] ?? 0) : ($agent->madow ?? 0); $released = is_array($agent) ? ($agent['voucher_released'] ?? 0) : ($agent->voucher_released ?? 0); $used = is_array($agent) ? ($agent['voucher_used'] ?? 0) : ($agent->voucher_used ?? 0); $total = is_array($agent) ? ($agent['voucher_total'] ?? 0) : ($agent->voucher_total ?? 0); $last = is_array($agent) ? ($agent['last_self_order_time'] ?? 0) : ($agent->last_self_order_time ?? 0); if ($rate === null) { $rate = (new RateService())->currentRate(); } $available = max(0, (float) $released - (float) $used); $windowDays = PartnerConfig::activeDays(); $isActive = (new ActiveService())->isActive($agent); $now = time(); $lastTime = $last ? intval($last) : 0; $expireTime = 0; $remainingSeconds = 0; $daysRemaining = 0; $activeStatus = 'inactive'; if ($isActive) { if ($lastTime > 0) { // 方案 A:以订单支付时刻精确顺延 30 天(秒级精确,满 720 小时) $expireTime = $lastTime + $windowDays * 86400; $remainingSeconds = max(0, $expireTime - $now); $daysRemaining = intval(ceil($remainingSeconds / 86400)); $activeStatus = ($daysRemaining <= 5) ? 'expiring' : 'active'; } else { $activeStatus = 'active'; $daysRemaining = $windowDays; $expireTime = $now + $windowDays * 86400; $remainingSeconds = $windowDays * 86400; } } else { $activeStatus = 'inactive'; $daysRemaining = 0; $remainingSeconds = 0; } $data = is_object($agent) ? $agent->toArray() : $agent; $userId = intval($data['user_id'] ?? 0); $agentTotalIncome = floatval($data['total_income'] ?? 0); $cashTypes = PartnerConfig::cashTypes(); $rewardCash = $userId > 0 ? \app\admin\model\shopro\partner\Reward::where('user_id', $userId) ->whereIn('reward_type', $cashTypes) ->where('status', 1) ->where('amount', '>', 0) ->sum('amount') : 0; $unifiedTotalIncome = max($agentTotalIncome, floatval($rewardCash)); return array_merge($data, $identity, [ 'total_income' => self::formatMoney($unifiedTotalIncome), 'income_total_cash' => self::formatMoney($unifiedTotalIncome), 'is_venue' => intval($data['is_venue'] ?? 0), 'is_county' => intval($data['is_county'] ?? 0), 'county_code' => (string) ($data['county_code'] ?? ''), 'direct_order_seq' => intval($data['direct_order_seq'] ?? 0), 'madow' => self::formatMadow($madow), 'madow_rate' => self::formatRate($rate), 'madow_value' => self::formatMoney((float) $madow * (float) $rate), 'show_madow_rate' => PartnerConfig::showMadowRate(), 'voucher_total' => self::formatMoney($total), 'voucher_released' => self::formatMoney($released), 'voucher_used' => self::formatMoney($used), 'voucher_available' => self::formatMoney($available), 'active_30d' => $isActive, 'active_status' => $activeStatus, 'days_remaining' => $daysRemaining, 'expire_time' => $expireTime, 'expire_seconds' => $remainingSeconds, 'last_self_order_time' => $lastTime, ]); } public static function attachProfile($agent, $rate = null, $user = null) { if (!$agent) { $baseRate = PartnerConfig::get('partner_madow_base_rate', '0.00000075'); $roleNames = PartnerConfig::roleNames(); return [ 'identity_text' => '游客', 'identities' => ['guest'], 'is_guest' => true, 'has_purchased' => false, 'is_partner' => false, 'is_advanced' => false, 'maker_level_name' => '', 'role_names' => $roleNames, 'custom_role_names' => $roleNames, 'madow' => '0', 'madow_rate' => self::formatRate($baseRate), 'madow_value' => '0.00', 'show_madow_rate' => PartnerConfig::showMadowRate(), 'total_income' => '0.00', 'income_total_cash' => '0.00', 'voucher_available' => '0.00', 'active_30d' => true, 'active_status' => 'inactive', 'days_remaining' => 0, 'expire_time' => 0, 'expire_seconds' => 0, 'last_self_order_time' => 0, 'agent_form' => self::agentFormEnabled(), 'partner_mode' => PartnerConfig::isPartnerMode(), ]; } $full = self::attachAgent($agent, $rate, $user); $roleNames = $full['role_names'] ?? PartnerConfig::roleNames(); return [ 'identity_text' => $full['identity_text'], 'identities' => $full['identities'], 'is_guest' => $full['is_guest'] ?? false, 'has_purchased' => $full['has_purchased'] ?? false, 'is_partner' => $full['is_partner'], 'is_advanced' => $full['is_advanced'] ?? false, 'maker_level_name' => $full['maker_level_name'], 'role_names' => $roleNames, 'custom_role_names' => $roleNames, 'total_income' => $full['total_income'] ?? '0.00', 'income_total_cash' => $full['income_total_cash'] ?? '0.00', 'madow' => $full['madow'], 'madow_rate' => $full['madow_rate'], 'madow_value' => $full['madow_value'], 'show_madow_rate' => $full['show_madow_rate'] ?? PartnerConfig::showMadowRate(), 'voucher_available' => $full['voucher_available'], 'active_30d' => $full['active_30d'], 'active_status' => $full['active_status'] ?? 'inactive', 'days_remaining' => $full['days_remaining'] ?? 0, 'expire_time' => $full['expire_time'] ?? 0, 'expire_seconds' => $full['expire_seconds'] ?? 0, 'last_self_order_time' => $full['last_self_order_time'] ?? 0, 'agent_form' => self::agentFormEnabled(), 'partner_mode' => PartnerConfig::isPartnerMode(), ]; } protected static function agentFormEnabled() { $form = sheep_config('shop.commission.agent_form'); if (is_array($form)) { return !empty($form['status']) && $form['status'] !== '0'; } return false; } public static function findAgent($userId) { if (!$userId) { return null; } return AgentModel::where('user_id', $userId)->find(); } }