317 行
13 KiB
PHP
317 行
13 KiB
PHP
<?php
|
||
|
||
namespace addons\shopro\service\partner;
|
||
|
||
use app\admin\model\shopro\commission\Agent as AgentModel;
|
||
|
||
/**
|
||
* 身份文案:品酒师 / 创客V{n} / 酒道馆 / 区县
|
||
*/
|
||
class IdentityService
|
||
{
|
||
public static function hasPurchased($userId, $user = null)
|
||
{
|
||
$userId = intval($userId);
|
||
if ($userId <= 0) {
|
||
return false;
|
||
}
|
||
|
||
// 1. 如果传入了 user 对象且 total_consume > 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) {
|
||
$orderDay = strtotime(date('Y-m-d 00:00:00', $lastTime));
|
||
$expireTime = $orderDay + $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);
|
||
$rewardCash = $userId > 0 ? \app\admin\model\shopro\partner\Reward::where('user_id', $userId)
|
||
->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();
|
||
}
|
||
}
|