feat(dashboard): 从专业财富观角度重构升级后台收益与分成统计仪表盘
这个提交包含在:
@@ -126,21 +126,217 @@ class Agent extends Commission
|
||||
public function team()
|
||||
{
|
||||
$agentId = $this->service->user->id;
|
||||
$levelType = $this->request->param('level_type', '1'); // 1=直推, 2=间推, all=全部
|
||||
$filterType = $this->request->param('filter_type', 'all'); // all, partner, active, expiring, inactive
|
||||
$keyword = trim((string) $this->request->param('keyword', ''));
|
||||
$sort = $this->request->param('sort', 'createtime'); // createtime, child_count, consume
|
||||
$listRows = (int) $this->request->param('list_rows', 10);
|
||||
|
||||
$data = UserModel::where('parent_user_id', $agentId)
|
||||
// 1. 获取直推一级成员 ID 列表
|
||||
$level1Ids = UserModel::where('parent_user_id', $agentId)
|
||||
->where('status', 'normal')
|
||||
->with(['agent' => function ($query) {
|
||||
return $query->with('level_info');
|
||||
}])
|
||||
->paginate($this->request->param('list_rows', 8));
|
||||
->column('id');
|
||||
|
||||
$data->each(function ($user) {
|
||||
if ($user->agent) {
|
||||
$user->setRelation('agent', \addons\shopro\service\partner\IdentityService::attachAgent($user->agent));
|
||||
// 2. 根据 level_type 确定父级范围
|
||||
$query = UserModel::alias('u')->where('u.status', 'normal');
|
||||
if ($levelType == '2') {
|
||||
if (empty($level1Ids)) {
|
||||
$query->whereRaw('1=0');
|
||||
} else {
|
||||
$query->whereIn('u.parent_user_id', $level1Ids);
|
||||
}
|
||||
} elseif ($levelType == 'all') {
|
||||
$allParentIds = array_merge([$agentId], $level1Ids);
|
||||
$query->whereIn('u.parent_user_id', $allParentIds);
|
||||
} else {
|
||||
$query->where('u.parent_user_id', $agentId);
|
||||
}
|
||||
|
||||
// 3. 关键字搜索 (昵称/手机号/ID)
|
||||
if ($keyword !== '') {
|
||||
$query->where(function ($q) use ($keyword) {
|
||||
if (is_numeric($keyword)) {
|
||||
$q->where('u.id', intval($keyword))
|
||||
->whereOr('u.mobile', 'like', "%{$keyword}%")
|
||||
->whereOr('u.nickname', 'like', "%{$keyword}%");
|
||||
} else {
|
||||
$q->where('u.nickname', 'like', "%{$keyword}%");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 4. 计算活跃度时间窗口 (默认30天保活,剩余<=5天为临期)
|
||||
$windowDays = \addons\shopro\service\partner\PartnerConfig::activeDays();
|
||||
$windowStart = (new \addons\shopro\service\partner\ActiveService())->windowStart();
|
||||
$expiringStart = time() - ($windowDays - 5) * 86400;
|
||||
|
||||
// 5. 身份与活跃状态过滤
|
||||
if ($filterType === 'partner') {
|
||||
$partnerUserIds = AgentModel::where('status', 'in', ['normal', 'freeze'])
|
||||
->where(function ($q) {
|
||||
$q->where('level', '>=', 2)
|
||||
->whereOr('is_venue', 1)
|
||||
->whereOr('is_county', 1);
|
||||
})->column('user_id');
|
||||
if (empty($partnerUserIds)) {
|
||||
$query->whereRaw('1=0');
|
||||
} else {
|
||||
$query->whereIn('u.id', $partnerUserIds);
|
||||
}
|
||||
} elseif ($filterType === 'active') {
|
||||
$activeUserIds = AgentModel::where('last_self_order_time', '>=', $expiringStart)->column('user_id');
|
||||
if (empty($activeUserIds)) {
|
||||
$query->whereRaw('1=0');
|
||||
} else {
|
||||
$query->whereIn('u.id', $activeUserIds);
|
||||
}
|
||||
} elseif ($filterType === 'expiring') {
|
||||
$expiringUserIds = AgentModel::where('last_self_order_time', '>=', $windowStart)
|
||||
->where('last_self_order_time', '<', $expiringStart)
|
||||
->column('user_id');
|
||||
if (empty($expiringUserIds)) {
|
||||
$query->whereRaw('1=0');
|
||||
} else {
|
||||
$query->whereIn('u.id', $expiringUserIds);
|
||||
}
|
||||
} elseif ($filterType === 'inactive') {
|
||||
$validUserIds = AgentModel::where('last_self_order_time', '>=', $windowStart)->column('user_id');
|
||||
if (!empty($validUserIds)) {
|
||||
$query->whereNotIn('u.id', $validUserIds);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 排序处理
|
||||
if ($sort === 'child_count') {
|
||||
$agentTable = (new AgentModel())->getTable();
|
||||
$query->join($agentTable . ' a', 'u.id = a.user_id', 'LEFT')
|
||||
->order('a.child_user_count_1', 'desc')
|
||||
->order('u.id', 'desc');
|
||||
} elseif ($sort === 'consume') {
|
||||
$query->order('u.total_consume', 'desc')->order('u.id', 'desc');
|
||||
} else {
|
||||
$query->order('u.createtime', 'desc')->order('u.id', 'desc');
|
||||
}
|
||||
$query->field('u.*');
|
||||
|
||||
// 7. 分页并加载推荐人与分销商信息
|
||||
$data = $query->with([
|
||||
'parentUser' => function ($q) {
|
||||
$q->field('id, nickname, avatar, mobile');
|
||||
},
|
||||
'agent' => function ($q) {
|
||||
$q->with('level_info');
|
||||
}
|
||||
])->paginate($listRows);
|
||||
|
||||
// 8. 增强成员卡片数据
|
||||
$now = time();
|
||||
$data->each(function ($user) use ($agentId, $windowDays, $now) {
|
||||
$userAgent = $user->agent;
|
||||
if ($userAgent) {
|
||||
$attached = \addons\shopro\service\partner\IdentityService::attachAgent($userAgent);
|
||||
$user->setRelation('agent', $attached);
|
||||
|
||||
$last = intval($attached['last_self_order_time'] ?? 0);
|
||||
$isActive = !empty($attached['active_30d']);
|
||||
|
||||
if ($isActive) {
|
||||
if ($last > 0) {
|
||||
$daysPassed = floor(($now - $last) / 86400);
|
||||
$remaining = max(0, intval($windowDays - $daysPassed));
|
||||
$user->active_status = ($remaining <= 5) ? 'expiring' : 'active';
|
||||
$user->days_remaining = $remaining;
|
||||
} else {
|
||||
$user->active_status = 'active';
|
||||
$user->days_remaining = $windowDays;
|
||||
}
|
||||
} else {
|
||||
$user->active_status = 'inactive';
|
||||
$user->days_remaining = 0;
|
||||
}
|
||||
|
||||
$user->order_count = intval($userAgent['child_order_count_0'] ?? 0);
|
||||
$user->consume_amount = \addons\shopro\service\partner\IdentityService::formatMoney($userAgent['child_order_money_0'] ?? ($user->total_consume ?? 0));
|
||||
$user->child_user_count_1 = intval($userAgent['child_user_count_1'] ?? 0);
|
||||
} else {
|
||||
$user->active_status = 'inactive';
|
||||
$user->days_remaining = 0;
|
||||
$user->order_count = 0;
|
||||
$user->consume_amount = \addons\shopro\service\partner\IdentityService::formatMoney($user->total_consume ?? 0);
|
||||
$user->child_user_count_1 = 0;
|
||||
}
|
||||
|
||||
$user->team_level = ($user->parent_user_id == $agentId) ? 1 : 2;
|
||||
$rawMobile = $user->mobile ? (string) $user->mobile : '';
|
||||
$user->mobile_raw = $rawMobile;
|
||||
$user->mobile_hide = $rawMobile ? (strlen($rawMobile) == 11 ? substr_replace($rawMobile, '****', 3, 4) : $rawMobile) : '';
|
||||
});
|
||||
|
||||
$this->success("", $data);
|
||||
// 9. 计算整体团队健康度统计
|
||||
$summary = $this->getTeamHealthSummary($agentId, $level1Ids, $windowStart, $expiringStart);
|
||||
|
||||
$result = $data->toArray();
|
||||
$result['summary'] = $summary;
|
||||
|
||||
$this->success("", $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算团队保活健康度统计
|
||||
*/
|
||||
protected function getTeamHealthSummary($agentId, $level1Ids, $windowStart, $expiringStart)
|
||||
{
|
||||
$allMembers = array_merge([$agentId], $level1Ids);
|
||||
$teamUserIds = UserModel::whereIn('parent_user_id', $allMembers)
|
||||
->where('status', 'normal')
|
||||
->column('id');
|
||||
|
||||
if (empty($teamUserIds)) {
|
||||
return [
|
||||
'total_count' => 0,
|
||||
'active_count' => 0,
|
||||
'expiring_count' => 0,
|
||||
'inactive_count' => 0,
|
||||
'partner_count' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$agents = AgentModel::whereIn('user_id', $teamUserIds)->select();
|
||||
$activeCount = 0;
|
||||
$expiringCount = 0;
|
||||
$inactiveCount = 0;
|
||||
$partnerCount = 0;
|
||||
|
||||
foreach ($agents as $ag) {
|
||||
$level = intval($ag->level ?? 0);
|
||||
$isVenue = intval($ag->is_venue ?? 0);
|
||||
$isCounty = intval($ag->is_county ?? 0);
|
||||
if ($level >= 2 || $isVenue == 1 || $isCounty == 1) {
|
||||
$partnerCount++;
|
||||
}
|
||||
|
||||
$last = intval($ag->last_self_order_time ?? 0);
|
||||
if ($last >= $expiringStart) {
|
||||
$activeCount++;
|
||||
} elseif ($last >= $windowStart) {
|
||||
$expiringCount++;
|
||||
} else {
|
||||
$inactiveCount++;
|
||||
}
|
||||
}
|
||||
|
||||
$missingAgentCount = count($teamUserIds) - count($agents);
|
||||
if ($missingAgentCount > 0) {
|
||||
$inactiveCount += $missingAgentCount;
|
||||
}
|
||||
|
||||
return [
|
||||
'total_count' => count($teamUserIds),
|
||||
'active_count' => $activeCount,
|
||||
'expiring_count' => $expiringCount,
|
||||
'inactive_count' => $inactiveCount,
|
||||
'partner_count' => $partnerCount,
|
||||
];
|
||||
}
|
||||
|
||||
// 佣金转余额
|
||||
|
||||
在新工单中引用
屏蔽一个用户