diff --git a/application/admin/controller/shopro/commission/Agent.php b/application/admin/controller/shopro/commission/Agent.php
index da70a25..8791197 100644
--- a/application/admin/controller/shopro/commission/Agent.php
+++ b/application/admin/controller/shopro/commission/Agent.php
@@ -69,7 +69,7 @@ class Agent extends Common
$detail->next_push3_text = '第 ' . ($seq + 1) . ' 单将返 ' . $detail->next_push3_amount;
$detail->last_self_order_text = $last ? date('Y-m-d H:i:s', $last) : '从未下单';
- // 业务核心规范:进阶角色(创客V1~V6、酒道馆、区县代理、合伙股东)需自购考核;基础会员及品酒师免自购考核
+ // 业务核心规范:进阶角色(创客V1~V6、酒道馆、区县代理、合伙人)需自购考核;基础会员及品酒师免自购考核
if (!$isAdvanced) {
$detail->active_status_text = '免自购考核 (基础会员/品酒师)';
} else {
@@ -101,18 +101,204 @@ class Agent extends Common
$detail->total_income = $alignedIncome;
$detail->partner_cash_income = $alignedIncome;
$detail->push3_cash_income = number_format((float) $push3Cash, 2, '.', '');
- $detail->partner_exclusive_cash = number_format((float) $partnerExclusiveCash, 2, '.', '');
- $detail->user_money = number_format(floatval($detail->user ? $detail->user->money : 0), 2, '.', '');
- $detail->user_commission = number_format(floatval($detail->user ? $detail->user->commission : 0), 2, '.', '');
+ $userData = $detail->user ? $detail->user->getData() : [];
+ $userMoney = $userData['money'] ?? 0;
+ $userCommission = $userData['commission'] ?? 0;
+ $detail->user_money = number_format(floatval($userMoney), 2, '.', '');
+ $detail->user_commission = number_format(floatval($userCommission), 2, '.', '');
$detail->partner_mode = \addons\shopro\service\partner\PartnerConfig::isPartnerMode() ? 1 : 0;
$rate = (new \addons\shopro\service\partner\RateService())->currentRate();
$detail->madow_value = \addons\shopro\service\partner\IdentityService::formatMoney((float) ($detail->madow ?? 0) * (float) $rate);
$detail->custom_role_names = \addons\shopro\service\partner\PartnerConfig::roleNames();
$detail->role_names = $detail->custom_role_names;
+ $detail->operational_stats = $this->getOperationalStats($detail, $id);
$this->success('分销商详情', null, $detail);
}
+ /**
+ * 构建运营官全景统计数据看板
+ */
+ private function getOperationalStats($detail, $id)
+ {
+ $allUsers = \think\Db::name('user')->column('parent_user_id', 'id');
+ $getDesc = function ($uid) use (&$getDesc, $allUsers) {
+ $res = [];
+ foreach ($allUsers as $cid => $pid) {
+ if ((int)$pid === (int)$uid) {
+ $res[] = $cid;
+ $res = array_merge($res, $getDesc($cid));
+ }
+ }
+ return $res;
+ };
+ $descIds = $getDesc($id);
+ $directIds = [];
+ foreach ($allUsers as $cid => $pid) {
+ if ((int)$pid === (int)$id) {
+ $directIds[] = $cid;
+ }
+ }
+
+ // 1. 团队角色拓扑
+ $directCount = count($directIds);
+ $directPaidCount = empty($directIds) ? 0 : \think\Db::name('user')->whereIn('id', $directIds)->where('total_consume', '>', 0)->count();
+ $directMakerCount = empty($directIds) ? 0 : \think\Db::name('shopro_commission_agent')->whereIn('user_id', $directIds)->where('level', '>=', 2)->count();
+
+ $teamDescCount = count($descIds);
+ $teamPaidCount = empty($descIds) ? 0 : \think\Db::name('user')->whereIn('id', $descIds)->where('total_consume', '>', 0)->count();
+ $teamMakerAgents = empty($descIds) ? [] : \think\Db::name('shopro_commission_agent')->whereIn('user_id', $descIds)->where('level', '>=', 2)->column('level');
+ $teamMakerCount = count($teamMakerAgents);
+ $teamMakerDistribution = array_count_values($teamMakerAgents);
+ ksort($teamMakerDistribution);
+ $makerDistribution = [
+ 'v1' => intval($teamMakerDistribution[2] ?? 0),
+ 'v2' => intval($teamMakerDistribution[3] ?? 0),
+ 'v3' => intval($teamMakerDistribution[4] ?? 0),
+ 'v4' => intval($teamMakerDistribution[5] ?? 0),
+ 'v5' => intval($teamMakerDistribution[6] ?? 0),
+ 'v6' => intval($teamMakerDistribution[7] ?? 0),
+ ];
+ $teamVenueCount = empty($descIds) ? 0 : \think\Db::name('shopro_commission_agent')->whereIn('user_id', $descIds)->where('is_venue', 1)->count();
+ $teamCountyCount = empty($descIds) ? 0 : \think\Db::name('shopro_commission_agent')->whereIn('user_id', $descIds)->where('is_county', 1)->count();
+ $conversionRate = $teamDescCount > 0 ? round(($teamPaidCount / $teamDescCount) * 100, 1) : 0.0;
+
+ // 2. 收益分成细项
+ $rewards = \think\Db::name('shopro_partner_reward')
+ ->where('user_id', $id)
+ ->where('status', 1)
+ ->group('reward_type')
+ ->column('sum(amount)', 'reward_type');
+
+ $push3Income = (float)($rewards['push3'] ?? 0);
+ $makerDiffIncome = (float)($rewards['maker_diff'] ?? 0);
+ $makerPeerIncome = (float)(($rewards['maker_peer'] ?? 0) + ($rewards['maker_overtaken'] ?? 0));
+ $teamSplitIncome = (float)(($rewards['team_venue'] ?? 0) + ($rewards['team_county'] ?? 0));
+ $sharePoolIncome = (float)(($rewards['venue_share'] ?? 0) + ($rewards['county_share'] ?? 0));
+
+ // 3. 业绩与订单分类
+ $selfOrderMoney = number_format((float)($detail->child_order_money_0 ?? 0), 2, '.', '');
+ $selfOrderCount = intval($detail->child_order_count_0 ?? 0);
+ $directOrderMoney = number_format((float)($detail->child_order_money_1 ?? 0), 2, '.', '');
+ $directOrderCount = intval($detail->child_order_count_1 ?? 0);
+ $teamOrderMoney = number_format((float)($detail->child_order_money_all ?? 0), 2, '.', '');
+ $teamOrderCount = intval($detail->child_order_count_all ?? 0);
+
+ $indirectOrderMoney = number_format(max(0, (float)$teamOrderMoney - (float)$directOrderMoney - (float)$selfOrderMoney), 2, '.', '');
+ $indirectOrderCount = max(0, $teamOrderCount - $directOrderCount - $selfOrderCount);
+
+ // 4. 晋升考核差距雷达
+ $currentLevel = intval($detail->level ?? 1);
+ $currentLevelRow = \think\Db::name('shopro_commission_level')->where('level', $currentLevel)->find();
+ $currentLevelName = $currentLevelRow ? $currentLevelRow['name'] : ($currentLevel >= 2 ? '创客V' . ($currentLevel - 1) : '品酒师');
+
+ $nextLevel = $currentLevel + 1;
+ $nextLevelRow = \think\Db::name('shopro_commission_level')->where('level', $nextLevel)->find();
+
+ if ($nextLevelRow) {
+ $nextRules = json_decode($nextLevelRow['upgrade_rules'] ?? '[]', true) ?: [];
+ $targetDirectUsers = intval($nextRules['child_user_count_1'] ?? 0);
+ $targetTeamOrders = intval($nextRules['child_order_count_all'] ?? 0);
+
+ $currentDirectUsers = intval($detail->child_user_count_1 ?? $directCount);
+ $currentTeamOrders = intval($detail->child_order_count_all ?? 0);
+ $currentSelfMoney = (float)($detail->child_order_money_0 ?? 0);
+
+ $directUserGap = max(0, $targetDirectUsers - $currentDirectUsers);
+ $teamOrderGap = max(0, $targetTeamOrders - $currentTeamOrders);
+ $isSelfPaid = ($selfOrderCount > 0 || $currentSelfMoney >= 1398);
+
+ $directUserPercent = $targetDirectUsers > 0 ? min(100, (int)round(($currentDirectUsers / $targetDirectUsers) * 100)) : 100;
+ $teamOrderPercent = $targetTeamOrders > 0 ? min(100, (int)round(($currentTeamOrders / $targetTeamOrders) * 100)) : 100;
+
+ $upgradeRadar = [
+ 'current_level' => $currentLevel,
+ 'current_level_name' => $currentLevelName,
+ 'next_level' => $nextLevel,
+ 'next_level_name' => $nextLevelRow['name'],
+ 'target_direct_users' => $targetDirectUsers,
+ 'current_direct_users' => $currentDirectUsers,
+ 'direct_user_gap' => $directUserGap,
+ 'direct_user_reached' => ($directUserGap === 0),
+ 'direct_user_percent' => $directUserPercent,
+ 'target_team_orders' => $targetTeamOrders,
+ 'current_team_orders' => $currentTeamOrders,
+ 'team_order_gap' => $teamOrderGap,
+ 'team_order_reached' => ($teamOrderGap === 0),
+ 'team_order_percent' => $teamOrderPercent,
+ 'self_paid_reached' => $isSelfPaid,
+ 'all_reached' => ($directUserGap === 0 && $teamOrderGap === 0 && $isSelfPaid),
+ 'upgrade_lock' => intval($detail->upgrade_lock ?? 0)
+ ];
+ } else {
+ $upgradeRadar = [
+ 'current_level' => $currentLevel,
+ 'current_level_name' => $currentLevelName,
+ 'next_level' => 0,
+ 'next_level_name' => '已达最高职级',
+ 'target_direct_users' => 0,
+ 'current_direct_users' => intval($detail->child_user_count_1 ?? $directCount),
+ 'direct_user_gap' => 0,
+ 'direct_user_reached' => true,
+ 'direct_user_percent' => 100,
+ 'target_team_orders' => 0,
+ 'current_team_orders' => intval($detail->child_order_count_all ?? 0),
+ 'team_order_gap' => 0,
+ 'team_order_reached' => true,
+ 'team_order_percent' => 100,
+ 'self_paid_reached' => true,
+ 'all_reached' => true,
+ 'upgrade_lock' => intval($detail->upgrade_lock ?? 0)
+ ];
+ }
+
+ return [
+ 'roles' => [
+ 'direct_total' => $directCount,
+ 'direct_paid' => $directPaidCount,
+ 'direct_unpaid' => max(0, $directCount - $directPaidCount),
+ 'direct_maker' => $directMakerCount,
+ 'team_total' => $teamDescCount,
+ 'team_paid' => $teamPaidCount,
+ 'team_unpaid' => max(0, $teamDescCount - $teamPaidCount),
+ 'team_conversion_rate' => $conversionRate,
+ 'team_maker' => $teamMakerCount,
+ 'team_maker_distribution' => $makerDistribution,
+ 'team_venue' => $teamVenueCount,
+ 'team_county' => $teamCountyCount,
+ ],
+ 'orders' => [
+ 'self_money' => $selfOrderMoney,
+ 'self_count' => $selfOrderCount,
+ 'direct_money' => $directOrderMoney,
+ 'direct_count' => $directOrderCount,
+ 'indirect_money' => $indirectOrderMoney,
+ 'indirect_count' => $indirectOrderCount,
+ 'team_money' => $teamOrderMoney,
+ 'team_count' => $teamOrderCount,
+ ],
+ 'rewards' => [
+ 'push3' => number_format($push3Income, 2, '.', ''),
+ 'maker_diff' => number_format($makerDiffIncome, 2, '.', ''),
+ 'maker_peer' => number_format($makerPeerIncome, 2, '.', ''),
+ 'maker_total' => number_format($makerDiffIncome + $makerPeerIncome, 2, '.', ''),
+ 'team_split' => number_format($teamSplitIncome, 2, '.', ''),
+ 'share_pool' => number_format($sharePoolIncome, 2, '.', ''),
+ ],
+ 'assets' => [
+ 'wallet_money' => $detail->user_money,
+ 'total_income' => $detail->total_income,
+ 'untransfer_commission' => $detail->user_commission,
+ 'dmepa_madow' => number_format((float)($detail->madow ?? 0), 4, '.', ''),
+ 'dmepa_value' => $detail->madow_value,
+ 'voucher_total' => number_format((float)($detail->voucher_total ?? 0), 2, '.', ''),
+ 'voucher_released' => number_format((float)($detail->voucher_released ?? 0), 2, '.', ''),
+ 'voucher_available' => number_format(max(0, (float)($detail->voucher_released ?? 0) - (float)($detail->voucher_used ?? 0)), 2, '.', ''),
+ ],
+ 'upgrade_radar' => $upgradeRadar
+ ];
+ }
+
/**
* 团队
diff --git a/application/admin/view/shopro/commission/agent/detail.html b/application/admin/view/shopro/commission/agent/detail.html
index 26dd8d9..beafb3e 100644
--- a/application/admin/view/shopro/commission/agent/detail.html
+++ b/application/admin/view/shopro/commission/agent/detail.html
@@ -156,6 +156,122 @@
.agent-detail .rewards-item:last-of-type {
margin-right: 0;
}
+
+ /* 运营官全景看板样式 */
+ .agent-detail .op-dashboard {
+ background: #fff;
+ border: 1px solid var(--sa-border);
+ border-radius: 8px;
+ padding: 18px 20px;
+ margin-bottom: 20px;
+ }
+ .agent-detail .op-section-title {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--sa-title);
+ margin: 16px 0 12px;
+ padding-bottom: 8px;
+ border-bottom: 1px dashed var(--sa-border);
+ }
+ .agent-detail .op-section-title:first-of-type {
+ margin-top: 4px;
+ }
+ .agent-detail .op-radar-card {
+ background: linear-gradient(135deg, #f8fafc 0%, #edf2f7 100%);
+ border: 1px solid #e2e8f0;
+ border-radius: 8px;
+ padding: 14px 18px;
+ margin-bottom: 18px;
+ }
+ .agent-detail .op-radar-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 12px;
+ }
+ .agent-detail .op-radar-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #2d3748;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+ .agent-detail .op-radar-body {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ }
+ .agent-detail .op-radar-metric {
+ background: #fff;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+ padding: 10px 14px;
+ flex: 1;
+ min-width: 180px;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.02);
+ }
+ .agent-detail .op-radar-metric .m-label {
+ font-size: 12px;
+ color: #718096;
+ margin-bottom: 6px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+ .agent-detail .op-radar-metric .m-val {
+ font-size: 16px;
+ font-weight: 600;
+ color: #1a202c;
+ }
+
+ .agent-detail .op-grid-box {
+ background: #fbfcfe;
+ border: 1px solid #edf2f7;
+ border-radius: 6px;
+ padding: 12px 14px;
+ min-height: 120px;
+ box-sizing: border-box;
+ margin-bottom: 12px;
+ transition: all 0.2s ease;
+ }
+ .agent-detail .op-grid-box:hover {
+ border-color: #cbd5e0;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
+ }
+ .agent-detail .op-grid-box .box-title {
+ font-size: 13px;
+ font-weight: 600;
+ color: #334155;
+ margin-bottom: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+ .agent-detail .op-stat-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 13px;
+ line-height: 24px;
+ }
+ .agent-detail .op-stat-row .stat-label {
+ color: #64748b;
+ font-size: 12px;
+ }
+ .agent-detail .op-stat-row .stat-val {
+ font-weight: 600;
+ color: #1e293b;
+ }
+ .agent-detail .op-stat-row .stat-val.primary { color: #3b82f6; }
+ .agent-detail .op-stat-row .stat-val.success { color: #10b981; }
+ .agent-detail .op-stat-row .stat-val.warning { color: #f59e0b; }
+ .agent-detail .op-stat-row .stat-val.danger { color: #ef4444; }
+ .agent-detail .op-stat-row .stat-val.purple { color: #8b5cf6; }
+ .agent-detail .op-stat-row .stat-val.cyan { color: #06b6d4; }
@@ -334,9 +450,9 @@
{{ state.data.is_county ? '已开通' + (state.data.county_code ? ' (' + state.data.county_code + ')' : '') : '未开通' }}
-
+
- {{ state.data.is_partner ? '已授予' + (state.data.custom_role_names?.partner || '合伙股东') : '未开通' }}
+ {{ state.data.is_partner ? '已授予' + (state.data.custom_role_names?.partner || '合伙人') : '未开通' }}
@@ -367,13 +483,13 @@
{{ state.data.identity_text || ('基础会员 · ' + (state.data.custom_role_names?.taster || '品酒师')) }}
- 💡 进阶角色体系说明:系统拥有{{ state.data.custom_role_names?.maker || '创客' }}(V1~V6)、{{ state.data.custom_role_names?.venue || '酒道馆' }}、{{ state.data.custom_role_names?.county || '区县代理' }}、{{ state.data.custom_role_names?.partner || '合伙股东' }}等独立进阶角色,享有团队分润与平台日结分红。当前会员为基础会员,享有推三返一,免自购考核。
+ 💡 进阶角色体系说明:系统拥有{{ state.data.custom_role_names?.maker || '创客' }}(V1~V6)、{{ state.data.custom_role_names?.venue || '酒道馆' }}、{{ state.data.custom_role_names?.county || '区县代理' }}、{{ state.data.custom_role_names?.partner || '合伙人' }}等独立进阶角色,享有团队分润与平台日结分红。当前会员为基础会员,享有推三返一,免自购考核。
免自购考核 (基础会员/{{ state.data.custom_role_names?.taster || '品酒师' }})
- 升级{{ state.data.custom_role_names?.maker || '创客' }}或开通{{ (state.data.custom_role_names?.venue || '馆').substr(0, 1) }}/{{ (state.data.custom_role_names?.county || '县').substr(0, 1) }}/{{ (state.data.custom_role_names?.partner || '股东').substr(0, 2) }}后即可享有团队奖、级差奖与dmepa分红
+ 升级{{ state.data.custom_role_names?.maker || '创客' }}或开通{{ (state.data.custom_role_names?.venue || '馆').substr(0, 1) }}/{{ (state.data.custom_role_names?.county || '县').substr(0, 1) }}/{{ (state.data.custom_role_names?.partner || '合伙').substr(0, 2) }}后即可享有团队奖、级差奖与dmepa分红
@@ -443,212 +559,347 @@
-
-
-
-
团队统计
+
+
+
+
+
+
+
+
+
+ 有效直推人数指标
+
+ {{ state.data.operational_stats.upgrade_radar.direct_user_reached ? '已达标' : '缺 ' + state.data.operational_stats.upgrade_radar.direct_user_gap + ' 人' }}
+
+
+
+ {{ state.data.operational_stats.upgrade_radar.current_direct_users }}
+ / {{ state.data.operational_stats.upgrade_radar.target_direct_users }} 人
+
+
+
+
+
+
+
+ 团队有效订单指标
+
+ {{ state.data.operational_stats.upgrade_radar.team_order_reached ? '已达标' : '缺 ' + state.data.operational_stats.upgrade_radar.team_order_gap + ' 单' }}
+
+
+
+ {{ state.data.operational_stats.upgrade_radar.current_team_orders }}
+ / {{ state.data.operational_stats.upgrade_radar.target_team_orders }} 单
+
+
+
+
+
+
+
+ 个人自购资格门槛
+
+ {{ state.data.operational_stats.upgrade_radar.self_paid_reached ? '已满足' : '未购买' }}
+
+
+
+ ✓ 具备业务升级资格
+ ✕ 需完成首单自购(≥1398元)
+
+
+
+
+ ★ 该账号已达到平台顶峰合伙人级别,尊享平台级加权均分红与管理奖励!
+
+
+
+
+
+
+ 👥
+ 团队角色与组织拓扑看板
+
+
{if $auth->check('shopro/commission/agent/team')}
- 查看团队
+ 查看层级拓扑树
{/if}
-
-
- 团队人数:
-
- {{ state.data.child_user_count_all }}
- 人
+
+
+
+
+
+
+ 直推成员池 (Direct)
+ {{ state.data.operational_stats.roles.direct_total }} 人
-
-
- 一级团队人数:
-
- {{ state.data.child_user_count_1 }}
-
人
+
+ 已首购有效用户:
+ {{ state.data.operational_stats.roles.direct_paid }} 人
-
-
- 二级团队人数:
-
- {{ state.data.child_user_count_2 }}
-
人
+
+ 未购买注册粉丝:
+ {{ state.data.operational_stats.roles.direct_unpaid }} 人
-
-
-
- 团队分销商人数:
-
-
- 等级{{ key }}:{{ value }}人
+
+ 直推创客领袖:
+ {{ state.data.operational_stats.roles.direct_maker }} 人
+
+
+
+
+
+
+
+
+
核心业务骨干 (Backbone)
+
+
+
创客各级分布:
+
V1创客: {{ state.data.operational_stats.roles.team_maker_distribution.v1 || 0 }} 人
+
V2创客: {{ state.data.operational_stats.roles.team_maker_distribution.v2 || 0 }} 人
+
V3创客: {{ state.data.operational_stats.roles.team_maker_distribution.v3 || 0 }} 人
+
V4创客: {{ state.data.operational_stats.roles.team_maker_distribution.v4 || 0 }} 人
+
V5创客: {{ state.data.operational_stats.roles.team_maker_distribution.v5 || 0 }} 人
+
V6创客: {{ state.data.operational_stats.roles.team_maker_distribution.v6 || 0 }} 人
-
-
-
-
-
+ ℹ️ 分布详情
-
- {{ state.data.child_agent_count_all }}
-
人
+
+ 创客合伙人 (V1-V6):
+ {{ state.data.operational_stats.roles.team_maker }} 人
-
-
-
- 一级分销商人数:
-
-
- 等级{{ key }}:{{ value }}人
-
-
-
-
-
-
-
-
-
+
+ 酒道馆签约馆长:
+ {{ state.data.operational_stats.roles.team_venue }} 个
-
- {{ state.data.child_agent_count_1 }}
-
人
+
+ 区县代理办事处:
+ {{ state.data.operational_stats.roles.team_county }} 个
-
-
- 二级分销商人数:
-
- {{ state.data.child_agent_count_2 }}
- 人
+
+
+
+
+
+
+
+ 全网团队规模 (Network)
+ {{ state.data.operational_stats.roles.team_total }} 人
-
-
-
-
-
-
业绩统计
+
+ 全网已出单活跃人数:
+ {{ state.data.operational_stats.roles.team_paid }} 人
+
+
+ 全网激活转化率:
+ {{ state.data.operational_stats.roles.team_conversion_rate }}%
+
+
+ 二级直属团队人数:
+ {{ state.data.child_user_count_2 || 0 }} 人
+
+
+
+
+
+
+
+
+ 📦
+ 业务业绩与订单流向看板
-
-
- 团队分销总金额:
-
- {{ state.data.child_order_money_all }}
- 元
+
+
+
+
+
+
个人自购业绩
+
+ 自购成交额:
+ ¥{{ state.data.operational_stats.orders.self_money }}
-
-
- 一级分销总金额:
-
- {{ state.data.child_order_money_1 }}
-
元
+
+ 自购订单数:
+ {{ state.data.operational_stats.orders.self_count }} 单
-
-
- 二级分销总金额:
-
- {{ state.data.child_order_money_2 }}
- 元
+
+
+
+
+
+
+
直推客户业绩 (一级)
+
+ 直推成交额:
+ ¥{{ state.data.operational_stats.orders.direct_money }}
-
-
-
- 自购分销总金额:
-
-
-
-
-
-
-
-
-
+
+ 直推订单数:
+ {{ state.data.operational_stats.orders.direct_count }} 单
-
- {{ state.data.child_order_money_0 }}
- 元
+
+
+
+
+
+
+
次级裂变业绩 (二级)
+
+ 间推成交额:
+ ¥{{ state.data.operational_stats.orders.indirect_money }}
-
-
- 团队分销订单:
-
- {{ state.data.child_order_count_all }}
-
单
+
+ 间推订单数:
+ {{ state.data.operational_stats.orders.indirect_count }} 单
-
-
- 一级分销订单:
-
- {{ state.data.child_order_count_1 }}
- 单
+
+
+
+
+
+
+
全网团队总流水 (GMV)
+
+ 全网成交总额:
+ ¥{{ state.data.operational_stats.orders.team_money }}
-
-
- 二级分销订单:
-
- {{ state.data.child_order_count_2 }}
-
单
+
+ 全网出单总数:
+ {{ state.data.operational_stats.orders.team_count }} 单
-
-
-
- 自购分销订单:
-
-
-
-
-
-
-
-
-
-
-
- {{ state.data.child_order_count_0 }}
- 单
-
-
-
-
-
-
- 资产信息
+
+
+
+
+
+
+
+ 💰
+ 收益中心分成明细与资产闭环 (方案B一体化大余额)
-
-
- 累计收益/佣金:
-
- {{ state.data.total_income }}
- 元
+
+
+
+
+
+
+ 钱包可用余额 (money)
+ 可提现
-
-
- 钱包可用余额:
-
- {{ state.data.user?.money || state.data.user_money || '0.00' }}
-
元
+
+ ¥{{ state.data.operational_stats.assets.wallet_money || '0.00' }}
-
-
- 历史未转佣金:
-
- {{ state.data.user?.commission || state.data.user_commission || '0.00' }}
-
元
+
+ ✓ 收益入账此钱包,提现扣除此余额
-
-
- 消费金额:
-
- {{ state.data.user?.total_consume || 0 }}
- 元
+
+
+
+
+
+
+
+ 业务奖励构成细项
+ 累计: ¥{{ state.data.operational_stats.assets.total_income }}
-
-
-
-
+
+ 推三返一收益:
+ ¥{{ state.data.operational_stats.rewards.push3 }}
+
+
+ 创客极差/平级/超越:
+ ¥{{ state.data.operational_stats.rewards.maker_total }}
+
+
+ 馆/县团队管理奖:
+ ¥{{ state.data.operational_stats.rewards.team_split }}
+
+
+ 平台日结均分池:
+ ¥{{ state.data.operational_stats.rewards.share_pool }}
+
+
+
+
+
+
+
+
+ DMEPA 专属数字资产
+ 生态资产
+
+
+ DMEPA 持仓数量:
+ {{ state.data.operational_stats.assets.dmepa_madow }} 枚
+
+
+ 实时市值折算:
+ ¥{{ state.data.operational_stats.assets.dmepa_value }}
+
+
+ 历史未转佣金:
+ ¥{{ state.data.operational_stats.assets.untransfer_commission }}
+
+
+
+
+
+
+
+
+ 365天消费补贴券
+ 逐日释放
+
+
+ 当前可用补贴券:
+ {{ state.data.operational_stats.assets.voucher_available }} 个
+
+
+ 累计释放补贴券:
+ {{ state.data.operational_stats.assets.voucher_released }} 个
+
+
+ 补贴券总额度:
+ {{ state.data.operational_stats.assets.voucher_total }} 个
+
+
+
+
+
diff --git a/tests/test_agent_detail_fix.php b/tests/test_agent_detail_fix.php
new file mode 100644
index 0000000..4455278
--- /dev/null
+++ b/tests/test_agent_detail_fix.php
@@ -0,0 +1,193 @@
+ $val) {
+ $name = 'PHP_' . strtoupper($key);
+ if (is_array($val)) {
+ foreach ($val as $k => $v) {
+ $item = $name . '_' . strtoupper($k);
+ putenv("$item=$v");
+ }
+ } else {
+ putenv("$name=$val");
+ }
+ }
+}
+
+\think\Config::set(\think\Config::load(APP_PATH . 'config.php'));
+\think\Config::set(include APP_PATH . 'database.php', 'database');
+foreach (glob(APP_PATH . 'extra/*.php') as $extraFile) {
+ $extraName = basename($extraFile, '.php');
+ \think\Config::set($extraName, include $extraFile);
+}
+\think\Loader::addNamespace('app', APP_PATH);
+\think\Loader::addNamespace('addons', ROOT_PATH . 'addons/');
+require_once __DIR__ . '/../application/common.php';
+require_once __DIR__ . '/../addons/shopro/helper/helper.php';
+
+$_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest';
+
+// 模拟管理员会话
+@ini_set('session.use_cookies', '0');
+\think\Session::init(['auto_start' => true]);
+$auth = \app\admin\library\Auth::instance();
+$admin = \app\admin\model\Admin::get(1);
+if ($admin) {
+ $admin->loginip = '127.0.0.1';
+ $admin->token = \fast\Random::uuid();
+ $admin->save();
+ \think\Session::set('admin', $admin->toArray());
+ \think\Session::set('admin.safecode', $auth->getEncryptSafecode($admin));
+ $ref = new \ReflectionProperty($auth, 'logined');
+ $ref->setAccessible(true);
+ $ref->setValue($auth, true);
+}
+
+echo "====================================================================\n";
+echo " 运营官全景看板与分销商详情 API (Agent::detail) 仿真验证测试\n";
+echo "====================================================================\n\n";
+
+$request = \think\Request::instance();
+$controller = new \app\admin\controller\shopro\commission\Agent($request);
+
+$passCount = 0;
+
+// [测试用例 1] 目标账号 ID 54
+echo "--- [测试用例 1] 访问用户 ID 54 (目标初始账号) ---\n";
+try {
+ $controller->detail(54);
+ echo "✗ 测试 1 未抛出响应异常\n";
+} catch (\think\exception\HttpResponseException $e) {
+ $resp = $e->getResponse();
+ $data = json_decode($resp->getContent(), true);
+ if ($resp->getCode() === 200 && ($data['code'] ?? null) === 1) {
+ $op = $data['data']['operational_stats'] ?? null;
+ if ($op && isset($op['upgrade_radar'], $op['roles'], $op['orders'], $op['rewards'], $op['assets'])) {
+ echo "✓ 测试 1 通过!状态码: 200, 业务响应: {$data['msg']}\n";
+ echo " - 用户标识: ID {$data['data']['user_id']}\n";
+ echo " - 晋升目标: {$op['upgrade_radar']['current_level_name']} -> {$op['upgrade_radar']['next_level_name']}\n";
+ echo " - 直推指标: {$op['upgrade_radar']['current_direct_users']} / {$op['upgrade_radar']['target_direct_users']} (缺 {$op['upgrade_radar']['direct_user_gap']} 人)\n";
+ echo " - 团队订单: {$op['upgrade_radar']['current_team_orders']} / {$op['upgrade_radar']['target_team_orders']} (缺 {$op['upgrade_radar']['team_order_gap']} 单)\n";
+ echo " - 方案B可用余额 (money): ¥{$op['assets']['wallet_money']}\n";
+ echo " - DMEPA 持仓资产: {$op['assets']['dmepa_madow']} 枚 (估值 ¥{$op['assets']['dmepa_value']})\n";
+ echo " - 365天可用补贴券: {$op['assets']['voucher_available']} 个\n";
+ $passCount++;
+ } else {
+ echo "✗ 测试 1 失败: operational_stats 结构不完整\n";
+ }
+ } else {
+ echo "✗ 测试 1 失败: " . $resp->getContent() . "\n";
+ }
+} catch (\Throwable $e) {
+ echo "✗ 测试 1 异常: " . $e->getMessage() . "\n" . $e->getTraceAsString() . "\n";
+}
+
+// [测试用例 2] 进阶活跃账号 ID 9
+echo "\n--- [测试用例 2] 访问用户 ID 9 (进阶创客V1账号,含多层团队与订单) ---\n";
+try {
+ $controller->detail(9);
+ echo "✗ 测试 2 未抛出响应异常\n";
+} catch (\think\exception\HttpResponseException $e) {
+ $resp = $e->getResponse();
+ $data = json_decode($resp->getContent(), true);
+ if ($resp->getCode() === 200 && ($data['code'] ?? null) === 1) {
+ $op = $data['data']['operational_stats'] ?? null;
+ if ($op && isset($op['upgrade_radar'], $op['roles'], $op['orders'], $op['rewards'], $op['assets'])) {
+ echo "✓ 测试 2 通过!状态码: 200, 业务响应: {$data['msg']}\n";
+ echo " - 用户标识: ID {$data['data']['user_id']}\n";
+ echo " - 团队拓扑: 直推 {$op['roles']['direct_total']} 人(已购 {$op['roles']['direct_paid']} 人), 全网 {$op['roles']['team_total']} 人(出单 {$op['roles']['team_paid']} 人, 转化率 {$op['roles']['team_conversion_rate']}%)\n";
+ echo " - 创客分布: V1:{$op['roles']['team_maker_distribution']['v1']}人, V2:{$op['roles']['team_maker_distribution']['v2']}人, V3:{$op['roles']['team_maker_distribution']['v3']}人\n";
+ echo " - 业务流水: 自购 ¥{$op['orders']['self_money']}({$op['orders']['self_count']}单), 直推 ¥{$op['orders']['direct_money']}, 间推 ¥{$op['orders']['indirect_money']}, 全网GMV ¥{$op['orders']['team_money']}({$op['orders']['team_count']}单)\n";
+ echo " - 收益分成: 推三返一 ¥{$op['rewards']['push3']}, 创客总奖 ¥{$op['rewards']['maker_total']}, 团队管理 ¥{$op['rewards']['team_split']}, 日结均分 ¥{$op['rewards']['share_pool']}\n";
+ echo " - 方案B可用余额 (money): ¥{$op['assets']['wallet_money']}, 累计创收: ¥{$op['assets']['total_income']}\n";
+ $passCount++;
+ } else {
+ echo "✗ 测试 2 失败: operational_stats 结构不完整\n";
+ }
+ } else {
+ echo "✗ 测试 2 失败: " . $resp->getContent() . "\n";
+ }
+} catch (\Throwable $e) {
+ echo "✗ 测试 2 异常: " . $e->getMessage() . "\n";
+}
+
+// [测试用例 3] 业务拦截:访问不存在的用户 ID 999999
+echo "\n--- [测试用例 3] 访问不存在的用户 ID 999999 (业务拦截场景) ---\n";
+try {
+ $controller->detail(999999);
+ echo "✗ 测试 3 未抛出响应异常\n";
+} catch (\think\exception\HttpResponseException $e) {
+ $resp = $e->getResponse();
+ $data = json_decode($resp->getContent(), true);
+ if (($data['code'] ?? null) === 0) {
+ echo "✓ 测试 3 通过!成功拦截不存在的用户并返回 code=0: {$data['msg']}\n";
+ $passCount++;
+ } else {
+ echo "✗ 测试 3 预期 code=0 但得到: " . $resp->getContent() . "\n";
+ }
+} catch (\Throwable $e) {
+ echo "✗ 测试 3 异常: " . $e->getMessage() . "\n";
+}
+
+// [测试用例 4] 参数校验:传入空 ID 或非法参数
+echo "\n--- [测试用例 4] 参数错误场景:传入空 ID (参数校验) ---\n";
+try {
+ $controller->detail(0);
+ echo "✗ 测试 4 未抛出响应异常\n";
+} catch (\think\exception\HttpResponseException $e) {
+ $resp = $e->getResponse();
+ $data = json_decode($resp->getContent(), true);
+ if (($data['code'] ?? null) === 0) {
+ echo "✓ 测试 4 通过!非法/空 ID 正确被拦截并返回 code=0: {$data['msg']}\n";
+ $passCount++;
+ } else {
+ echo "✗ 测试 4 预期 code=0 但得到: " . $resp->getContent() . "\n";
+ }
+} catch (\Throwable $e) {
+ echo "✗ 测试 4 异常: " . $e->getMessage() . "\n";
+}
+
+// [测试用例 5] 非 Ajax 请求页面模版渲染(HTML 渲染与 Think 标签解析测试)
+echo "\n--- [测试用例 5] 页面模版渲染:访问 HTML 页面 (View Fetch) ---\n";
+try {
+ // 设置模版引擎 view_path 以支持 ThinkPHP CLI 下解析包含标签 {include file="/shopro/common/script" /}
+ \think\Config::set('template.view_path', APP_PATH . 'admin/view/');
+ $templateFile = APP_PATH . 'admin/view/shopro/commission/agent/detail.html';
+ $view = new \think\View(\think\Config::get('template'));
+ $view->assign([
+ 'auth' => $auth,
+ 'config' => [],
+ 'DARK_TYPE' => '',
+ ]);
+ $htmlContent = $view->fetch($templateFile);
+ if (!empty($htmlContent) && strpos($htmlContent, 'op-dashboard') !== false) {
+ echo "✓ 测试 5 通过!HTML 模版解析成功,成功输出 op-dashboard 容器,模版大小: " . strlen($htmlContent) . " bytes\n";
+ $passCount++;
+ } else {
+ echo "✗ 测试 5 模版渲染返回内容: " . substr($htmlContent, 0, 100) . "\n";
+ }
+} catch (\Throwable $e) {
+ echo "✗ 测试 5 异常: [" . get_class($e) . "] " . $e->getMessage() . " at " . $e->getFile() . ":" . $e->getLine() . "\n";
+}
+
+echo "\n====================================================================\n";
+echo " 测试总结: 共 5 项测试全部执行完毕,通过 {$passCount}/5 项\n";
+echo "====================================================================\n";
+
+if ($passCount !== 5) {
+ exit(1);
+}