441 行
19 KiB
PHP
441 行
19 KiB
PHP
<?php
|
|
|
|
namespace addons\shopro\controller\commission;
|
|
|
|
use think\Db;
|
|
use app\admin\model\shopro\user\User as UserModel;
|
|
use app\admin\model\shopro\commission\Agent as AgentModel;
|
|
use app\admin\model\shopro\goods\Goods as GoodsModel;
|
|
use app\admin\model\shopro\partner\Reward as RewardModel;
|
|
use addons\shopro\service\Wallet;
|
|
|
|
|
|
class Agent extends Commission
|
|
{
|
|
protected $noNeedLogin = [];
|
|
protected $noNeedRight = ['*'];
|
|
|
|
// 分销商详情
|
|
public function index()
|
|
{
|
|
$status = $this->service->getAgentStatus(true);
|
|
|
|
$condition = [
|
|
'type' => '',
|
|
'value' => ''
|
|
];
|
|
|
|
switch ($status) {
|
|
case AgentModel::AGENT_STATUS_NULL:
|
|
$condition = $this->service->config->getBecomeAgentEvent();
|
|
if ($condition['type'] === 'goods') {
|
|
$condition['value'] = GoodsModel::show()->whereIn('id', $condition['value'])->select();
|
|
}
|
|
$this->error('', $condition, 100);
|
|
break;
|
|
case AgentModel::AGENT_STATUS_NEEDINFO:
|
|
$this->error('待完善信息,请补充您的资料后提交审核', $condition, 103);
|
|
break;
|
|
case AgentModel::AGENT_STATUS_PENDING:
|
|
$this->error('正在审核中,请耐心等候结果', $condition, 104);
|
|
break;
|
|
case AgentModel::AGENT_STATUS_REJECT:
|
|
$agentFormStatus = $this->service->config->isAgentApplyForm();
|
|
if ($agentFormStatus) {
|
|
$this->error('抱歉!您的申请信息未通过,请尝试修改后重新提交', $condition, 105);
|
|
} else {
|
|
$this->error('抱歉!您的申请未通过,请尝试重新申请', $condition, 106);
|
|
}
|
|
break;
|
|
case AgentModel::AGENT_STATUS_FREEZE:
|
|
$this->error('抱歉!您的账户已被冻结,如有疑问请联系客服', $condition, 107);
|
|
break;
|
|
}
|
|
$data = \addons\shopro\service\partner\IdentityService::attachAgent($this->service->agent);
|
|
|
|
$this->success('分销商信息', $data);
|
|
}
|
|
|
|
// 分销商完善个人信息
|
|
public function form()
|
|
{
|
|
if (!$this->service->config->isAgentApplyForm()) {
|
|
$this->error('未开启分销商申请');
|
|
}
|
|
|
|
$agentForm = $this->service->config->getAgentForm();
|
|
$protocol = $this->service->config->getApplyProtocol();
|
|
$applyInfo = $this->service->agent->apply_info ?? [];
|
|
|
|
$config = [
|
|
'form' => $agentForm['content'],
|
|
'status' => $this->service->getAgentStatus(true),
|
|
'background' => $agentForm['background_image'],
|
|
'protocol' => $protocol,
|
|
'applyInfo' => $applyInfo
|
|
];
|
|
|
|
$this->success("", $config);
|
|
}
|
|
|
|
// 申请分销商/完善资料
|
|
public function apply()
|
|
{
|
|
if (!$this->request->isPost()) {
|
|
$this->error('');
|
|
}
|
|
if (!$this->service->config->isAgentApplyForm()) {
|
|
$this->error('未开启分销商申请');
|
|
}
|
|
$status = $this->service->getAgentStatus(true);
|
|
|
|
if (!in_array($status, [AgentModel::AGENT_STATUS_NEEDINFO, AgentModel::AGENT_STATUS_REJECT, AgentModel::AGENT_STATUS_NORMAL])) {
|
|
$this->error('您暂时不能申请');
|
|
}
|
|
Db::transaction(function () use ($status) {
|
|
$data = $this->request->param('data/a');
|
|
// 过滤无效表单字段数据
|
|
$config = $this->service->config->getAgentForm();
|
|
$form = (array)$config['content'];
|
|
$data = array_column($data, 'value', 'name');
|
|
|
|
foreach ($form as &$item) {
|
|
if (!empty($data[$item['name']])) {
|
|
$item['value'] = $data[$item['name']];
|
|
} else {
|
|
$this->error($item['name'] . '不能为空');
|
|
}
|
|
}
|
|
if ($status === AgentModel::AGENT_STATUS_NEEDINFO) {
|
|
$this->service->createNewAgent('', $form);
|
|
} else {
|
|
// 重置为审核中
|
|
if ($status === AgentModel::AGENT_STATUS_REJECT) {
|
|
$this->service->agent->status = AgentModel::AGENT_STATUS_PENDING;
|
|
}
|
|
// 更新分销商信息
|
|
$this->service->agent->apply_info = $form;
|
|
$this->service->agent->save();
|
|
}
|
|
});
|
|
|
|
|
|
$this->success('提交成功');
|
|
}
|
|
|
|
// 我的团队(支持直推查看与安全下钻)
|
|
public function team()
|
|
{
|
|
$agentId = $this->service->user->id;
|
|
$drillUserId = (int) $this->request->param('drill_user_id', 0);
|
|
$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);
|
|
|
|
$targetUserId = $agentId;
|
|
$drillUser = null;
|
|
|
|
// 1. 如果传了下钻目标成员,严格执行树状归属鉴权,防越权防注入
|
|
if ($drillUserId > 0 && $drillUserId !== $agentId) {
|
|
$targetUser = UserModel::where('id', $drillUserId)->where('status', 'normal')->find();
|
|
if (!$targetUser) {
|
|
$this->error('指定成员不存在或已注销');
|
|
}
|
|
|
|
// 向上追溯层级,确保目标成员在当前用户的团队树中
|
|
$isDownline = false;
|
|
$currParentId = $targetUser->parent_user_id;
|
|
$depth = 0;
|
|
while ($currParentId > 0 && $depth < 30) {
|
|
if ($currParentId == $agentId) {
|
|
$isDownline = true;
|
|
break;
|
|
}
|
|
$currParentId = UserModel::where('id', $currParentId)->value('parent_user_id') ?: 0;
|
|
$depth++;
|
|
}
|
|
|
|
if (!$isDownline) {
|
|
$this->error('无权查看非直属团队成员信息');
|
|
}
|
|
|
|
$targetUserId = $drillUserId;
|
|
$targetAgent = AgentModel::where('user_id', $targetUserId)->find();
|
|
$drillUser = [
|
|
'id' => $targetUser->id,
|
|
'nickname' => $targetUser->nickname ?: '微信用户',
|
|
'avatar' => $targetUser->avatar,
|
|
'parent_user_id' => $targetUser->parent_user_id,
|
|
'child_user_count_1' => intval($targetAgent->child_user_count_1 ?? 0),
|
|
'child_user_count_all' => intval($targetAgent->child_user_count_all ?? ($targetAgent->child_user_count_1 ?? 0)),
|
|
];
|
|
}
|
|
|
|
// 2. 获取目标用户的直推一级成员 ID 列表(全面采用一级直推模型)
|
|
$level1Ids = UserModel::where('parent_user_id', $targetUserId)
|
|
->where('status', 'normal')
|
|
->column('id');
|
|
|
|
// 3. 构建直推成员查询
|
|
$query = UserModel::alias('u')->where('u.status', 'normal');
|
|
$query->where('u.parent_user_id', $targetUserId);
|
|
|
|
// 4. 关键字搜索 (昵称/手机号/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}%");
|
|
}
|
|
});
|
|
}
|
|
|
|
// 5. 计算活跃度时间窗口 (默认30天保活,剩余<=5天为临期)
|
|
$windowDays = \addons\shopro\service\partner\PartnerConfig::activeDays();
|
|
$windowStart = (new \addons\shopro\service\partner\ActiveService())->windowStart();
|
|
$expiringStart = time() - ($windowDays - 5) * 86400;
|
|
|
|
// 6. 身份与活跃状态过滤
|
|
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)
|
|
->whereOr('is_partner', 1)
|
|
->whereOr('child_order_money_0', '>', 0);
|
|
})->column('user_id');
|
|
$purchasedUserIds = !empty($level1Ids)
|
|
? UserModel::whereIn('id', $level1Ids)->where('total_consume', '>', 0)->column('id')
|
|
: [];
|
|
$memberUserIds = array_values(array_unique(array_merge($partnerUserIds, $purchasedUserIds)));
|
|
if (empty($memberUserIds)) {
|
|
$query->whereRaw('1=0');
|
|
} else {
|
|
$query->whereIn('u.id', $memberUserIds);
|
|
}
|
|
} 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);
|
|
}
|
|
}
|
|
|
|
// 7. 排序处理
|
|
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.*');
|
|
|
|
// 8. 分页并加载推荐人与分销商信息
|
|
$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);
|
|
|
|
$user->active_status = $attached['active_status'] ?? 'inactive';
|
|
$user->days_remaining = $attached['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 = 1;
|
|
$rawMobile = $user->mobile ? (string) $user->mobile : '';
|
|
$maskedMobile = $rawMobile ? (strlen($rawMobile) == 11 ? substr_replace($rawMobile, '****', 3, 4) : $rawMobile) : '';
|
|
$user->mobile = $maskedMobile;
|
|
$user->mobile_hide = $maskedMobile;
|
|
$user->mobile_raw = $rawMobile;
|
|
|
|
// 所属推荐人手机号脱敏
|
|
if ($user->parentUser && $user->parentUser->mobile) {
|
|
$pMobile = (string) $user->parentUser->mobile;
|
|
$pMasked = strlen($pMobile) == 11 ? substr_replace($pMobile, '****', 3, 4) : $pMobile;
|
|
$user->parentUser->mobile = $pMasked;
|
|
$user->parentUser->mobile_hide = $pMasked;
|
|
}
|
|
});
|
|
|
|
// 9. 计算整体团队健康度与综合收益业绩统计(基于当前直推用户)
|
|
$summary = $this->getTeamHealthSummary($targetUserId, $level1Ids, $windowStart, $expiringStart);
|
|
|
|
$result = $data->toArray();
|
|
$result['summary'] = $summary;
|
|
$result['drill_user'] = $drillUser;
|
|
|
|
$this->success("获取成功", $result);
|
|
}
|
|
|
|
/**
|
|
* 计算团队保活健康度与综合收益统计(全面直推口径)
|
|
*/
|
|
protected function getTeamHealthSummary($targetUserId, $level1Ids, $windowStart, $expiringStart)
|
|
{
|
|
$teamUserIds = !empty($level1Ids) ? $level1Ids : [];
|
|
|
|
if (empty($teamUserIds)) {
|
|
$selfAgent = AgentModel::where('user_id', $targetUserId)->find();
|
|
$selfUser = UserModel::where('id', $targetUserId)->find();
|
|
$teamCount = isset($selfAgent->child_user_count_all) && intval($selfAgent->child_user_count_all) > 0
|
|
? intval($selfAgent->child_user_count_all)
|
|
: intval($selfAgent->child_user_count_1 ?? 0);
|
|
return [
|
|
'total_count' => 0,
|
|
'direct_count' => 0,
|
|
'team_count' => $teamCount,
|
|
'active_count' => 0,
|
|
'expiring_count' => 0,
|
|
'inactive_count' => 0,
|
|
'partner_count' => 0,
|
|
'team_total_consume' => \addons\shopro\service\partner\IdentityService::formatMoney($selfUser->total_consume ?? 0),
|
|
'team_total_reward' => '0.00',
|
|
'team_month_reward' => '0.00',
|
|
'team_order_count' => intval($selfAgent->child_order_count_0 ?? 0),
|
|
];
|
|
}
|
|
|
|
$agents = AgentModel::whereIn('user_id', $teamUserIds)->select();
|
|
$activeCount = 0;
|
|
$expiringCount = 0;
|
|
$inactiveCount = 0;
|
|
$partnerCount = 0;
|
|
$teamOrderCount = 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++;
|
|
}
|
|
|
|
$teamOrderCount += intval($ag->child_order_count_0 ?? 0);
|
|
}
|
|
|
|
$missingAgentCount = count($teamUserIds) - count($agents);
|
|
if ($missingAgentCount > 0) {
|
|
$inactiveCount += $missingAgentCount;
|
|
}
|
|
|
|
// 1. 团队累计自购总业绩与团队订单总数(统一使用包含本人自购的分销团队全口径)
|
|
$selfAgent = AgentModel::where('user_id', $targetUserId)->find();
|
|
$selfUser = UserModel::where('id', $targetUserId)->find();
|
|
|
|
$teamTotalConsume = isset($selfAgent->child_order_money_all) && floatval($selfAgent->child_order_money_all) > 0
|
|
? floatval($selfAgent->child_order_money_all)
|
|
: (UserModel::whereIn('id', $teamUserIds)->sum('total_consume') + floatval($selfUser->total_consume ?? 0));
|
|
|
|
$teamOrderCount = isset($selfAgent->child_order_count_all) && intval($selfAgent->child_order_count_all) > 0
|
|
? intval($selfAgent->child_order_count_all)
|
|
: ($teamOrderCount + intval($selfAgent->child_order_count_0 ?? 0));
|
|
|
|
// 2. 团队成员为目标用户贡献的累计收益与本月收益
|
|
$monthStart = strtotime(date('Y-m-01 00:00:00'));
|
|
$cashTypes = \addons\shopro\service\partner\PartnerConfig::cashTypes();
|
|
$teamTotalReward = RewardModel::where('user_id', $targetUserId)
|
|
->whereIn('buyer_id', $teamUserIds)
|
|
->whereIn('reward_type', $cashTypes)
|
|
->where('status', 1)
|
|
->sum('amount');
|
|
$teamMonthReward = RewardModel::where('user_id', $targetUserId)
|
|
->whereIn('buyer_id', $teamUserIds)
|
|
->whereIn('reward_type', $cashTypes)
|
|
->where('status', 1)
|
|
->where('createtime', '>=', $monthStart)
|
|
->sum('amount');
|
|
|
|
$directCount = count($teamUserIds);
|
|
$teamCount = isset($selfAgent->child_user_count_all) && intval($selfAgent->child_user_count_all) > 0
|
|
? intval($selfAgent->child_user_count_all)
|
|
: $directCount;
|
|
|
|
return [
|
|
'total_count' => $directCount,
|
|
'direct_count' => $directCount,
|
|
'team_count' => $teamCount,
|
|
'active_count' => $activeCount,
|
|
'expiring_count' => $expiringCount,
|
|
'inactive_count' => $inactiveCount,
|
|
'partner_count' => $partnerCount,
|
|
'team_total_consume' => \addons\shopro\service\partner\IdentityService::formatMoney($teamTotalConsume),
|
|
'team_total_reward' => \addons\shopro\service\partner\IdentityService::formatMoney($teamTotalReward),
|
|
'team_month_reward' => \addons\shopro\service\partner\IdentityService::formatMoney($teamMonthReward),
|
|
'team_order_count' => $teamOrderCount,
|
|
];
|
|
}
|
|
|
|
// 佣金转余额
|
|
public function transfer()
|
|
{
|
|
if (!$this->request->isPost()) {
|
|
$this->error('');
|
|
}
|
|
$amount = $this->request->param('amount');
|
|
if ($amount <= 0) {
|
|
$this->error('请输入正确的金额');
|
|
}
|
|
Db::transaction(function () use ($amount) {
|
|
$user = auth_user();
|
|
Wallet::change($user, 'commission', -$amount, 'transfer_to_money');
|
|
Wallet::change($user, 'money', $amount, 'transfer_by_commission');
|
|
});
|
|
$this->success('');
|
|
}
|
|
}
|