From dbb99518570583916068cceb39c8c109389f2b2f Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 29 Sep 2026 15:07:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(commission):=20=E9=87=8D=E6=9E=84=E5=88=86?= =?UTF-8?q?=E9=94=80=E5=95=86=E8=AF=A6=E6=83=85=E8=BF=90=E8=90=A5=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1=E7=9C=8B=E6=9D=BF=E7=B4=A7=E6=89=A3=E6=81=92=E8=81=94?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E4=B8=8E=E6=96=B9=E6=A1=88B=E4=B8=80?= =?UTF-8?q?=E4=BD=93=E5=8C=96=E5=A4=A7=E4=BD=99=E9=A2=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/shopro/commission/Agent.php | 194 +++++- .../view/shopro/commission/agent/detail.html | 613 ++++++++++++------ tests/test_agent_detail_fix.php | 193 ++++++ 3 files changed, 815 insertions(+), 185 deletions(-) create mode 100644 tests/test_agent_detail_fix.php 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.current_level_name }} + + + 目标:{{ state.data.operational_stats.upgrade_radar.next_level_name }} + + + 已达顶级 + +
+
+ + 已锁定升级 + + + ✓ 晋升条件全部达成 + + + 考核晋升中 + +
+
+ +
+ +
+
+ 有效直推人数指标 + + {{ 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); +}