文件
Henglianshutong/application/admin/controller/shopro/Dashboard.php

881 行
40 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?php
namespace app\admin\controller\shopro;
use app\admin\model\shopro\order\Order;
use app\admin\model\shopro\order\OrderItem;
use app\admin\model\shopro\goods\Goods;
use app\admin\model\shopro\user\User;
use app\admin\model\shopro\Share;
use app\admin\model\shopro\commission\Agent;
use app\admin\model\shopro\partner\Reward;
use app\admin\model\shopro\partner\DailyReport;
use app\admin\model\shopro\partner\RateLog;
use app\admin\model\shopro\partner\VoucherPlan;
use addons\shopro\service\SearchHistory;
use addons\shopro\service\partner\RateService;
use addons\shopro\service\partner\IdentityService;
use addons\shopro\service\partner\PartnerConfig;
use app\admin\model\shopro\Withdraw;
use app\admin\model\shopro\Refund;
use app\admin\model\shopro\goods\SkuPrice as SkuPriceModel;
class Dashboard extends Common
{
public function index()
{
$cardList = [
[
'name' => 'total',
'card' => 'shopro/dashboard/total',
'status' => $this->auth->check('shopro/dashboard/total')
],
[
'name' => 'chart',
'card' => 'shopro/dashboard/chart',
'status' => $this->auth->check('shopro/dashboard/chart')
],
[
'name' => 'ranking',
'card' => 'shopro/dashboard/ranking',
'status' => $this->auth->check('shopro/dashboard/ranking')
],
];
$this->assignconfig('cardList', $cardList);
return $this->view->fetch();
}
public function total()
{
// 1. 基础用户数据 (保持向下兼容)
$userData = [
'total' => User::count(),
'today' => User::whereTime('createtime', 'today')->count(),
'week' => User::whereTime('createtime', 'week')->count(),
'array' => collection(User::field('id,createtime')->whereTime('createtime', 'today')->select())->each(function ($user) {
$user->counter = 1;
$user->createtime_unix = $user->getData('createtime') * 1000;
})
];
// 2. 分销商/合伙人基础数据 (保持向下兼容)
$agentData = [
'total' => Agent::count(),
'today' => Agent::whereTime('createtime', 'today')->count(),
'week' => Agent::whereTime('createtime', 'week')->count(),
'array' => collection(Agent::field('user_id,createtime')->whereTime('createtime', 'today')->select())->each(function ($agent) {
$agent->counter = 1;
$agent->createtime_unix = $agent->getData('createtime') * 1000;
})
];
// 3. 分享数据 (保持向下兼容)
$shareData = [
'total' => Share::count(),
'today' => Share::whereTime('createtime', 'today')->count(),
'week' => Share::whereTime('createtime', 'week')->count(),
'array' => collection(Share::field('id,createtime')->whereTime('createtime', 'today')->select())->each(function ($share) {
$share->counter = 1;
$share->createtime_unix = $share->getData('createtime') * 1000;
})
];
// 4. 全局财富大盘与资金流 (专业财富指标)
$paidStatuses = [Order::STATUS_PAID, Order::STATUS_COMPLETED];
$totalGmv = (float) Order::whereIn('status', $paidStatuses)->sum('pay_fee');
$todayGmv = (float) Order::whereIn('status', $paidStatuses)->whereTime('createtime', 'today')->sum('pay_fee');
$paidOrderCount = (int) Order::whereIn('status', $paidStatuses)->count();
$todayPaidOrderCount = (int) Order::whereIn('status', $paidStatuses)->whereTime('createtime', 'today')->count();
$avgOrderAmount = $paidOrderCount > 0 ? round($totalGmv / $paidOrderCount, 2) : 0.00;
// 现金分成总支出 (status = 1 真实入账)
$totalPayout = (float) Reward::where('status', 1)->sum('amount');
$todayPayout = (float) Reward::where('status', 1)->whereTime('createtime', 'today')->sum('amount');
$netRetention = round($totalGmv - $totalPayout, 2);
$payoutRate = $totalGmv > 0 ? round(($totalPayout / $totalGmv) * 100, 2) : 0.00;
$wealthData = [
'total_gmv' => number_format($totalGmv, 2, '.', ''),
'today_gmv' => number_format($todayGmv, 2, '.', ''),
'total_payout' => number_format($totalPayout, 2, '.', ''),
'today_payout' => number_format($todayPayout, 2, '.', ''),
'net_retention' => number_format($netRetention, 2, '.', ''),
'payout_rate' => number_format($payoutRate, 2, '.', ''),
'paid_order_count' => $paidOrderCount,
'today_paid_order_count' => $todayPaidOrderCount,
'avg_order_amount' => number_format($avgOrderAmount, 2, '.', ''),
];
// 5. 马豆生态资产蓄水池
$totalMadow = (float) Agent::sum('madow');
$rateService = new RateService();
$currentRate = $rateService->currentRate();
$totalMadowCny = round($totalMadow * $currentRate, 2);
$historyOrderN = $rateService->countN();
$latestReport = DailyReport::order('settle_date', 'desc')->find();
$yesterdayMadowPool = $latestReport ? (float) $latestReport->madow_pool : 0.00;
$madowData = [
'total_madow' => IdentityService::formatMadow($totalMadow),
'current_rate' => number_format($currentRate, 10, '.', ''),
'rate_per_wan' => number_format($currentRate * 10000, 4, '.', ''), // 每万豆价值折算
'total_madow_cny' => number_format($totalMadowCny, 2, '.', ''),
'history_order_n' => $historyOrderN,
'yesterday_madow_pool' => IdentityService::formatMadow($yesterdayMadowPool),
];
// 6. 消费兑换券杠杆池
$totalVoucher = (float) VoucherPlan::sum('total_amount');
$releasedVoucher = (float) VoucherPlan::sum('released_amount');
$usedVoucher = (float) Agent::sum('voucher_used');
$pendingVoucher = max(0, round($totalVoucher - $releasedVoucher, 2));
$availableVoucher = max(0, round($releasedVoucher - $usedVoucher, 2));
$voucherData = [
'total_voucher' => number_format($totalVoucher, 2, '.', ''),
'released_voucher' => number_format($releasedVoucher, 2, '.', ''),
'used_voucher' => number_format($usedVoucher, 2, '.', ''),
'pending_voucher' => number_format($pendingVoucher, 2, '.', ''),
'available_voucher' => number_format($availableVoucher, 2, '.', ''),
];
// 7. 合伙股东网络与全角色身份资产矩阵 (会员数、马豆数量、分佣数量、马豆价值)
$totalRegisteredUsers = (int) User::count();
$totalAgents = (int) Agent::count();
$normalUserCount = max(0, $totalRegisteredUsers - $totalAgents);
$tasterCount = (int) Agent::where('level', 1)->count();
$makerCount = (int) Agent::where('level', '>=', 2)->count();
$makerLevels = [];
for ($lv = 2; $lv <= 7; $lv++) {
$makerLevels['V' . ($lv - 1)] = (int) Agent::where('level', $lv)->count();
}
$venueCount = (int) Agent::where('is_venue', 1)->count();
$countyCount = (int) Agent::where('is_county', 1)->count();
$activeWindow = time() - (int) PartnerConfig::get('partner_active_days', 30) * 86400;
$activeCount = (int) Agent::where('last_self_order_time', '>=', $activeWindow)->count();
$inactiveCount = max(0, $totalAgents - $activeCount);
$activeRate = $totalAgents > 0 ? round(($activeCount / $totalAgents) * 100, 1) : 0.0;
// 全量计算各角色身份名下资产与分佣
$allAgents = Agent::field('user_id, level, is_venue, is_county, madow, last_self_order_time')->select();
$userRewardAgg = Reward::where('status', 1)
->where('user_id', '>', 0)
->field('user_id, sum(amount) as total_amount, count(*) as count')
->group('user_id')
->select();
$userRewards = [];
foreach ($userRewardAgg as $rw) {
$userRewards[$rw->user_id] = [
'amount' => (float) $rw->total_amount,
'count' => (int) $rw->count,
];
}
$calcRoleStats = function ($filterCallback, $roleKey, $roleName, $category, $unit, $badge, $color, $filterLevel = '', $filterType = '') use ($allAgents, $userRewards, $currentRate, $activeWindow) {
$uCount = 0;
$madowTotal = 0.0;
$rewardAmt = 0.0;
$rewardCnt = 0;
$activeCnt = 0;
foreach ($allAgents as $ag) {
if ($filterCallback($ag)) {
$uCount++;
$madowTotal = (float) bcadd((string)$madowTotal, (string)($ag->madow ?? 0), 8);
$uid = $ag->user_id;
if (isset($userRewards[$uid])) {
$rewardAmt = round($rewardAmt + $userRewards[$uid]['amount'], 2);
$rewardCnt += $userRewards[$uid]['count'];
}
if ((int) $ag->getData('last_self_order_time') >= $activeWindow) {
$activeCnt++;
}
}
}
$madowCny = round($madowTotal * $currentRate, 2);
$activeRate = $uCount > 0 ? round(($activeCnt / $uCount) * 100, 1) : 0.0;
return [
'role_key' => $roleKey,
'role_name' => $roleName,
'category' => $category,
'unit' => $unit,
'badge' => $badge,
'color' => $color,
'user_count' => $uCount,
'madow_total' => IdentityService::formatMadow($madowTotal),
'madow_cny' => number_format($madowCny, 2, '.', ''),
'madow_cny_raw' => round($madowTotal * $currentRate, 4),
'reward_amount' => number_format($rewardAmt, 2, '.', ''),
'reward_count' => $rewardCnt,
'active_count' => $activeCnt,
'active_rate' => $activeRate,
'filter_level' => $filterLevel,
'filter_type' => $filterType,
];
};
// 统计各角色身份详细指标
$roleList = [
$calcRoleStats(function($a) { return (int)$a->level == 1; }, 'taster', '品酒师', '基础身份', '人', '品', '#806af6', 1, ''),
$calcRoleStats(function($a) { return (int)$a->level == 2; }, 'maker_v1', '创客V1', '创客梯队', '人', 'V1', '#13c2c2', 2, ''),
$calcRoleStats(function($a) { return (int)$a->level == 3; }, 'maker_v2', '创客V2', '创客梯队', '人', 'V2', '#13c2c2', 3, ''),
$calcRoleStats(function($a) { return (int)$a->level == 4; }, 'maker_v3', '创客V3', '创客梯队', '人', 'V3', '#13c2c2', 4, ''),
$calcRoleStats(function($a) { return (int)$a->level == 5; }, 'maker_v4', '创客V4', '创客梯队', '人', 'V4', '#13c2c2', 5, ''),
$calcRoleStats(function($a) { return (int)$a->level == 6; }, 'maker_v5', '创客V5', '创客梯队', '人', 'V5', '#13c2c2', 6, ''),
$calcRoleStats(function($a) { return (int)$a->level == 7; }, 'maker_v6', '创客V6', '创客梯队', '人', 'V6', '#13c2c2', 7, ''),
$calcRoleStats(function($a) { return (int)$a->level >= 2; }, 'maker_total', '创客队伍小计', '创客梯队', '人', '创', '#13c2c2', 'maker', ''),
$calcRoleStats(function($a) { return (int)$a->is_venue == 1; }, 'venue', '实体酒道馆', '渠道网络', '家', '馆', '#fa8c16', '', 'venue'),
$calcRoleStats(function($a) { return (int)$a->is_county == 1; }, 'county', '区县代理', '渠道网络', '个', '县', '#722ed1', '', 'county'),
$calcRoleStats(function($a) { return (int)$a->level >= 2 || (int)$a->is_venue == 1 || (int)$a->is_county == 1; }, 'partner_total', '合伙股东汇总', '汇总统计', '人', '合', '#eb2f96', '', 'partner'),
];
// 增加普通注册会员行
$roleList[] = [
'role_key' => 'normal_user',
'role_name' => '普通注册会员',
'category' => '电商用户',
'unit' => '人',
'badge' => '普',
'color' => '#8c8c8c',
'user_count' => $normalUserCount,
'madow_total' => '0.00000000',
'madow_cny' => '0.00',
'madow_cny_raw' => 0.0,
'reward_amount' => '0.00',
'reward_count' => 0,
'active_count' => 0,
'active_rate' => 0.0,
'filter_level' => '',
'filter_type' => 'normal',
];
// 全平台全员合计行
$allTotalRow = $calcRoleStats(function($a) { return true; }, 'all_total', '全平台全员合计', '汇总统计', '人', '全', '#1890ff', '', 'all');
if ($normalUserCount > 0) {
$allTotalRow['user_count'] = $totalRegisteredUsers;
$allTotalRow['active_rate'] = $totalRegisteredUsers > 0 ? round(($allTotalRow['active_count'] / $totalRegisteredUsers) * 100, 1) : 0.0;
}
$roleList[] = $allTotalRow;
$roleStats = [];
foreach ($roleList as $rItem) {
$roleStats[$rItem['role_key']] = $rItem;
}
$partnerNetwork = [
'total_registered_users' => $totalRegisteredUsers,
'total_agents' => $totalAgents,
'normal_user_count' => $normalUserCount,
'taster_count' => $tasterCount,
'maker_count' => $makerCount,
'maker_levels' => $makerLevels,
'venue_count' => $venueCount,
'county_count' => $countyCount,
'active_count' => $activeCount,
'inactive_count' => $inactiveCount,
'active_rate' => $activeRate,
'role_list' => $roleList,
'role_stats' => $roleStats,
];
// 8. CFO 视角:资金浮存、提现出款风控与资产负债中枢
$userCommissionFloat = (float) User::sum('commission');
$userMoneyFloat = (float) User::sum('money');
$totalFloatFunds = round($userCommissionFloat + $userMoneyFloat, 2);
$totalWithdrawApplied = (float) Withdraw::sum('amount');
$totalWithdrawPaid = (float) Withdraw::where('status', 2)->sum('paid_fee');
$pendingWithdrawAmount = (float) Withdraw::where('status', 0)->sum('amount');
$pendingWithdrawCount = (int) Withdraw::where('status', 0)->count();
$withdrawCashRate = $totalPayout > 0 ? round(($totalWithdrawPaid / $totalPayout) * 100, 2) : 0.0;
$totalRefundAmount = (float) Refund::where('status', 'completed')->sum('refund_fee');
$totalRefundCount = (int) Refund::where('status', 'completed')->count();
$refundRate = $totalGmv > 0 ? round(($totalRefundAmount / $totalGmv) * 100, 2) : 0.0;
$realNetCashFlow = round($totalGmv - $totalRefundAmount - $totalWithdrawPaid, 2);
// 未来总承兑责任 (待释放消费券 + 马豆折算市值 + 用户未提现佣金)
$pendingVoucherLiability = $pendingVoucher;
$madowMarketLiability = $totalMadowCny;
$commissionLiability = $userCommissionFloat;
$totalFutureLiability = round($pendingVoucherLiability + $madowMarketLiability + $commissionLiability, 2);
$financeOverview = [
'commission_float' => number_format($userCommissionFloat, 2, '.', ''),
'user_balance_float' => number_format($userMoneyFloat, 2, '.', ''),
'total_float_funds' => number_format($totalFloatFunds, 2, '.', ''),
'total_withdraw_applied' => number_format($totalWithdrawApplied, 2, '.', ''),
'total_withdraw_paid' => number_format($totalWithdrawPaid, 2, '.', ''),
'pending_withdraw_amount' => number_format($pendingWithdrawAmount, 2, '.', ''),
'pending_withdraw_count' => $pendingWithdrawCount,
'withdraw_cash_rate' => $withdrawCashRate,
'total_refund_amount' => number_format($totalRefundAmount, 2, '.', ''),
'total_refund_count' => $totalRefundCount,
'refund_rate' => $refundRate,
'real_net_cash_flow' => number_format($realNetCashFlow, 2, '.', ''),
'pending_voucher_liability' => number_format($pendingVoucherLiability, 2, '.', ''),
'madow_market_liability' => number_format($madowMarketLiability, 2, '.', ''),
'commission_liability' => number_format($commissionLiability, 2, '.', ''),
'total_future_liability' => number_format($totalFutureLiability, 2, '.', ''),
];
// 9. COO 视角:转化复购漏斗、合伙人30天掉队雷达与供应链库存安全
// 9.1 用户购买频次聚合与转化漏斗
$paidOrderUsers = Order::whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED])
->group('user_id')
->field('user_id, count(*) as buy_count')
->select();
$paidUsersCount = count($paidOrderUsers);
$singleBuyCount = 0;
$repeatBuyCount = 0;
foreach ($paidOrderUsers as $buyer) {
if ((int)$buyer->buy_count > 1) {
$repeatBuyCount++;
} else {
$singleBuyCount++;
}
}
$payConversionRate = $totalRegisteredUsers > 0 ? round(($paidUsersCount / $totalRegisteredUsers) * 100, 1) : 0.0;
$repeatPurchaseRate = $paidUsersCount > 0 ? round(($repeatBuyCount / $paidUsersCount) * 100, 1) : 0.0;
$arpu = $paidUsersCount > 0 ? round($totalGmv / $paidUsersCount, 2) : 0.0;
// 9.2 合伙人 30 天自购断档掉队雷达(安全活跃、7天即将断档预警、已断档失效)
$now = time();
$activeDays = (int) PartnerConfig::get('partner_active_days', 30);
$warningThresholdDays = 7;
$activeWindow = $now - ($activeDays * 86400);
$warningWindow = $now - (($activeDays - $warningThresholdDays) * 86400);
$activePartnersCount = 0;
$warningPartnersCount = 0;
$frozenPartnersCount = 0;
$warningList = [];
$frozenList = [];
// 批量拉取合伙人用户信息,避免循环内查询
$agentUserIds = array_column(collection($allAgents)->toArray(), 'user_id');
$agentUserMap = [];
if (!empty($agentUserIds)) {
$agentUserRows = User::whereIn('id', $agentUserIds)->field('id, nickname, mobile, avatar')->select();
foreach ($agentUserRows as $uRow) {
$agentUserMap[$uRow->id] = $uRow;
}
}
foreach ($allAgents as $ag) {
$lastTime = (int) $ag->getData('last_self_order_time');
$uid = $ag->user_id;
$u = $agentUserMap[$uid] ?? null;
$nickname = $u ? ($u->nickname ?: '用户#' . $uid) : ('用户#' . $uid);
$mobile = ($u && $u->mobile) ? substr_replace($u->mobile, '****', 3, 4) : '-';
$avatar = $u ? ($u->avatar ?: '') : '';
$identityData = IdentityService::build($ag);
$identityText = $identityData['identity_text'] ?: ('Lv.' . $ag->level);
if ($lastTime > 0 && $lastTime >= $warningWindow) {
// 安全活跃期 (距今 < 23天)
$activePartnersCount++;
} elseif ($lastTime > 0 && $lastTime >= $activeWindow) {
// 7 天内即将断档预警 (距今 23~30天)
$warningPartnersCount++;
$daysLeft = max(1, (int) ceil(($lastTime + $activeDays * 86400 - $now) / 86400));
$warningList[] = [
'user_id' => $uid,
'nickname' => $nickname,
'avatar' => $avatar,
'mobile' => $mobile,
'level_text' => $identityText,
'last_order_date' => date('Y-m-d H:i', $lastTime),
'days_left' => $daysLeft,
];
} else {
// 已断档冻结 (> 30天 或 从未自购)
$frozenPartnersCount++;
if (count($frozenList) < 10) {
$overdueDays = $lastTime > 0 ? max(1, (int) floor(($now - $lastTime) / 86400) - $activeDays) : 999;
$frozenList[] = [
'user_id' => $uid,
'nickname' => $nickname,
'avatar' => $avatar,
'mobile' => $mobile,
'level_text' => $identityText,
'last_order_date' => $lastTime > 0 ? date('Y-m-d H:i', $lastTime) : '从未自购',
'overdue_days' => $overdueDays == 999 ? '从未自购' : "逾期 {$overdueDays} 天",
];
}
}
}
// 9.3 供应链库存安全与动销商品
$onSaleGoodsCount = (int) Goods::where('status', 'up')->count();
$totalStock = (int) SkuPriceModel::where('status', 'up')->sum('stock');
$lowStockCount = (int) SkuPriceModel::alias('sp')
->join('shopro_goods g', 'sp.goods_id = g.id')
->where('g.status', 'up')
->where('sp.status', 'up')
->where('sp.stock', '<=', 10)
->count();
$lowStockSkuList = SkuPriceModel::alias('sp')
->join('shopro_goods g', 'sp.goods_id = g.id')
->where('g.status', 'up')
->where('sp.status', 'up')
->where('sp.stock', '<=', 10)
->field('sp.id as sku_id, sp.goods_id, g.title as goods_title, g.image as goods_image, sp.goods_sku_text, sp.stock, sp.price')
->order('sp.stock', 'asc')
->limit(10)
->select();
$lowStockFormatted = [];
foreach ($lowStockSkuList as $lsku) {
$skuText = is_array($lsku->goods_sku_text) ? implode(' ', $lsku->goods_sku_text) : ($lsku->goods_sku_text ?: '默认单规格');
$lowStockFormatted[] = [
'sku_id' => $lsku->sku_id,
'goods_id' => $lsku->goods_id,
'goods_title' => $lsku->goods_title,
'goods_image' => $lsku->goods_image,
'sku_text' => $skuText,
'stock' => (int) $lsku->stock,
'price' => $lsku->price,
];
}
// 9.4 平台经营健康度评分模型 (满分 100)
// ① 拨比健康度 (30分)
$payoutScore = 30.0;
if ($totalGmv > 0) {
if ($payoutRate > 60) {
$payoutScore = 10.0;
} elseif ($payoutRate > 40) {
$payoutScore = 20.0;
} elseif ($payoutRate < 15) {
$payoutScore = 25.0;
} else {
$payoutScore = 30.0;
}
}
// ② 合伙人活跃健康度 (25分)
$partnerActiveRate = $totalAgents > 0 ? ($activePartnersCount / $totalAgents) : 0;
$activeScore = round($partnerActiveRate * 25, 1);
// ③ 用户复购粘性 (25分, 基准30%得满分)
$repeatScore = $paidUsersCount > 0 ? min(25.0, round(($repeatPurchaseRate / 30) * 25, 1)) : 15.0;
// ④ 资金履约安全度 (20分)
$safetyScore = 20.0;
if ($refundRate > 5.0) {
$safetyScore = 10.0;
} elseif ($refundRate > 2.0) {
$safetyScore = 15.0;
} else {
$safetyScore = 20.0;
}
$healthTotalScore = round($payoutScore + $activeScore + $repeatScore + $safetyScore, 1);
$healthGrade = 'S·卓越';
$healthGradeColor = '#52c41a';
if ($healthTotalScore < 70) {
$healthGrade = 'C·预警';
$healthGradeColor = '#f5222d';
} elseif ($healthTotalScore < 80) {
$healthGrade = 'B·稳健';
$healthGradeColor = '#fa8c16';
} elseif ($healthTotalScore < 90) {
$healthGrade = 'A·良好';
$healthGradeColor = '#1890ff';
}
$healthSuggestions = [];
if ($payoutRate <= 35) {
$healthSuggestions[] = "当前综合拨比为 {$payoutRate}%,处于精算设定的安全区间,净现金流沉淀稳固。";
} else {
$healthSuggestions[] = "当前综合拨比为 {$payoutRate}%,略高于基准安全线,建议关注毛利率较高的商品排期。";
}
if ($warningPartnersCount > 0) {
$healthSuggestions[] = "监测到有 {$warningPartnersCount} 位合伙人进入 7 天内即将断档预警期,建议及时定向唤醒催单。";
} else {
$healthSuggestions[] = "合伙股东团队自购状态良好,暂无 7 天内即将断档的合伙人。";
}
if ($lowStockCount > 0) {
$healthSuggestions[] = "仓储有 {$lowStockCount} 款商品 SKU 库存告急(≤10件),请及时补充库存避免断货缺货。";
} else {
$healthSuggestions[] = "在售商品库存充沛,供应链运转健康。";
}
if ($pendingWithdrawCount > 0) {
$healthSuggestions[] = "当前有 {$pendingWithdrawCount} 笔提现申请等待审核(¥{$financeOverview['pending_withdraw_amount']}),请及时审核保障出款体验。";
}
$operationOverview = [
'total_registered' => $totalRegisteredUsers,
'paid_users_count' => $paidUsersCount,
'pay_conversion_rate' => $payConversionRate,
'single_buy_users_count' => $singleBuyCount,
'repeat_buy_users_count' => $repeatBuyCount,
'repeat_purchase_rate' => $repeatPurchaseRate,
'arpu' => number_format($arpu, 2, '.', ''),
'active_partners' => $activePartnersCount,
'warning_partners' => $warningPartnersCount,
'frozen_partners' => $frozenPartnersCount,
'warning_list' => $warningList,
'frozen_list' => $frozenList,
'on_sale_goods_count' => $onSaleGoodsCount,
'total_stock' => $totalStock,
'low_stock_count' => $lowStockCount,
'low_stock_list' => $lowStockFormatted,
'health' => [
'score' => $healthTotalScore,
'grade' => $healthGrade,
'grade_color' => $healthGradeColor,
'payout_score' => $payoutScore,
'active_score' => $activeScore,
'repeat_score' => $repeatScore,
'safety_score' => $safetyScore,
'suggestions' => $healthSuggestions,
]
];
$this->success('获取成功', null, [
'user_data' => $userData,
'agent_data' => $agentData ?? null,
'share_data' => $shareData,
'wealth_data' => $wealthData,
'madow_data' => $madowData,
'voucher_data' => $voucherData,
'partner_network' => $partnerNetwork,
'finance_overview' => $financeOverview,
'operation_overview' => $operationOverview,
]);
}
public function chart()
{
$date = $this->request->param('date', '');
$date = array_values(array_filter(explode(' - ', $date)));
if (empty($date) || count($date) < 2) {
$date = [date('Y-m-d 00:00:00'), date('Y-m-d 23:59:59')];
}
$orders = Order::with(['items'])->whereTime('createtime', 'between', $date)
->order('id', 'asc')->select();
// 订单数
$data['orderNum'] = count($orders);
$data['orderArr'] = [];
// 支付订单(包含退款的订单, 不包含货到付款还未收货(未支付)订单)
$data['payOrderNum'] = 0;
$data['payOrderArr'] = [];
// 支付金额(包含退款的订单, 不包含货到付款还未收货(未支付)订单)
$data['payAmountNum'] = 0;
$data['payAmountArr'] = [];
// 支付用户
$userIds = [];
$data['payUserNum'] = 0;
$data['payUserArr'] = [];
// 待发货(包含货到付款)
$data['noSendNum'] = 0;
$data['noSendArr'] = [];
// 售后维权
$data['aftersaleNum'] = 0;
$data['aftersaleArr'] = [];
// 退款订单
$data['refundNum'] = 0;
$data['refundArr'] = [];
foreach ($orders as $key => $order) {
$data['orderArr'][] = [
'counter' => 1,
'createtime' => $order->getData('createtime') * 1000,
'user_id' => $order->user_id
];
// 已支付的,不包含货到付款未支付的
if (in_array($order->status, [Order::STATUS_PAID, Order::STATUS_COMPLETED])) {
$data['payOrderNum']++;
$data['payOrderArr'][] = [
'counter' => 1,
'createtime' => $order->getData('createtime') * 1000,
'user_id' => $order->user_id
];
// 支付金额
$data['payAmountNum'] = bcadd((string)$data['payAmountNum'], $order->pay_fee, 2);
$data['payAmountArr'][] = [
'counter' => (float) $order->pay_fee,
'createtime' => $order->getData('createtime') * 1000,
];
// 下单用户
if (!in_array($order->user_id, $userIds)) {
$data['payUserNum']++;
$data['payUserArr'][] = [
'counter' => 1,
'createtime' => $order->getData('createtime') * 1000,
'user_id' => $order->user_id
];
}
}
// 已支付的和货到付款未支付的
if (in_array($order->status, [Order::STATUS_PAID, Order::STATUS_COMPLETED]) || $order->isOffline($order)) {
$flagnoSend = false;
$flagaftersale = false;
$flagrefund = false;
$aftersaleIng = false;
foreach ($order->items as $k => $item) {
if (
!$flagnoSend
&& $item->dispatch_status == OrderItem::DISPATCH_STATUS_NOSEND
&& $item->refund_status == OrderItem::REFUND_STATUS_NOREFUND
&& in_array($order->apply_refund_status, [
Order::APPLY_REFUND_STATUS_NOAPPLY,
Order::APPLY_REFUND_STATUS_REFUSE
])
) {
$flagnoSend = true;
}
if (
$item->aftersale_status == OrderItem::AFTERSALE_STATUS_ING
&& $item->dispatch_status == OrderItem::DISPATCH_STATUS_NOSEND
&& $item->refund_status == OrderItem::REFUND_STATUS_NOREFUND
) {
$aftersaleIng = true;
}
if (!$flagaftersale && $item->aftersale_status != OrderItem::AFTERSALE_STATUS_NOAFTER) {
$data['aftersaleNum']++;
$flagaftersale = true;
}
if (!$flagrefund && $item->refund_status > OrderItem::REFUND_STATUS_NOREFUND) {
$data['refundNum']++;
$flagrefund = true;
}
}
if (!$aftersaleIng && $flagnoSend) {
$data['noSendNum']++;
}
}
}
// ================= 财富与收益分成时段穿透 (新增) =================
$rewards = Reward::where('status', 1)
->whereTime('createtime', 'between', $date)
->order('id', 'asc')
->select();
$payoutTotal = 0.00;
$payoutArr = [];
$periodMadow = 0.00000000;
// 四大分成板块结构
$structureMap = [
'push3' => [
'key' => 'push3',
'name' => '直推裂变(推三返一)',
'types' => ['push3'],
'amount' => 0.00,
'count' => 0,
'color' => '#806AF6',
],
'maker' => [
'key' => 'maker',
'name' => '创客团队(极差/平级/超越)',
'types' => ['maker_diff', 'maker_peer', 'maker_overtaken'],
'amount' => 0.00,
'count' => 0,
'color' => '#13c2c2',
],
'channel' => [
'key' => 'channel',
'name' => '实体渠道(馆县拆分/平级)',
'types' => ['team_venue', 'team_county', 'venue_peer', 'county_peer'],
'amount' => 0.00,
'count' => 0,
'color' => '#fa8c16',
],
'share' => [
'key' => 'share',
'name' => '全网共享(平台日结均分)',
'types' => ['venue_share', 'county_share'],
'amount' => 0.00,
'count' => 0,
'color' => '#eb2f96',
],
];
// 详细奖项映射
$detailList = [];
foreach (PartnerConfig::rewardTypeMap() as $typeKey => $typeName) {
if (in_array($typeKey, ['madow_share', 'voucher_grant', 'voucher_release'])) {
continue;
}
$detailList[$typeKey] = [
'type' => $typeKey,
'name' => $typeName,
'amount' => 0.00,
'count' => 0,
];
}
foreach ($rewards as $reward) {
$amt = (float) $reward->amount;
$rwType = $reward->reward_type;
if ($rwType === 'madow_share') {
$periodMadow = function_exists('bcadd')
? (float) bcadd((string)$periodMadow, (string)($reward->madow ?? 0), 8)
: ($periodMadow + (float)($reward->madow ?? 0));
continue;
}
if ($amt > 0) {
$payoutTotal = round($payoutTotal + $amt, 2);
$payoutArr[] = [
'counter' => $amt,
'createtime' => $reward->getData('createtime') * 1000,
'type' => $rwType,
];
// 归类到板块
foreach ($structureMap as $catKey => &$cat) {
if (in_array($rwType, $cat['types'])) {
$cat['amount'] = round($cat['amount'] + $amt, 2);
$cat['count']++;
break;
}
}
unset($cat);
if (isset($detailList[$rwType])) {
$detailList[$rwType]['amount'] = round($detailList[$rwType]['amount'] + $amt, 2);
$detailList[$rwType]['count']++;
}
}
}
// 格式化分成板块占比
$splitStructure = [];
foreach ($structureMap as $catKey => $cat) {
$catAmount = (float) $cat['amount'];
$ratio = $payoutTotal > 0 ? round(($catAmount / $payoutTotal) * 100, 1) : 0.0;
$splitStructure[] = [
'key' => $catKey,
'name' => $cat['name'],
'amount' => number_format($catAmount, 2, '.', ''),
'count' => $cat['count'],
'ratio' => $ratio,
'color' => $cat['color'],
];
}
$payAmountNum = (float) $data['payAmountNum'];
$data['payoutNum'] = number_format($payoutTotal, 2, '.', '');
$data['payoutArr'] = $payoutArr;
$data['retentionNum'] = number_format(max(0, round($payAmountNum - $payoutTotal, 2)), 2, '.', '');
$data['payoutRate'] = $payAmountNum > 0 ? round(($payoutTotal / $payAmountNum) * 100, 2) : 0.00;
$data['splitStructure'] = $splitStructure;
$data['rewardDetails'] = array_values($detailList);
$data['periodMadow'] = IdentityService::formatMadow($periodMadow);
$this->success('获取成功', null, $data);
}
public function ranking()
{
// 1. 商品销量排行 (原有)
$goods = Goods::limit(5)->order('sales', 'desc')->select();
foreach ($goods as $key => $gd) {
$gd->append(['real_sales']);
$result = OrderItem::field('sum(goods_num * goods_price) as sale_total_money')->where('goods_id', $gd['id'])
->whereExists(function ($query) use ($gd) {
$order_table_name = (new Order())->getQuery()->getTable();
$table_name = (new OrderItem())->getQuery()->getTable();
$query->table($order_table_name)->where($table_name . '.order_id=' . $order_table_name . '.id')
->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED]); // 已支付的订单
})->find();
$gd['sale_total_money'] = $result['sale_total_money'] ?: 0;
}
// 2. 热搜排行 (原有)
$searchHistory = new SearchHistory();
$hotSearch = $searchHistory->hotSearch();
// 3. 合伙股东收益排行 TOP 5 (新增)
$partnerRanking = [];
$rankingRows = Reward::field('user_id, sum(amount) as total_income, count(*) as reward_count')
->where('status', 1)
->where('user_id', '>', 0)
->group('user_id')
->order('total_income', 'desc')
->limit(5)
->select();
foreach ($rankingRows as $row) {
$user = User::field('id, nickname, avatar, mobile')->where('id', $row->user_id)->find();
$agent = Agent::where('user_id', $row->user_id)->find();
$identity = IdentityService::build($agent);
$partnerRanking[] = [
'user_id' => $row->user_id,
'nickname' => $user ? ($user->nickname ?: '用户#' . $row->user_id) : '用户#' . $row->user_id,
'avatar' => $user ? $user->avatar : '',
'mobile' => $user ? $user->mobile : '',
'total_income' => number_format((float)$row->total_income, 2, '.', ''),
'reward_count' => (int) $row->reward_count,
'identity_text' => $identity['identity_text'] ?: '普通会员',
'is_partner' => $identity['is_partner'],
'direct_order_seq' => $agent ? (int) $agent->direct_order_seq : 0,
];
}
// 4. 最新日结概览 (新增)
$dailyReport = DailyReport::order('settle_date', 'desc')->find();
$recentReport = null;
if ($dailyReport) {
$recentReport = [
'settle_date' => $dailyReport->settle_date,
'gmv' => number_format((float)$dailyReport->gmv, 2, '.', ''),
'order_count' => (int)$dailyReport->order_count,
'order_count_n' => (int)$dailyReport->order_count_n,
'rate' => number_format((float)$dailyReport->rate, 10, '.', ''),
'madow_pool' => IdentityService::formatMadow($dailyReport->madow_pool),
'share_user_count' => (int)$dailyReport->share_user_count,
'venue_pool' => number_format((float)$dailyReport->venue_pool, 2, '.', ''),
'venue_user_count' => (int)$dailyReport->venue_user_count,
'county_pool' => number_format((float)$dailyReport->county_pool, 2, '.', ''),
'county_user_count' => (int)$dailyReport->county_user_count,
'voucher_release_total' => number_format((float)$dailyReport->voucher_release_total, 2, '.', ''),
'status' => (int)$dailyReport->status,
'status_text' => $dailyReport->status_text,
];
}
$this->success('获取成功', null, [
'goods' => $goods,
'hot_search' => $hotSearch,
'partner_ranking' => $partnerRanking,
'daily_report_recent' => $recentReport,
]);
}
}