fix(dashboard): 优化控制台财务与运营口径,彻底消除误导信息并实现实时待办防漏单
这个提交包含在:
@@ -16,9 +16,11 @@ use addons\shopro\service\SearchHistory;
|
||||
use addons\shopro\service\partner\RateService;
|
||||
use addons\shopro\service\partner\IdentityService;
|
||||
use addons\shopro\service\partner\PartnerConfig;
|
||||
use addons\shopro\service\partner\ActiveService;
|
||||
use app\admin\model\shopro\Withdraw;
|
||||
use app\admin\model\shopro\Refund;
|
||||
use app\admin\model\shopro\goods\SkuPrice as SkuPriceModel;
|
||||
use app\admin\model\shopro\order\Aftersale;
|
||||
|
||||
class Dashboard extends Common
|
||||
{
|
||||
@@ -82,33 +84,58 @@ class Dashboard extends Common
|
||||
})
|
||||
];
|
||||
|
||||
// 4. 全局财富大盘与资金流 (专业财富指标)
|
||||
// 4. 全局实收流水与资金大盘 (以 paid_time 为准,与商户号入账对齐)
|
||||
$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');
|
||||
|
||||
$todayStart = strtotime('today');
|
||||
$todayEnd = strtotime('tomorrow') - 1;
|
||||
$getTodayPaidQuery = function() use ($paidStatuses, $todayStart, $todayEnd) {
|
||||
return Order::whereIn('status', $paidStatuses)
|
||||
->whereRaw("((paid_time >= {$todayStart} AND paid_time <= {$todayEnd}) OR ((paid_time IS NULL OR paid_time = 0) AND createtime >= {$todayStart} AND createtime <= {$todayEnd}))");
|
||||
};
|
||||
$todayGmv = (float) $getTodayPaidQuery()->sum('pay_fee');
|
||||
$paidOrderCount = (int) Order::whereIn('status', $paidStatuses)->count();
|
||||
$todayPaidOrderCount = (int) Order::whereIn('status', $paidStatuses)->whereTime('createtime', 'today')->count();
|
||||
$todayPaidOrderCount = (int) $getTodayPaidQuery()->count();
|
||||
$avgOrderAmount = $paidOrderCount > 0 ? round($totalGmv / $paidOrderCount, 2) : 0.00;
|
||||
|
||||
// 退款数据统计
|
||||
$totalRefundAmount = (float) Refund::where('status', 'completed')->sum('refund_fee');
|
||||
$totalRefundCount = (int) Refund::where('status', 'completed')->count();
|
||||
$todayRefundAmount = (float) Refund::where('status', 'completed')->whereTime('createtime', 'today')->sum('refund_fee');
|
||||
|
||||
// 净营收 (GMV 扣除退款)
|
||||
$netGmv = max(0, round($totalGmv - $totalRefundAmount, 2));
|
||||
$todayNetGmv = max(0, round($todayGmv - $todayRefundAmount, 2));
|
||||
$refundRate = $totalGmv > 0 ? round(($totalRefundAmount / $totalGmv) * 100, 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;
|
||||
|
||||
// 平台有效留存 = 有效净 GMV (扣除退款后) - 真实分成总支出
|
||||
$netRetention = round($netGmv - $totalPayout, 2);
|
||||
// 真实综合分成拨比 = 真实分成支出 / 有效净 GMV
|
||||
$payoutRate = $netGmv > 0 ? round(($totalPayout / $netGmv) * 100, 2) : 0.00;
|
||||
|
||||
$wealthData = [
|
||||
'total_gmv' => number_format($totalGmv, 2, '.', ''),
|
||||
'today_gmv' => number_format($todayGmv, 2, '.', ''),
|
||||
'total_refund_amount' => number_format($totalRefundAmount, 2, '.', ''),
|
||||
'total_refund_count' => $totalRefundCount,
|
||||
'net_gmv' => number_format($netGmv, 2, '.', ''),
|
||||
'today_net_gmv' => number_format($todayNetGmv, 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, '.', ''),
|
||||
'refund_rate' => number_format($refundRate, 2, '.', ''),
|
||||
'paid_order_count' => $paidOrderCount,
|
||||
'today_paid_order_count' => $todayPaidOrderCount,
|
||||
'avg_order_amount' => number_format($avgOrderAmount, 2, '.', ''),
|
||||
];
|
||||
|
||||
// 5. 马豆生态资产蓄水池
|
||||
// 5. 生态营销资产蓄水池 (基于日结订单利润均分释放,单向升值赋能,无现金刚性回购)
|
||||
$totalMadow = (float) Agent::sum('madow');
|
||||
$rateService = new RateService();
|
||||
$currentRate = $rateService->currentRate();
|
||||
@@ -121,13 +148,13 @@ class Dashboard extends Common
|
||||
$madowData = [
|
||||
'total_madow' => IdentityService::formatMadow($totalMadow),
|
||||
'current_rate' => number_format($currentRate, 10, '.', ''),
|
||||
'rate_per_wan' => number_format($currentRate * 10000, 4, '.', ''), // 每万豆价值折算
|
||||
'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. 消费兑换券杠杆池
|
||||
// 6. 消费兑换券杠杆池 (商城购物抵扣补贴,非现金兑付,无现金偿债压力)
|
||||
$totalVoucher = (float) VoucherPlan::sum('total_amount');
|
||||
$releasedVoucher = (float) VoucherPlan::sum('released_amount');
|
||||
$usedVoucher = (float) Agent::sum('voucher_used');
|
||||
@@ -142,7 +169,7 @@ class Dashboard extends Common
|
||||
'available_voucher' => number_format($availableVoucher, 2, '.', ''),
|
||||
];
|
||||
|
||||
// 7. 合伙股东网络与全角色身份资产矩阵 (会员数、马豆数量、分佣数量、马豆价值)
|
||||
// 7. 合伙股东网络与全角色身份矩阵 (收益按角色专属类型归集,杜绝重复统计)
|
||||
$totalRegisteredUsers = (int) User::count();
|
||||
$totalAgents = (int) Agent::count();
|
||||
$normalUserCount = max(0, $totalRegisteredUsers - $totalAgents);
|
||||
@@ -156,28 +183,49 @@ class Dashboard extends Common
|
||||
$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;
|
||||
// 30天自购活跃度核算 (对接 ActiveService 标准服务类)
|
||||
$activeService = new ActiveService();
|
||||
$now = time();
|
||||
$activeDays = PartnerConfig::activeDays();
|
||||
$activeWindowStart = $activeService->windowStart($now);
|
||||
$warningWindowStart = $now - (($activeDays - 7) * 86400);
|
||||
|
||||
// 全量计算各角色身份名下资产与分佣
|
||||
// 获取全量合格自购活跃用户 ID 集合 (含历史有效订单容错)
|
||||
$activeUserIds = $activeService->activeUserIds($activeWindowStart, $now);
|
||||
$activeUserMap = array_flip($activeUserIds);
|
||||
|
||||
// 全量合伙人基础信息
|
||||
$allAgents = Agent::field('user_id, level, is_venue, is_county, madow, last_self_order_time')->select();
|
||||
|
||||
// 收益明细按 (user_id + reward_type) 聚合,精确归集各角色产出收益
|
||||
$userRewardAgg = Reward::where('status', 1)
|
||||
->where('user_id', '>', 0)
|
||||
->field('user_id, sum(amount) as total_amount, count(*) as count')
|
||||
->group('user_id')
|
||||
->field('user_id, reward_type, sum(amount) as total_amount, count(*) as count')
|
||||
->group('user_id, reward_type')
|
||||
->select();
|
||||
|
||||
$userRewards = [];
|
||||
$userTypeRewards = [];
|
||||
foreach ($userRewardAgg as $rw) {
|
||||
$userRewards[$rw->user_id] = [
|
||||
$uid = (int) $rw->user_id;
|
||||
$rType = (string) $rw->reward_type;
|
||||
if (!isset($userTypeRewards[$uid])) {
|
||||
$userTypeRewards[$uid] = [];
|
||||
}
|
||||
$userTypeRewards[$uid][$rType] = [
|
||||
'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) {
|
||||
// 角色专属收益类型映射表
|
||||
$roleRewardTypeFilter = [
|
||||
'taster' => ['push3'],
|
||||
'maker' => ['maker_diff', 'maker_peer', 'maker_overtaken'],
|
||||
'venue' => ['team_venue', 'venue_peer', 'venue_share'],
|
||||
'county' => ['team_county', 'county_peer', 'county_share'],
|
||||
];
|
||||
|
||||
$calcRoleStats = function ($filterCallback, $roleKey, $roleName, $category, $unit, $badge, $color, $filterLevel = '', $filterType = '', $rewardTypeCategory = null) use ($allAgents, $userTypeRewards, $currentRate, $activeUserMap) {
|
||||
$uCount = 0;
|
||||
$madowTotal = 0.0;
|
||||
$rewardAmt = 0.0;
|
||||
@@ -188,12 +236,25 @@ class Dashboard extends Common
|
||||
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'];
|
||||
$uid = (int) $ag->user_id;
|
||||
|
||||
// 收益按角色类型精准归集
|
||||
if (isset($userTypeRewards[$uid])) {
|
||||
foreach ($userTypeRewards[$uid] as $rtype => $rdata) {
|
||||
$match = false;
|
||||
if ($rewardTypeCategory === null) {
|
||||
$match = true;
|
||||
} elseif (is_array($rewardTypeCategory) && in_array($rtype, $rewardTypeCategory)) {
|
||||
$match = true;
|
||||
}
|
||||
if ($match) {
|
||||
$rewardAmt = round($rewardAmt + $rdata['amount'], 2);
|
||||
$rewardCnt += $rdata['count'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((int) $ag->getData('last_self_order_time') >= $activeWindow) {
|
||||
|
||||
if (isset($activeUserMap[$uid])) {
|
||||
$activeCnt++;
|
||||
}
|
||||
}
|
||||
@@ -217,6 +278,7 @@ class Dashboard extends Common
|
||||
'reward_count' => $rewardCnt,
|
||||
'active_count' => $activeCnt,
|
||||
'active_rate' => $activeRate,
|
||||
'is_exempt_active' => false,
|
||||
'filter_level' => $filterLevel,
|
||||
'filter_type' => $filterType,
|
||||
];
|
||||
@@ -224,20 +286,19 @@ class Dashboard extends Common
|
||||
|
||||
// 统计各角色身份详细指标
|
||||
$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'),
|
||||
$calcRoleStats(function($a) { return (int)$a->level == 1; }, 'taster', '品酒师', '基础身份', '人', '品', '#806af6', 1, '', $roleRewardTypeFilter['taster']),
|
||||
$calcRoleStats(function($a) { return (int)$a->level == 2; }, 'maker_v1', '创客V1', '创客梯队', '人', 'V1', '#13c2c2', 2, '', $roleRewardTypeFilter['maker']),
|
||||
$calcRoleStats(function($a) { return (int)$a->level == 3; }, 'maker_v2', '创客V2', '创客梯队', '人', 'V2', '#13c2c2', 3, '', $roleRewardTypeFilter['maker']),
|
||||
$calcRoleStats(function($a) { return (int)$a->level == 4; }, 'maker_v3', '创客V3', '创客梯队', '人', 'V3', '#13c2c2', 4, '', $roleRewardTypeFilter['maker']),
|
||||
$calcRoleStats(function($a) { return (int)$a->level == 5; }, 'maker_v4', '创客V4', '创客梯队', '人', 'V4', '#13c2c2', 5, '', $roleRewardTypeFilter['maker']),
|
||||
$calcRoleStats(function($a) { return (int)$a->level == 6; }, 'maker_v5', '创客V5', '创客梯队', '人', 'V5', '#13c2c2', 6, '', $roleRewardTypeFilter['maker']),
|
||||
$calcRoleStats(function($a) { return (int)$a->level == 7; }, 'maker_v6', '创客V6', '创客梯队', '人', 'V6', '#13c2c2', 7, '', $roleRewardTypeFilter['maker']),
|
||||
$calcRoleStats(function($a) { return (int)$a->level >= 2; }, 'maker_total', '创客队伍小计', '创客梯队', '人', '创', '#13c2c2', 'maker', '', $roleRewardTypeFilter['maker']),
|
||||
$calcRoleStats(function($a) { return (int)$a->is_venue == 1; }, 'venue', '实体酒道馆', '渠道授权', '家', '馆', '#fa8c16', '', 'venue', $roleRewardTypeFilter['venue']),
|
||||
$calcRoleStats(function($a) { return (int)$a->is_county == 1; }, 'county', '区县代理', '渠道授权', '个', '县', '#722ed1', '', 'county', $roleRewardTypeFilter['county']),
|
||||
];
|
||||
|
||||
// 增加普通注册会员行
|
||||
// 增加普通注册会员行 (明确标明无需自购考核)
|
||||
$roleList[] = [
|
||||
'role_key' => 'normal_user',
|
||||
'role_name' => '普通注册会员',
|
||||
@@ -251,18 +312,34 @@ class Dashboard extends Common
|
||||
'madow_cny_raw' => 0.0,
|
||||
'reward_amount' => '0.00',
|
||||
'reward_count' => 0,
|
||||
'active_count' => 0,
|
||||
'active_rate' => 0.0,
|
||||
'active_count' => '-',
|
||||
'active_rate' => 100,
|
||||
'is_exempt_active' => true,
|
||||
'active_text' => '无需自购考核',
|
||||
'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;
|
||||
}
|
||||
// 全平台全员合计行 (总马豆与总分佣与顶部大盘完全一致)
|
||||
$allTotalRow = [
|
||||
'role_key' => 'all_total',
|
||||
'role_name' => '全平台全员合计',
|
||||
'category' => '全网总计',
|
||||
'unit' => '人',
|
||||
'badge' => '全',
|
||||
'color' => '#1890ff',
|
||||
'user_count' => $totalRegisteredUsers,
|
||||
'madow_total' => IdentityService::formatMadow($totalMadow),
|
||||
'madow_cny' => number_format($totalMadowCny, 2, '.', ''),
|
||||
'madow_cny_raw' => $totalMadowCny,
|
||||
'reward_amount' => number_format($totalPayout, 2, '.', ''),
|
||||
'reward_count' => (int) Reward::where('status', 1)->count(),
|
||||
'active_count' => count($activeUserIds),
|
||||
'active_rate' => $totalAgents > 0 ? round((count($activeUserIds) / $totalAgents) * 100, 1) : 0.0,
|
||||
'is_exempt_active' => false,
|
||||
'filter_level' => '',
|
||||
'filter_type' => 'all',
|
||||
];
|
||||
$roleList[] = $allTotalRow;
|
||||
|
||||
$roleStats = [];
|
||||
@@ -270,6 +347,10 @@ class Dashboard extends Common
|
||||
$roleStats[$rItem['role_key']] = $rItem;
|
||||
}
|
||||
|
||||
$activePartnersCount = count($activeUserIds);
|
||||
$inactiveCount = max(0, $totalAgents - $activePartnersCount);
|
||||
$activeRate = $totalAgents > 0 ? round(($activePartnersCount / $totalAgents) * 100, 1) : 0.0;
|
||||
|
||||
$partnerNetwork = [
|
||||
'total_registered_users' => $totalRegisteredUsers,
|
||||
'total_agents' => $totalAgents,
|
||||
@@ -279,14 +360,14 @@ class Dashboard extends Common
|
||||
'maker_levels' => $makerLevels,
|
||||
'venue_count' => $venueCount,
|
||||
'county_count' => $countyCount,
|
||||
'active_count' => $activeCount,
|
||||
'active_count' => $activePartnersCount,
|
||||
'inactive_count' => $inactiveCount,
|
||||
'active_rate' => $activeRate,
|
||||
'role_list' => $roleList,
|
||||
'role_stats' => $roleStats,
|
||||
];
|
||||
|
||||
// 8. CFO 视角:资金浮存、提现出款风控与资产负债中枢
|
||||
// 8. CFO 视角:平台资金池结余、刚性法币流动负债与提现风控
|
||||
$userCommissionFloat = (float) User::sum('commission');
|
||||
$userMoneyFloat = (float) User::sum('money');
|
||||
$totalFloatFunds = round($userCommissionFloat + $userMoneyFloat, 2);
|
||||
@@ -295,40 +376,39 @@ class Dashboard extends Common
|
||||
$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;
|
||||
// 提现审核完成率 = 已打款出款 / 累计申请总额
|
||||
$withdrawAuditRate = $totalWithdrawApplied > 0 ? round(($totalWithdrawPaid / $totalWithdrawApplied) * 100, 2) : 0.0;
|
||||
// 佣金提现转化率 = 已打款出款 / 累计产生分成
|
||||
$commissionCashRate = $totalPayout > 0 ? round(($totalWithdrawPaid / $totalPayout) * 100, 2) : 0.0;
|
||||
|
||||
// 平台商户号可用流动资金结余 = 实付总额 - 累计退款 - 已打款出款
|
||||
$realNetCashFlow = round($totalGmv - $totalRefundAmount - $totalWithdrawPaid, 2);
|
||||
|
||||
// 未来总承兑责任 (待释放消费券 + 马豆折算市值 + 用户未提现佣金)
|
||||
$pendingVoucherLiability = $pendingVoucher;
|
||||
$madowMarketLiability = $totalMadowCny;
|
||||
$commissionLiability = $userCommissionFloat;
|
||||
$totalFutureLiability = round($pendingVoucherLiability + $madowMarketLiability + $commissionLiability, 2);
|
||||
// 平台刚性法币现金流动负债 = 用户未提现佣金 + 用户账户可用余额 + 待审核打款提现
|
||||
$rigidCashLiability = round($userCommissionFloat + $userMoneyFloat + $pendingWithdrawAmount, 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,
|
||||
'rigid_cash_liability' => number_format($rigidCashLiability, 2, '.', ''), // 刚性法币流动负债
|
||||
'total_withdraw_applied' => number_format($totalWithdrawApplied, 2, '.', ''),
|
||||
'total_withdraw_paid' => number_format($totalWithdrawPaid, 2, '.', ''),
|
||||
'withdraw_audit_rate' => $withdrawAuditRate, // 提现审核完成率
|
||||
'commission_cash_rate' => $commissionCashRate, // 佣金提现转化率
|
||||
'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, '.', ''),
|
||||
'real_net_cash_flow' => number_format($realNetCashFlow, 2, '.', ''), // 真实资金留存
|
||||
// 生态非现金权益存量(清晰标注)
|
||||
'madow_market_val' => number_format($totalMadowCny, 2, '.', ''),
|
||||
'pending_voucher_quota' => number_format($pendingVoucher, 2, '.', ''),
|
||||
];
|
||||
|
||||
// 9. COO 视角:转化复购漏斗、合伙人30天掉队雷达与供应链库存安全
|
||||
// 9.1 用户购买频次聚合与转化漏斗
|
||||
// 9. COO 视角:转化复购漏斗、合伙人自购续期雷达、供应链库存与实时待办看板
|
||||
$paidOrderUsers = Order::whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED])
|
||||
->group('user_id')
|
||||
->field('user_id, count(*) as buy_count')
|
||||
@@ -346,22 +426,16 @@ class Dashboard extends Common
|
||||
}
|
||||
$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);
|
||||
$arpu = $paidUsersCount > 0 ? round($netGmv / $paidUsersCount, 2) : 0.0;
|
||||
|
||||
// 9.2 合伙人 30 天自购续期雷达 (客观四态分类,避免恐慌误判)
|
||||
$activePartnersCount = 0;
|
||||
$warningPartnersCount = 0;
|
||||
$frozenPartnersCount = 0;
|
||||
$pendingRenewCount = 0;
|
||||
$newNoOrderCount = 0;
|
||||
$warningList = [];
|
||||
$frozenList = [];
|
||||
$pendingRenewList = [];
|
||||
|
||||
// 批量拉取合伙人用户信息,避免循环内查询
|
||||
$agentUserIds = array_column(collection($allAgents)->toArray(), 'user_id');
|
||||
$agentUserMap = [];
|
||||
if (!empty($agentUserIds)) {
|
||||
@@ -372,8 +446,7 @@ class Dashboard extends Common
|
||||
}
|
||||
|
||||
foreach ($allAgents as $ag) {
|
||||
$lastTime = (int) $ag->getData('last_self_order_time');
|
||||
$uid = $ag->user_id;
|
||||
$uid = (int) $ag->user_id;
|
||||
$u = $agentUserMap[$uid] ?? null;
|
||||
$nickname = $u ? ($u->nickname ?: '用户#' . $uid) : ('用户#' . $uid);
|
||||
$mobile = ($u && $u->mobile) ? substr_replace($u->mobile, '****', 3, 4) : '-';
|
||||
@@ -381,36 +454,46 @@ class Dashboard extends Common
|
||||
$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[] = [
|
||||
$lastTime = (int) $ag->getData('last_self_order_time');
|
||||
$isActive = isset($activeUserMap[$uid]);
|
||||
|
||||
if ($isActive) {
|
||||
if ($lastTime > 0 && $lastTime < $warningWindowStart) {
|
||||
// 临近续期预警 (1~7天内需续期自购)
|
||||
$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' => $lastTime > 0 ? date('Y-m-d H:i', $lastTime) : '从未自购',
|
||||
'overdue_days' => $overdueDays == 999 ? '从未自购' : "逾期 {$overdueDays} 天",
|
||||
'last_order_date' => date('Y-m-d H:i', $lastTime),
|
||||
'days_left' => $daysLeft,
|
||||
];
|
||||
} else {
|
||||
// 安全活跃达标 (>7天余量)
|
||||
$activePartnersCount++;
|
||||
}
|
||||
} else {
|
||||
if ($lastTime > 0) {
|
||||
// 待续期激活 (已超30天未自购,自购1单即可恢复极差资格)
|
||||
$pendingRenewCount++;
|
||||
if (count($pendingRenewList) < 10) {
|
||||
$overdueDays = max(1, (int) floor(($now - $lastTime) / 86400) - $activeDays);
|
||||
$pendingRenewList[] = [
|
||||
'user_id' => $uid,
|
||||
'nickname' => $nickname,
|
||||
'avatar' => $avatar,
|
||||
'mobile' => $mobile,
|
||||
'level_text' => $identityText,
|
||||
'last_order_date' => date('Y-m-d H:i', $lastTime),
|
||||
'overdue_text' => "超期 {$overdueDays} 天待续",
|
||||
];
|
||||
}
|
||||
} else {
|
||||
// 新成员待自购首购 (尚未下单)
|
||||
$newNoOrderCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,68 +532,60 @@ class Dashboard extends Common
|
||||
];
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
// 9.4 平台客观风控指标看板 (客观真实区间,替换主观伪评分)
|
||||
$riskIndicators = [
|
||||
'payout_rate' => [
|
||||
'val' => $payoutRate,
|
||||
'text' => $payoutRate <= 35 ? '精算安全 (≤35%)' : ($payoutRate <= 50 ? '轻度偏高 (35%~50%)' : '需重点关注 (>50%)'),
|
||||
'status' => $payoutRate <= 35 ? 'success' : ($payoutRate <= 50 ? 'warning' : 'danger'),
|
||||
],
|
||||
'partner_active_rate' => [
|
||||
'val' => $activeRate,
|
||||
'text' => "达标率 {$activeRate}%",
|
||||
'status' => $activeRate >= 50 ? 'success' : 'warning',
|
||||
],
|
||||
'withdraw_audit_rate' => [
|
||||
'val' => $withdrawAuditRate,
|
||||
'text' => "出款率 {$withdrawAuditRate}%",
|
||||
'status' => $withdrawAuditRate >= 90 ? 'success' : 'warning',
|
||||
],
|
||||
'refund_rate' => [
|
||||
'val' => $refundRate,
|
||||
'text' => $refundRate <= 2.0 ? '退款极低 (≤2%)' : ($refundRate <= 5.0 ? '正常可控 (2%~5%)' : '退款偏高 (>5%)'),
|
||||
'status' => $refundRate <= 2.0 ? 'success' : ($refundRate <= 5.0 ? 'warning' : 'danger'),
|
||||
],
|
||||
];
|
||||
|
||||
$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 = [];
|
||||
$riskSuggestions = [];
|
||||
if ($payoutRate <= 35) {
|
||||
$healthSuggestions[] = "当前综合拨比为 {$payoutRate}%,处于精算设定的安全区间,净现金流沉淀稳固。";
|
||||
$riskSuggestions[] = "综合拨比 {$payoutRate}%,处于精算安全区间,净利润沉淀稳固。";
|
||||
} elseif ($payoutRate <= 50) {
|
||||
$riskSuggestions[] = "综合拨比 {$payoutRate}%,略高于基础基准,建议提升高毛利商品动销比例。";
|
||||
} else {
|
||||
$healthSuggestions[] = "当前综合拨比为 {$payoutRate}%,略高于基准安全线,建议关注毛利率较高的商品排期。";
|
||||
$riskSuggestions[] = "综合拨比 {$payoutRate}% 超出常规安全线,请复核极差与日结各奖项拨出参数。";
|
||||
}
|
||||
if ($warningPartnersCount > 0) {
|
||||
$healthSuggestions[] = "监测到有 {$warningPartnersCount} 位合伙人进入 7 天内即将断档预警期,建议及时定向唤醒催单。";
|
||||
} else {
|
||||
$healthSuggestions[] = "合伙股东团队自购状态良好,暂无 7 天内即将断档的合伙人。";
|
||||
}
|
||||
if ($lowStockCount > 0) {
|
||||
$healthSuggestions[] = "仓储有 {$lowStockCount} 款商品 SKU 库存告急(≤10件),请及时补充库存避免断货缺货。";
|
||||
} else {
|
||||
$healthSuggestions[] = "在售商品库存充沛,供应链运转健康。";
|
||||
$riskSuggestions[] = "有 {$warningPartnersCount} 位合伙人进入 7 天临近到期预警期,建议及时定向提醒自购续期。";
|
||||
}
|
||||
if ($pendingWithdrawCount > 0) {
|
||||
$healthSuggestions[] = "当前有 {$pendingWithdrawCount} 笔提现申请等待审核(¥{$financeOverview['pending_withdraw_amount']}),请及时审核保障出款体验。";
|
||||
$riskSuggestions[] = "当前有 {$pendingWithdrawCount} 笔提现申请等待审核(¥{$financeOverview['pending_withdraw_amount']}),请及时处理。";
|
||||
}
|
||||
if ($lowStockCount > 0) {
|
||||
$riskSuggestions[] = "仓储有 {$lowStockCount} 款商品 SKU 库存告急(≤10件),请及时补充库存。";
|
||||
}
|
||||
|
||||
// 9.5 全局实时待办看板 (100% 实时统计,不受日期选择框影响,杜绝漏单漏审)
|
||||
$realtimeNosend = (int) Order::pretendPaid()->nosend()->count();
|
||||
$realtimeAftersale = (int) Aftersale::whereIn('aftersale_status', [Aftersale::AFTERSALE_STATUS_NOOPER, Aftersale::AFTERSALE_STATUS_ING])->count();
|
||||
$realtimeRefundApply = (int) Order::paid()->where('apply_refund_status', Order::APPLY_REFUND_STATUS_APPLY)->count();
|
||||
|
||||
$todoOverview = [
|
||||
'nosend_count' => $realtimeNosend,
|
||||
'aftersale_count' => $realtimeAftersale,
|
||||
'refund_apply_count' => $realtimeRefundApply,
|
||||
'pending_withdraw_count' => $pendingWithdrawCount,
|
||||
'low_stock_count' => $lowStockCount,
|
||||
];
|
||||
|
||||
$operationOverview = [
|
||||
'total_registered' => $totalRegisteredUsers,
|
||||
@@ -522,23 +597,17 @@ class Dashboard extends Common
|
||||
'arpu' => number_format($arpu, 2, '.', ''),
|
||||
'active_partners' => $activePartnersCount,
|
||||
'warning_partners' => $warningPartnersCount,
|
||||
'frozen_partners' => $frozenPartnersCount,
|
||||
'pending_renew_partners' => $pendingRenewCount,
|
||||
'new_no_order_partners' => $newNoOrderCount,
|
||||
'warning_list' => $warningList,
|
||||
'frozen_list' => $frozenList,
|
||||
'pending_renew_list' => $pendingRenewList,
|
||||
'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,
|
||||
]
|
||||
'risk_indicators' => $riskIndicators,
|
||||
'risk_suggestions' => $riskSuggestions,
|
||||
'todo_overview' => $todoOverview,
|
||||
];
|
||||
|
||||
$this->success('获取成功', null, [
|
||||
@@ -563,117 +632,79 @@ class Dashboard extends Common
|
||||
if (empty($date) || count($date) < 2) {
|
||||
$date = [date('Y-m-d 00:00:00'), date('Y-m-d 23:59:59')];
|
||||
}
|
||||
$startTimestamp = strtotime($date[0]);
|
||||
$endTimestamp = strtotime($date[1]);
|
||||
|
||||
$orders = Order::with(['items'])->whereTime('createtime', 'between', $date)
|
||||
->order('id', 'asc')->select();
|
||||
// 1. 时段内支付订单 (以支付时间为准,与分成支出时间严格对齐)
|
||||
$paidOrders = Order::with(['items'])
|
||||
->paid()
|
||||
->whereRaw("((paid_time >= {$startTimestamp} AND paid_time <= {$endTimestamp}) OR ((paid_time IS NULL OR paid_time = 0) AND createtime >= {$startTimestamp} AND createtime <= {$endTimestamp}))")
|
||||
->order('id', 'asc')
|
||||
->select();
|
||||
|
||||
// 订单数
|
||||
$data['orderNum'] = count($orders);
|
||||
// 2. 时段内下单订单数 (反映下单活跃度)
|
||||
$createdOrders = Order::whereTime('createtime', 'between', $date)
|
||||
->order('id', 'asc')
|
||||
->select();
|
||||
|
||||
$data['orderNum'] = count($createdOrders);
|
||||
$data['orderArr'] = [];
|
||||
foreach ($createdOrders as $co) {
|
||||
$rawCt = $co->getData('createtime');
|
||||
$cTime = (is_numeric($rawCt) ? intval($rawCt) : strtotime($rawCt)) * 1000;
|
||||
$data['orderArr'][] = [
|
||||
'counter' => 1,
|
||||
'createtime' => $cTime,
|
||||
'user_id' => $co->user_id,
|
||||
];
|
||||
}
|
||||
|
||||
// 支付订单(包含退款的订单, 不包含货到付款还未收货(未支付)订单)
|
||||
$data['payOrderNum'] = 0;
|
||||
// 3. 支付统计
|
||||
$data['payOrderNum'] = count($paidOrders);
|
||||
$data['payOrderArr'] = [];
|
||||
// 支付金额(包含退款的订单, 不包含货到付款还未收货(未支付)订单)
|
||||
$data['payAmountNum'] = 0;
|
||||
$data['payAmountNum'] = '0.00';
|
||||
$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
|
||||
];
|
||||
}
|
||||
foreach ($paidOrders as $order) {
|
||||
$rawPaid = $order->getData('paid_time');
|
||||
$rawCt = $order->getData('createtime');
|
||||
$oTimestamp = 0;
|
||||
if ($rawPaid) {
|
||||
$oTimestamp = is_numeric($rawPaid) ? intval($rawPaid) : strtotime($rawPaid);
|
||||
} elseif ($rawCt) {
|
||||
$oTimestamp = is_numeric($rawCt) ? intval($rawCt) : strtotime($rawCt);
|
||||
}
|
||||
|
||||
// 已支付的和货到付款未支付的
|
||||
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']++;
|
||||
}
|
||||
$pTime = $oTimestamp * 1000;
|
||||
$data['payOrderArr'][] = [
|
||||
'counter' => 1,
|
||||
'createtime' => $pTime,
|
||||
'user_id' => $order->user_id,
|
||||
];
|
||||
$data['payAmountNum'] = bcadd((string)$data['payAmountNum'], (string)$order->pay_fee, 2);
|
||||
$data['payAmountArr'][] = [
|
||||
'counter' => (float) $order->pay_fee,
|
||||
'createtime' => $pTime,
|
||||
];
|
||||
if (!in_array($order->user_id, $userIds)) {
|
||||
$userIds[] = $order->user_id;
|
||||
$data['payUserNum']++;
|
||||
$data['payUserArr'][] = [
|
||||
'counter' => 1,
|
||||
'createtime' => $pTime,
|
||||
'user_id' => $order->user_id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 财富与收益分成时段穿透 (新增) =================
|
||||
// 全局实时待办数据 (不受时段过滤影响,彻底杜绝漏单漏审)
|
||||
$data['noSendNum'] = (int) Order::pretendPaid()->nosend()->count();
|
||||
$data['aftersaleNum'] = (int) Aftersale::whereIn('aftersale_status', [Aftersale::AFTERSALE_STATUS_NOOPER, Aftersale::AFTERSALE_STATUS_ING])->count();
|
||||
$data['refundNum'] = (int) Order::paid()->where('apply_refund_status', Order::APPLY_REFUND_STATUS_APPLY)->count();
|
||||
|
||||
// 4. 收益分成时段穿透 (以分成入账时间为准)
|
||||
$rewards = Reward::where('status', 1)
|
||||
->whereTime('createtime', 'between', $date)
|
||||
->order('id', 'asc')
|
||||
@@ -746,9 +777,11 @@ class Dashboard extends Common
|
||||
|
||||
if ($amt > 0) {
|
||||
$payoutTotal = round($payoutTotal + $amt, 2);
|
||||
$rCt = $reward->getData('createtime');
|
||||
$rTime = (is_numeric($rCt) ? intval($rCt) : strtotime($rCt)) * 1000;
|
||||
$payoutArr[] = [
|
||||
'counter' => $amt,
|
||||
'createtime' => $reward->getData('createtime') * 1000,
|
||||
'createtime' => $rTime,
|
||||
'type' => $rwType,
|
||||
];
|
||||
|
||||
@@ -800,27 +833,47 @@ class Dashboard extends Common
|
||||
|
||||
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();
|
||||
// 1. 商品真实销量排行 (基于真实已支付订单聚合,杜绝后台虚拟销量刷榜误导)
|
||||
$goodsRanking = \think\Db::name('shopro_order_item')->alias('oi')
|
||||
->join('shopro_order o', 'oi.order_id = o.id')
|
||||
->whereIn('o.status', [Order::STATUS_PAID, Order::STATUS_COMPLETED])
|
||||
->field('oi.goods_id, sum(oi.goods_num) as real_sales, sum(oi.goods_num * oi.goods_price) as sale_total_money')
|
||||
->group('oi.goods_id')
|
||||
->order('real_sales', 'desc')
|
||||
->limit(5)
|
||||
->select();
|
||||
|
||||
$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;
|
||||
$goods = [];
|
||||
if (!empty($goodsRanking)) {
|
||||
$goodsIds = array_column($goodsRanking, 'goods_id');
|
||||
$goodsModels = Goods::whereIn('id', $goodsIds)->select();
|
||||
$goodsMap = [];
|
||||
foreach ($goodsModels as $gm) {
|
||||
$goodsMap[$gm->id] = $gm;
|
||||
}
|
||||
foreach ($goodsRanking as $gr) {
|
||||
$gid = $gr['goods_id'];
|
||||
$gItem = $goodsMap[$gid] ?? null;
|
||||
if ($gItem) {
|
||||
$gItem->real_sales = (int) $gr['real_sales'];
|
||||
$gItem->sale_total_money = (float) $gr['sale_total_money'];
|
||||
$goods[] = $gItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($goods)) {
|
||||
$goods = Goods::where('status', 'up')->limit(5)->order('id', 'desc')->select();
|
||||
foreach ($goods as $gd) {
|
||||
$gd->real_sales = 0;
|
||||
$gd->sale_total_money = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 热搜排行 (原有)
|
||||
// 2. 热搜排行
|
||||
$searchHistory = new SearchHistory();
|
||||
$hotSearch = $searchHistory->hotSearch();
|
||||
|
||||
// 3. 合伙股东收益排行 TOP 5 (新增)
|
||||
// 3. 合伙股东收益排行 TOP 5
|
||||
$partnerRanking = [];
|
||||
$rankingRows = Reward::field('user_id, sum(amount) as total_income, count(*) as reward_count')
|
||||
->where('status', 1)
|
||||
@@ -848,7 +901,7 @@ class Dashboard extends Common
|
||||
];
|
||||
}
|
||||
|
||||
// 4. 最新日结概览 (新增)
|
||||
// 4. 最新日结概览
|
||||
$dailyReport = DailyReport::order('settle_date', 'desc')->find();
|
||||
$recentReport = null;
|
||||
if ($dailyReport) {
|
||||
|
||||
@@ -340,18 +340,18 @@
|
||||
<div class="wealth-kpi-card card">
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span class="kpi-title">商城实收流水 (GMV)</span>
|
||||
<el-tag size="small" type="primary" effect="plain">实付总额</el-tag>
|
||||
<el-tag size="small" type="primary" effect="plain">实付到账</el-tag>
|
||||
</div>
|
||||
<div class="kpi-main-num text-primary">
|
||||
<span class="currency">¥</span>{{ state.wealth.total_gmv }}
|
||||
</div>
|
||||
<div class="kpi-sub-row sa-flex sa-row-between">
|
||||
<span>今日实收:<strong class="text-dark">¥{{ state.wealth.today_gmv }}</strong></span>
|
||||
<span>客单均价:<strong class="text-dark">¥{{ state.wealth.avg_order_amount }}</strong></span>
|
||||
<span>今日实付:<strong class="text-dark">¥{{ state.wealth.today_gmv }}</strong></span>
|
||||
<span>有效净GMV:<strong class="text-success">¥{{ state.wealth.net_gmv }}</strong></span>
|
||||
</div>
|
||||
<div class="kpi-footer sa-flex sa-row-between">
|
||||
<span>累计支付单量</span>
|
||||
<span class="font-bold">{{ state.wealth.paid_order_count }} 单</span>
|
||||
<span>累计支付:<strong>{{ state.wealth.paid_order_count }} 单</strong></span>
|
||||
<span>累计退款:<strong class="text-dark">¥{{ state.wealth.total_refund_amount }}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
@@ -361,8 +361,8 @@
|
||||
<div class="wealth-kpi-card card">
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span class="kpi-title">现金分成支出与拨比</span>
|
||||
<el-tooltip content="现金分成占实付GMV的百分比,评估平台精算模型健康度" placement="top">
|
||||
<el-tag size="small" :type="parseFloat(state.wealth.payout_rate) <= 35 ? 'success' : 'warning'" effect="plain">
|
||||
<el-tooltip content="现金分成占有效净GMV(扣退款后)的百分比,评估平台精算模型健康度" placement="top">
|
||||
<el-tag size="small" :type="parseFloat(state.wealth.payout_rate) <= 35 ? 'success' : (parseFloat(state.wealth.payout_rate) <= 50 ? 'warning' : 'danger')" effect="plain">
|
||||
拨比 {{ state.wealth.payout_rate }}%
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
@@ -371,7 +371,7 @@
|
||||
<span class="currency">¥</span>{{ state.wealth.total_payout }}
|
||||
</div>
|
||||
<div class="kpi-sub-row sa-flex sa-row-between">
|
||||
<span>平台净留存:<strong class="text-success">¥{{ state.wealth.net_retention }}</strong></span>
|
||||
<span>平台有效留存:<strong class="text-success">¥{{ state.wealth.net_retention }}</strong></span>
|
||||
<span class="cursor-pointer text-primary" @click="onOpenReward('')">明细 →</span>
|
||||
</div>
|
||||
<div class="kpi-footer sa-flex sa-row-between">
|
||||
@@ -386,7 +386,9 @@
|
||||
<div class="wealth-kpi-card card">
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span class="kpi-title">马豆生态资产蓄水池</span>
|
||||
<el-tag size="small" type="success" effect="plain">单向升值</el-tag>
|
||||
<el-tooltip content="基于每日日结订单利润均分释放,单向赋能升值,非现金刚性回购" placement="top">
|
||||
<el-tag size="small" type="success" effect="plain">日结均分</el-tag>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="kpi-main-num text-purple">
|
||||
{{ state.madow.total_madow }} <span class="unit">豆</span>
|
||||
@@ -407,7 +409,9 @@
|
||||
<div class="wealth-kpi-card card">
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span class="kpi-title">消费兑换券杠杆池</span>
|
||||
<el-tag size="small" type="info" effect="plain">1.5倍补贴</el-tag>
|
||||
<el-tooltip content="1.5倍购物抵扣补贴,仅限商城消费抵扣,非现金刚性兑付" placement="top">
|
||||
<el-tag size="small" type="info" effect="plain">1.5倍补贴</el-tag>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="kpi-main-num text-danger">
|
||||
<span class="currency">¥</span>{{ state.voucher.total_voucher }}
|
||||
@@ -424,43 +428,43 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- ================= 1.1 CFO 财务全貌 · 资金流向、平台浮存与资产负债中枢 ================= -->
|
||||
<!-- ================= 1.1 CFO 财务全貌 · 资金流向、商户号结余与刚性流动应付中枢 ================= -->
|
||||
<el-row :gutter="16">
|
||||
<!-- 资金浮存池 (Cash Float) -->
|
||||
<!-- 资金结余池 (Cash Float) -->
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
|
||||
<div class="finance-hub-card card">
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<div class="sa-flex">
|
||||
<span class="network-badge bg-primary mr-2" style="background: #1890ff;">浮</span>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">资金浮存池 (Cash Float)</span>
|
||||
<span class="network-badge bg-primary mr-2" style="background: #1890ff;">资</span>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">可用流动资金存量 (商户号预估结余)</span>
|
||||
</div>
|
||||
<el-tag size="small" type="success" effect="light">平台流动免息资金</el-tag>
|
||||
<el-tag size="small" type="success" effect="light">流动资金沉淀</el-tag>
|
||||
</div>
|
||||
<div class="kpi-main-num text-primary" style="font-size: 24px; margin: 8px 0;">
|
||||
<span class="currency">¥</span>{{ state.finance.total_float_funds }}
|
||||
<span class="currency">¥</span>{{ state.finance.real_net_cash_flow }}
|
||||
</div>
|
||||
<div class="finance-sub-metric sa-flex sa-row-between">
|
||||
<span>未提现佣金沉淀 (用户收益留存):</span>
|
||||
<strong class="text-warning">¥{{ state.finance.commission_float }}</strong>
|
||||
</div>
|
||||
<div class="finance-sub-metric sa-flex sa-row-between">
|
||||
<span>用户账户余额沉淀 (已充未销):</span>
|
||||
<span>用户账户余额沉淀 (已充未销资金):</span>
|
||||
<strong class="text-dark">¥{{ state.finance.user_balance_float }}</strong>
|
||||
</div>
|
||||
<div class="sa-flex sa-row-between pt-2" style="font-size: 12px; color: var(--sa-subfont); border-top: 1px dashed var(--sa-space);">
|
||||
<span>真实净现金流 (GMV-出款-退款):</span>
|
||||
<strong class="text-success">¥{{ state.finance.real_net_cash_flow }}</strong>
|
||||
<span>计算口径:实收总额 - 累计退款 - 已打款出款</span>
|
||||
<span class="text-success font-bold">资金池充沛</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 提现出款与资金风控敞口 -->
|
||||
<!-- 提现出款与资金风控 -->
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
|
||||
<div class="finance-hub-card card">
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<div class="sa-flex">
|
||||
<span class="network-badge bg-warning mr-2" style="background: #fa8c16;">提</span>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">提现出款与风控敞口</span>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">提现出款与风控管理</span>
|
||||
</div>
|
||||
<span class="cursor-pointer text-primary" style="font-size: 12px;" @click="onOpenWithdraw('all')">提现流水 →</span>
|
||||
</div>
|
||||
@@ -469,7 +473,7 @@
|
||||
<span class="unit" style="font-size: 12px; font-weight: normal; color: var(--sa-subfont);">已打款出款</span>
|
||||
</div>
|
||||
<div class="finance-sub-metric sa-flex sa-row-between" :style="state.finance.pending_withdraw_count > 0 ? 'background: #fff2e8; border-color: #ffbb96;' : ''">
|
||||
<span>待审核出款风控资金:</span>
|
||||
<span>待审核出款提现资金:</span>
|
||||
<div class="sa-flex">
|
||||
<strong :class="state.finance.pending_withdraw_count > 0 ? 'text-danger' : 'text-dark'">¥{{ state.finance.pending_withdraw_amount }}</strong>
|
||||
<el-badge v-if="state.finance.pending_withdraw_count > 0" :value="state.finance.pending_withdraw_count + '笔'" class="ml-1" type="danger" />
|
||||
@@ -478,7 +482,7 @@
|
||||
</div>
|
||||
<div class="finance-sub-metric sa-flex sa-row-between">
|
||||
<span>累计申请总额:¥{{ state.finance.total_withdraw_applied }}</span>
|
||||
<span>出款兑现率:<strong class="text-purple">{{ state.finance.withdraw_cash_rate }}%</strong></span>
|
||||
<span>提现完成率:<strong class="text-purple">{{ state.finance.withdraw_audit_rate }}%</strong></span>
|
||||
</div>
|
||||
<div class="sa-flex sa-row-between pt-2" style="font-size: 12px; color: var(--sa-subfont); border-top: 1px dashed var(--sa-space);">
|
||||
<span>累计退款:¥{{ state.finance.total_refund_amount }} ({{ state.finance.total_refund_count }}笔)</span>
|
||||
@@ -487,30 +491,30 @@
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 资产负债表与未来承兑责任 -->
|
||||
<!-- 刚性法币现金流动负债 -->
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
|
||||
<div class="finance-hub-card card">
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<div class="sa-flex">
|
||||
<span class="network-badge bg-danger mr-2" style="background: #f5222d;">债</span>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">未来承兑责任准备金 (负债中枢)</span>
|
||||
<span class="network-badge mr-2" style="background: #722ed1;">付</span>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">刚性现金流动负债 (法币应付责任)</span>
|
||||
</div>
|
||||
<el-tag size="small" type="danger" effect="light">履约与承兑储备</el-tag>
|
||||
<el-tag size="small" type="warning" effect="light">流动承兑准备</el-tag>
|
||||
</div>
|
||||
<div class="kpi-main-num text-danger" style="font-size: 24px; margin: 8px 0;">
|
||||
<span class="currency">¥</span>{{ state.finance.total_future_liability }}
|
||||
<div class="kpi-main-num text-purple" style="font-size: 24px; margin: 8px 0;">
|
||||
<span class="currency">¥</span>{{ state.finance.rigid_cash_liability }}
|
||||
</div>
|
||||
<div class="finance-sub-metric sa-flex sa-row-between">
|
||||
<span>① 消费兑换券补贴待释放负债 (1.5倍补贴):</span>
|
||||
<strong class="text-danger">¥{{ state.finance.pending_voucher_liability }}</strong>
|
||||
<span>① 用户未提现分润 (随时可提现):</span>
|
||||
<strong class="text-warning">¥{{ state.finance.commission_float }}</strong>
|
||||
</div>
|
||||
<div class="finance-sub-metric sa-flex sa-row-between">
|
||||
<span>② 马豆流通折算市值负债 (生态回购承兑):</span>
|
||||
<strong class="text-purple">¥{{ state.finance.madow_market_liability }}</strong>
|
||||
<span>② 用户账户可用余额 (可消费/提现):</span>
|
||||
<strong class="text-dark">¥{{ state.finance.user_balance_float }}</strong>
|
||||
</div>
|
||||
<div class="sa-flex sa-row-between pt-2" style="font-size: 12px; color: var(--sa-subfont); border-top: 1px dashed var(--sa-space);">
|
||||
<span>③ 用户未提佣金流动负债:</span>
|
||||
<strong class="text-warning">¥{{ state.finance.commission_liability }}</strong>
|
||||
<span>③ 待审核提现准备金:</span>
|
||||
<strong class="text-danger">¥{{ state.finance.pending_withdraw_amount }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
@@ -606,7 +610,7 @@
|
||||
<div>
|
||||
<span class="font-bold text-dark" style="font-size: 15px;">全角色身份会员资产与分佣统计全景矩阵</span>
|
||||
<span style="font-size: 12px; color: var(--sa-subfont); margin-left: 12px;">
|
||||
逐一统计每一个角色身份的会员人数、持豆存量、实时市值、累计分佣金额与笔数
|
||||
逐一统计每一个角色身份的会员人数、持豆存量、实时市值、专属业务分润与笔数
|
||||
</span>
|
||||
</div>
|
||||
<div class="sa-flex" style="margin-top: 4px;">
|
||||
@@ -655,7 +659,13 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="累计现金分佣" min-width="140" align="right">
|
||||
<el-table-column label="专属业务分佣" min-width="140" align="right">
|
||||
<template #header>
|
||||
<span style="font-size: 13px;">专属业务分佣</span>
|
||||
<el-tooltip content="按各角色专属分润类型归集(如创客极差/酒道馆/区县),杜绝一人兼多职时交叉重复计算" placement="top">
|
||||
<i class="el-icon-info text-muted" style="margin-left: 2px; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<span class="font-bold text-warning" style="font-size: 14px;">¥{{ scope.row.reward_amount }}</span>
|
||||
</template>
|
||||
@@ -668,13 +678,18 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="30天自购活跃度" min-width="170">
|
||||
<el-table-column label="30天自购考核" min-width="170">
|
||||
<template #default="scope">
|
||||
<div class="sa-flex sa-row-between" style="font-size: 12px; margin-bottom: 3px;">
|
||||
<span>{{ scope.row.active_count }} {{ scope.row.unit }} 达标</span>
|
||||
<strong :class="scope.row.active_rate >= 60 ? 'text-success' : 'text-warning'">{{ scope.row.active_rate }}%</strong>
|
||||
<div v-if="scope.row.is_exempt_active" class="text-muted" style="font-size: 12px;">
|
||||
<el-tag size="mini" type="info" effect="plain">{{ scope.row.active_text || '无需自购考核' }}</el-tag>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="sa-flex sa-row-between" style="font-size: 12px; margin-bottom: 3px;">
|
||||
<span>{{ scope.row.active_count }} {{ scope.row.unit }} 达标</span>
|
||||
<strong :class="scope.row.active_rate >= 60 ? 'text-success' : 'text-warning'">{{ scope.row.active_rate }}%</strong>
|
||||
</div>
|
||||
<el-progress :percentage="scope.row.active_rate" :stroke-width="4" :show-text="false" :color="scope.row.active_rate >= 60 ? '#52c41a' : '#fa8c16'"></el-progress>
|
||||
</div>
|
||||
<el-progress :percentage="scope.row.active_rate" :stroke-width="4" :show-text="false" :color="scope.row.active_rate >= 60 ? '#52c41a' : '#fa8c16'"></el-progress>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -737,46 +752,53 @@
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 中间:合伙人 30 天自购断档掉队雷达 -->
|
||||
<!-- 中间:合伙人 30 天自购考核与续期雷达 -->
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
|
||||
<div class="coo-panel-card card">
|
||||
<div class="sa-flex sa-row-between mb-3">
|
||||
<div>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">合伙人 30天自购断档掉队雷达</span>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">合伙人 30天自购考核与续期雷达</span>
|
||||
</div>
|
||||
<span class="cursor-pointer text-primary" style="font-size: 12px;" @click="onOpenAgent('')">合伙团队 →</span>
|
||||
</div>
|
||||
|
||||
<!-- 3状态指标标签 -->
|
||||
<el-row :gutter="8" class="mb-3">
|
||||
<el-col :span="8">
|
||||
<div class="radar-stat-pill" style="background: #f6ffed; border: 1px solid #b7eb8f; text-align: center;">
|
||||
<div style="font-size: 11px; color: #389e0d;">活跃达标</div>
|
||||
<div style="font-size: 18px; font-weight: bold; color: #52c41a; margin-top: 2px;">{{ state.operation.active_partners }}</div>
|
||||
<!-- 4状态指标标签 -->
|
||||
<el-row :gutter="6" class="mb-3">
|
||||
<el-col :span="6">
|
||||
<div class="radar-stat-pill" style="background: #f6ffed; border: 1px solid #b7eb8f; text-align: center; padding: 6px 2px;">
|
||||
<div style="font-size: 11px; color: #389e0d;">达标在保</div>
|
||||
<div style="font-size: 16px; font-weight: bold; color: #52c41a; margin-top: 2px;">{{ state.operation.active_partners }}</div>
|
||||
<div style="font-size: 10px; color: #8c8c8c;">>7天余量</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="radar-stat-pill" style="background: #fffbe6; border: 1px solid #ffe58f; text-align: center;">
|
||||
<div style="font-size: 11px; color: #d46b08;">即将断档</div>
|
||||
<div style="font-size: 18px; font-weight: bold; color: #fa8c16; margin-top: 2px;">{{ state.operation.warning_partners }}</div>
|
||||
<el-col :span="6">
|
||||
<div class="radar-stat-pill" style="background: #fffbe6; border: 1px solid #ffe58f; text-align: center; padding: 6px 2px;">
|
||||
<div style="font-size: 11px; color: #d46b08;">临期预警</div>
|
||||
<div style="font-size: 16px; font-weight: bold; color: #fa8c16; margin-top: 2px;">{{ state.operation.warning_partners }}</div>
|
||||
<div style="font-size: 10px; color: #fa8c16;">≤7天临界</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="radar-stat-pill" style="background: #fff1f0; border: 1px solid #ffa39e; text-align: center;">
|
||||
<div style="font-size: 11px; color: #cf1322;">已断档冻结</div>
|
||||
<div style="font-size: 18px; font-weight: bold; color: #f5222d; margin-top: 2px;">{{ state.operation.frozen_partners }}</div>
|
||||
<div style="font-size: 10px; color: #8c8c8c;">无分佣资格</div>
|
||||
<el-col :span="6">
|
||||
<div class="radar-stat-pill" style="background: #f0f5ff; border: 1px solid #adc6ff; text-align: center; padding: 6px 2px;">
|
||||
<div style="font-size: 11px; color: #1d39c4;">待续期</div>
|
||||
<div style="font-size: 16px; font-weight: bold; color: #2f54eb; margin-top: 2px;">{{ state.operation.pending_renew_partners }}</div>
|
||||
<div style="font-size: 10px; color: #8c8c8c;">超期可恢复</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="radar-stat-pill" style="background: #fafafa; border: 1px solid #d9d9d9; text-align: center; padding: 6px 2px;">
|
||||
<div style="font-size: 11px; color: #595959;">新成员</div>
|
||||
<div style="font-size: 16px; font-weight: bold; color: #595959; margin-top: 2px;">{{ state.operation.new_no_order_partners }}</div>
|
||||
<div style="font-size: 10px; color: #8c8c8c;">待首购激活</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 7天临界断档名单列表(若有则紧急告警,若无则展示健康与已断档) -->
|
||||
<!-- 7天临界断档名单列表(若有则提醒续期,若无则展示待续期列表) -->
|
||||
<div v-if="state.operation.warning_partners > 0">
|
||||
<div class="warning-alert-row mb-2 sa-flex sa-row-between">
|
||||
<span>⚠️ <strong>紧急唤醒:</strong>以下合伙人自购即将到期</span>
|
||||
<span style="font-weight: bold;">{{ state.operation.warning_partners }} 人临界</span>
|
||||
<span>⚠️ <strong>续期提醒:</strong>以下合伙人自购即将到期</span>
|
||||
<span style="font-weight: bold;">{{ state.operation.warning_partners }} 人临期</span>
|
||||
</div>
|
||||
<div style="max-height: 120px; overflow-y: auto;">
|
||||
<div class="sa-flex sa-row-between mb-2 pb-1" v-for="wUser in state.operation.warning_list" :key="wUser.user_id" style="border-bottom: 1px dashed #f0f2f5; font-size: 12px;">
|
||||
@@ -789,7 +811,7 @@
|
||||
</div>
|
||||
<div class="sa-flex">
|
||||
<el-tag size="mini" type="danger">剩余 {{ wUser.days_left }} 天</el-tag>
|
||||
<el-button type="primary" link size="mini" class="ml-1" @click="onOpenAgent('')">催单</el-button>
|
||||
<el-button type="primary" link size="mini" class="ml-1" @click="onOpenAgent('')">提醒</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -797,66 +819,85 @@
|
||||
|
||||
<div v-else>
|
||||
<div class="sa-flex sa-row-between" style="background: #f6ffed; border: 1px solid #b7eb8f; padding: 8px 12px; border-radius: 6px; font-size: 12px; color: #389e0d; margin-bottom: 8px;">
|
||||
<span>🛡️ 合伙股东自购健康度良好,暂无临近断档人员</span>
|
||||
<span>🛡️ 合伙团队在保人员健康,暂无 7 天内临期合伙人</span>
|
||||
<span>全部在保</span>
|
||||
</div>
|
||||
<div style="font-size: 11px; color: var(--sa-subfont); margin-bottom: 4px;">已断档冻结人员采样 (失去分佣资格):</div>
|
||||
<div style="font-size: 11px; color: var(--sa-subfont); margin-bottom: 4px;">超期待续期合伙人 (自购 1 单即可恢复分润资格):</div>
|
||||
<div style="max-height: 100px; overflow-y: auto;">
|
||||
<div class="sa-flex sa-row-between mb-1 pb-1" v-for="fUser in state.operation.frozen_list.slice(0, 3)" :key="fUser.user_id" style="border-bottom: 1px dashed #f0f2f5; font-size: 12px;">
|
||||
<div class="sa-flex sa-row-between mb-1 pb-1" v-for="pUser in state.operation.pending_renew_list.slice(0, 3)" :key="pUser.user_id" style="border-bottom: 1px dashed #f0f2f5; font-size: 12px;">
|
||||
<div class="sa-flex">
|
||||
<span class="text-muted mr-1">#{{ fUser.user_id }}</span>
|
||||
<span class="text-dark">{{ fUser.nickname }}</span>
|
||||
<span class="text-muted ml-1" style="font-size: 11px;">({{ fUser.mobile }})</span>
|
||||
<span class="text-muted mr-1">#{{ pUser.user_id }}</span>
|
||||
<span class="text-dark">{{ pUser.nickname }}</span>
|
||||
<span class="text-muted ml-1" style="font-size: 11px;">({{ pUser.mobile }})</span>
|
||||
</div>
|
||||
<el-tag size="mini" type="danger" effect="plain">{{ fUser.overdue_days }}</el-tag>
|
||||
<el-tag size="mini" type="info" effect="plain">{{ pUser.overdue_text }}</el-tag>
|
||||
</div>
|
||||
<div v-if="!state.operation.pending_renew_list || state.operation.pending_renew_list.length === 0" class="text-muted" style="font-size: 11px; text-align: center; padding: 8px 0;">
|
||||
暂无超期待续期合伙人
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧:供应链库存告急与平台经营健康度评分 -->
|
||||
<!-- 右侧:经营风控指标与供应链安全 -->
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
|
||||
<div class="coo-panel-card card">
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<div>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">经营健康评分与供应链安全</span>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">经营风控指标与供应链安全</span>
|
||||
</div>
|
||||
<el-tag size="small" :style="{ backgroundColor: state.operation.health.grade_color, color: '#fff', borderColor: state.operation.health.grade_color, fontWeight: 'bold' }">
|
||||
{{ state.operation.health.grade }}
|
||||
</el-tag>
|
||||
<el-tag size="small" type="success" effect="light">精算模型监控</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- 综合评分双栏展示 -->
|
||||
<el-row :gutter="12" class="mb-3">
|
||||
<el-col :span="10">
|
||||
<div class="health-score-dial">
|
||||
<div style="font-size: 11px; color: var(--sa-subfont); margin-bottom: 4px;">综合健康度</div>
|
||||
<div class="health-score-val" :style="{ color: state.operation.health.grade_color }">{{ state.operation.health.score }}</div>
|
||||
<div style="font-size: 10px; color: var(--sa-subfont); margin-top: 4px;">满分 100分</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div style="font-size: 11px; color: var(--sa-subfont); line-height: 1.8;">
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span>拨比健康 (30分):</span>
|
||||
<strong class="text-dark">{{ state.operation.health.payout_score }}分</strong>
|
||||
<!-- 4大客观风控指标看板 -->
|
||||
<div class="mb-3" style="background: #fafafa; border: 1px solid #f0f0f0; border-radius: 8px; padding: 10px 12px;">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12" class="mb-2">
|
||||
<div style="font-size: 11px; color: var(--sa-subfont);">实付留存与有效拨比</div>
|
||||
<div class="sa-flex sa-row-between mt-1">
|
||||
<strong class="text-dark" style="font-size: 14px;">{{ state.finance?.effective_payout_rate || state.wealth?.payout_rate || '0' }}%</strong>
|
||||
<el-tag size="mini" :type="state.operation?.risk_indicators?.payout_rate?.status || 'success'">
|
||||
{{ state.operation?.risk_indicators?.payout_rate?.text || '拨比可控' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span>合伙人活力 (25分):</span>
|
||||
<strong class="text-dark">{{ state.operation.health.active_score }}分</strong>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb-2">
|
||||
<div style="font-size: 11px; color: var(--sa-subfont);">合伙人自购达标率</div>
|
||||
<div class="sa-flex sa-row-between mt-1">
|
||||
<strong class="text-dark" style="font-size: 14px;">{{ state.partner?.active_rate || 0 }}%</strong>
|
||||
<el-tag size="mini" :type="state.operation?.risk_indicators?.partner_active_rate?.status || 'success'">
|
||||
{{ state.operation?.risk_indicators?.partner_active_rate?.text || '正常' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span>复购粘性 (25分):</span>
|
||||
<strong class="text-dark">{{ state.operation.health.repeat_score }}分</strong>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div style="font-size: 11px; color: var(--sa-subfont);">提现出款完成率</div>
|
||||
<div class="sa-flex sa-row-between mt-1">
|
||||
<strong class="text-dark" style="font-size: 14px;">{{ state.finance?.withdraw_audit_rate || 0 }}%</strong>
|
||||
<el-tag size="mini" :type="state.operation?.risk_indicators?.withdraw_audit_rate?.status || 'success'">
|
||||
{{ state.operation?.risk_indicators?.withdraw_audit_rate?.text || '正常' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span>履约安全 (20分):</span>
|
||||
<strong class="text-dark">{{ state.operation.health.safety_score }}分</strong>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div style="font-size: 11px; color: var(--sa-subfont);">全店退款率</div>
|
||||
<div class="sa-flex sa-row-between mt-1">
|
||||
<strong class="text-dark" style="font-size: 14px;">{{ state.finance?.refund_rate || 0 }}%</strong>
|
||||
<el-tag size="mini" :type="state.operation?.risk_indicators?.refund_rate?.status || 'success'">
|
||||
{{ state.operation?.risk_indicators?.refund_rate?.text || '正常' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 经营建议/预警提示 -->
|
||||
<div v-if="state.operation?.risk_suggestions && state.operation?.risk_suggestions.length > 0" class="mb-2" style="font-size: 11px; color: #595959; background: #fffbe6; border: 1px solid #ffe58f; padding: 6px 10px; border-radius: 6px; line-height: 1.6;">
|
||||
<div v-for="(sug, sIdx) in state.operation.risk_suggestions.slice(0, 2)" :key="sIdx">
|
||||
💡 {{ sug }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 库存告急监测 -->
|
||||
<div class="sa-flex sa-row-between mb-1" style="border-top: 1px dashed var(--sa-space); padding-top: 8px;">
|
||||
@@ -879,7 +920,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-muted sa-flex sa-row-center" style="font-size: 12px; padding: 10px 0;">
|
||||
<div v-else class="text-muted sa-flex sa-row-center" style="font-size: 12px; padding: 6px 0;">
|
||||
✅ 所有在售商品库存充足
|
||||
</div>
|
||||
</div>
|
||||
@@ -999,38 +1040,52 @@
|
||||
<!-- 右侧:电商运营风控 & 日结监控 -->
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
|
||||
<div class="card" style="min-height: 340px;">
|
||||
<div class="card-header-title">电商运营与日结对账监控</div>
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<div class="card-header-title" style="margin-bottom: 0;">电商待办与日结对账监控</div>
|
||||
<el-tag size="mini" type="danger" effect="light">全量实时待办</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- 待处理订单指标卡 -->
|
||||
<el-row :gutter="12" class="mb-3">
|
||||
<el-col :span="8">
|
||||
<!-- 待处理订单指标卡 (全量实时统计,不受时段筛选影响,杜绝漏单) -->
|
||||
<el-row :gutter="10" class="mb-3">
|
||||
<el-col :span="6">
|
||||
<div class="todo-stat-card" @click="onOpen('nosend')">
|
||||
<div style="color: var(--sa-subfont); margin-bottom: 4px;">待发货订单</div>
|
||||
<div style="color: var(--sa-subfont); font-size: 11px; margin-bottom: 4px;">待发货订单</div>
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span style="font-size: 20px; font-weight: 700;" :class="statistics.noSend.num > 0 ? 'text-warning' : 'text-dark'">
|
||||
{{ statistics.noSend.num }}
|
||||
<span style="font-size: 18px; font-weight: 700;" :class="(state.operation?.todo_overview?.nosend_count ?? statistics.noSend.num) > 0 ? 'text-warning' : 'text-dark'">
|
||||
{{ state.operation?.todo_overview?.nosend_count ?? statistics.noSend.num }}
|
||||
</span>
|
||||
<el-icon><arrow-right /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-col :span="6">
|
||||
<div class="todo-stat-card" @click="onOpen('aftersale')">
|
||||
<div style="color: var(--sa-subfont); margin-bottom: 4px;">售后维权</div>
|
||||
<div style="color: var(--sa-subfont); font-size: 11px; margin-bottom: 4px;">售后维权</div>
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span style="font-size: 20px; font-weight: 700;" :class="statistics.aftersale.num > 0 ? 'text-danger' : 'text-dark'">
|
||||
{{ statistics.aftersale.num }}
|
||||
<span style="font-size: 18px; font-weight: 700;" :class="(state.operation?.todo_overview?.aftersale_count ?? statistics.aftersale.num) > 0 ? 'text-danger' : 'text-dark'">
|
||||
{{ state.operation?.todo_overview?.aftersale_count ?? statistics.aftersale.num }}
|
||||
</span>
|
||||
<el-icon><arrow-right /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-col :span="6">
|
||||
<div class="todo-stat-card" @click="onOpen('refund')">
|
||||
<div style="color: var(--sa-subfont); margin-bottom: 4px;">退款订单</div>
|
||||
<div style="color: var(--sa-subfont); font-size: 11px; margin-bottom: 4px;">待审退款</div>
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span style="font-size: 20px; font-weight: 700;">
|
||||
{{ statistics.refund.num }}
|
||||
<span style="font-size: 18px; font-weight: 700;" :class="(state.operation?.todo_overview?.refund_apply_count ?? statistics.refund.num) > 0 ? 'text-danger' : 'text-dark'">
|
||||
{{ state.operation?.todo_overview?.refund_apply_count ?? statistics.refund.num }}
|
||||
</span>
|
||||
<el-icon><arrow-right /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="todo-stat-card" @click="onOpenWithdraw(0)">
|
||||
<div style="color: var(--sa-subfont); font-size: 11px; margin-bottom: 4px;">待审提现</div>
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span style="font-size: 18px; font-weight: 700;" :class="(state.operation?.todo_overview?.pending_withdraw_count ?? state.finance?.pending_withdraw_count ?? 0) > 0 ? 'text-purple' : 'text-dark'">
|
||||
{{ state.operation?.todo_overview?.pending_withdraw_count ?? state.finance?.pending_withdraw_count ?? 0 }}
|
||||
</span>
|
||||
<el-icon><arrow-right /></el-icon>
|
||||
</div>
|
||||
|
||||
在新工单中引用
屏蔽一个用户