Files
huangjingfen/pro_v3.5.1/app/services/agent/AgentLevelServices.php
panchengyong c1e74d8e68 chore(php): 统一 ScottPan 文件头与注释域名替换
- 按 docs/renew-code-comment.md 将 PHP 文件头改为带边框的 Author 注释\n- 注释中的 crmeb.com 替换为 uj345.cn(代码字符串中的外链未改)\n- 新增 docs/renew-code-comment.md 说明

Made-with: Cursor
2026-03-29 11:22:58 +08:00

565 lines
24 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// +----------------------------------------------------------------------
// | Author: ScottPan Team
// +----------------------------------------------------------------------
namespace app\services\agent;
use app\dao\agent\AgentLevelDao;
use app\services\BaseServices;
use app\services\order\StoreOrderServices;
use app\services\user\UserBrokerageServices;
use app\services\user\UserExtractServices;
use app\services\user\UserServices;
use app\services\user\UserSpreadServices;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use FormBuilder\Factory\Iview;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Route as Url;
/**
* 分销等级
* Class AgentLevelServices
* @package app\services\agent
* @mixin AgentLevelDao
*/
class AgentLevelServices extends BaseServices
{
/**
* @var AgentLevelDao
*/
#[Inject]
protected AgentLevelDao $dao;
/**
* HJF 官方会员等级名称(与数据库插入数据一致)
* 用于区分 CRMEB 默认「等级一/等级二…」与 HJF 创客/云店…
*/
public const HJF_OFFICIAL_LEVEL_NAMES = ['创客', '云店', '服务商', '分公司'];
/**
* 一次查询并返回用户列表展示所需等级索引(供 UserServices::index() 调用)
*
* @return array{byId: array<int,array>, byGradeAny: array<int,array>, byGradeOfficial: array<int,array>}
*/
public function loadHjfUserListLevelMaps(): array
{
$rows = $this->dao->getList(['is_del' => 0]);
return $this->buildHjfUserListLevelMaps($rows);
}
/**
* 从等级行列表构建用户列表展示用索引
*
* @param array $hjfLevelRows
* @return array{byId: array<int,array>, byGradeAny: array<int,array>, byGradeOfficial: array<int,array>}
*/
public function buildHjfUserListLevelMaps(array $hjfLevelRows): array
{
$byId = [];
$byGradeAny = [];
$byGradeOfficial = [];
$official = self::HJF_OFFICIAL_LEVEL_NAMES;
foreach ($hjfLevelRows as $hjfRow) {
$lid = (int)($hjfRow['id'] ?? 0);
if ($lid > 0) {
$byId[$lid] = $hjfRow;
}
$g = (int)($hjfRow['grade'] ?? 0);
if ($g > 0 && !isset($byGradeAny[$g])) {
$byGradeAny[$g] = $hjfRow;
}
$nm = (string)($hjfRow['name'] ?? '');
if ($g > 0 && in_array($nm, $official, true) && !isset($byGradeOfficial[$g])) {
$byGradeOfficial[$g] = $hjfRow;
}
}
return ['byId' => $byId, 'byGradeAny' => $byGradeAny, 'byGradeOfficial' => $byGradeOfficial];
}
/**
* 用户列表场景:解析应展示的等级行
* - agent_level 指向 CRMEB 默认行时,按 grade 改用 HJF 官方行
* - 旧 id 已软删或误把 grade 写入 agent_level 时,按 byGradeAny 回退
*
* @param int $agentLevelId 用户的 agent_level 字段值
* @param array $maps loadHjfUserListLevelMaps() 返回的索引
* @return array|null
*/
public function pickHjfLevelRowForUserListDisplay(int $agentLevelId, array $maps): ?array
{
if ($agentLevelId <= 0) {
return null;
}
$byId = $maps['byId'] ?? [];
$byGradeAny = $maps['byGradeAny'] ?? [];
$byGradeOfficial = $maps['byGradeOfficial'] ?? [];
$official = self::HJF_OFFICIAL_LEVEL_NAMES;
$row = $byId[$agentLevelId] ?? null;
if ($row === null) {
return $byGradeAny[$agentLevelId] ?? null;
}
$nm = (string)($row['name'] ?? '');
$g = (int)($row['grade'] ?? 0);
if ($g > 0 && !in_array($nm, $official, true) && isset($byGradeOfficial[$g])) {
return $byGradeOfficial[$g];
}
return $row;
}
/**
* 根据 grade 获取 agent_level ID用于筛选条件转换
*
* @param int $grade 等级数字 (1=创客, 2=云店, 3=服务商, 4=分公司)
* @return int agent_level ID找不到返回 0
*/
public function getLevelIdByGrade(int $grade): int
{
if ($grade <= 0) {
return 0;
}
return (int)$this->dao->value(['grade' => $grade, 'is_del' => 0, 'status' => 1], 'id') ?: 0;
}
/**
* 根据 agent_level ID 获取等级 gradeHJF 会员等级数字 0-4
*/
public function getGradeByLevelId(int $agentLevelId): int
{
if ($agentLevelId <= 0) {
return 0;
}
$levelInfo = $this->getLevelInfo($agentLevelId);
return (int)($levelInfo['grade'] ?? 0);
}
/**
* 根据 agent_level ID 获取直推奖励积分
*/
public function getDirectRewardPoints(int $agentLevelId): int
{
if ($agentLevelId <= 0) {
return 0;
}
$levelInfo = $this->getLevelInfo($agentLevelId);
return (int)($levelInfo['direct_reward_points'] ?? 0);
}
/**
* 根据 agent_level ID 获取伞下奖励积分
*/
public function getUmbrellaRewardPoints(int $agentLevelId): int
{
if ($agentLevelId <= 0) {
return 0;
}
$levelInfo = $this->getLevelInfo($agentLevelId);
return (int)($levelInfo['umbrella_reward_points'] ?? 0);
}
/**
* 获取某一个等级信息
* @param int $id
* @param string $field
* @param array $with
* @return array|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getLevelInfo(int $id, string $field = '*', array $with = [])
{
return $this->dao->getOne(['id' => $id, 'is_del' => 0], $field, $with);
}
/**
* 获取等级列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getLevelList(array $where)
{
$where['is_del'] = 0;
[$page, $limit] = $this->getPageValue();
$count = $this->dao->count($where);
$list = [];
if ($count) {
$list = $this->dao->getList($where, '*', ['task' => function ($query) {
$query->field('count(*) as sum');
}], $page, $limit);
$store_brokerage_ratio = sys_config('store_brokerage_ratio');
$store_brokerage_two = sys_config('store_brokerage_two');
foreach ($list as &$item) {
$item['one_brokerage_ratio'] = $this->compoteBrokerage($store_brokerage_ratio, $item['one_brokerage']);
$item['two_brokerage_ratio'] = $this->compoteBrokerage($store_brokerage_two, $item['two_brokerage']);
}
}
return compact('count', 'list');
}
/**
* 商城获取分销员等级列表
* @param int $uid
* @return array
*/
public function getUserlevelList(int $uid)
{
//商城分销是否开启
if (!sys_config('brokerage_func_status')) {
return [];
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserCacheInfo($uid);
if (!$user) {
throw new ValidateException('没有此用户');
}
try {
//检测升级
$this->checkUserLevelFinish($uid);
} catch (\Throwable $e) {
}
$list = $this->dao->getList(['is_del' => 0, 'status' => 1]);
$agent_level = $user['agent_level'] ?? 0;
//没等级默认最低等级
if (!$agent_level) {
$levelInfo = [];
} else {
$levelInfo = $this->getLevelInfo($agent_level) ?: [];
}
$sum_task = $finish_task = 0;
if ($levelInfo) {
/** @var AgentLevelTaskServices $levelTaskServices */
$levelTaskServices = app()->make(AgentLevelTaskServices::class);
$sum_task = $levelTaskServices->count(['level_id' => $levelInfo['id'], 'is_del' => 0, 'status' => 1]);
/** @var AgentLevelTaskRecordServices $levelTaskRecordServices */
$levelTaskRecordServices = app()->make(AgentLevelTaskRecordServices::class);
$finish_task = $levelTaskRecordServices->count(['level_id' => $levelInfo['id'], 'uid' => $uid]);
}
$levelInfo['sum_task'] = $sum_task;
$levelInfo['finish_task'] = $finish_task;
//推广订单总数
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
$user['spread_order_count'] = $orderServices->count(['type' => 0, 'paid' => 1, 'refund_status' => [0, 3], 'is_del' => 0, 'is_system_del' => 0, 'spread_or_uid' => $uid]);
//冻结佣金和可提现金额
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$user['broken_commission'] = $userBrokerageServices->getUserFrozenPrice($uid);
$user['commissionCount'] = bcsub($user['brokerage_price'], $user['broken_commission'], 2);
$user['commissionCount'] = max($user['commissionCount'], 0);
$whereType = [['type', 'in', ['self_brokerage', 'one_brokerage', 'two_brokerage', 'brokerage_user', 'division_brokerage', 'agent_brokerage', 'staff_brokerage']]];
$brokerage = $userBrokerageServices->sum($whereType + ['uid' => $uid, 'pm' => 1], 'number', true);
$refund = $userBrokerageServices->sum(['uid' => $uid, 'pm' => 0, 'type' => 'refund'], 'number', true);
$user['accumulate'] = bcsub((string)$brokerage, (string)$refund, 2);
//佣金排行
$user['position_count'] = $userBrokerageServices->getUserBrokerageRank($uid, 'week')['position'];
//推广人排行
$startTime = strtotime('this week Monday');
$endTime = time();
$field = 'spread_uid,count(uid) AS count,spread_time';
/** @var UserSpreadServices $userSpreadServices */
$userSpreadServices = app()->make(UserSpreadServices::class);
$rankList = $userSpreadServices->getAgentRankList([$startTime, $endTime], $field);
$rank = 0;
if ($rankList) {
$key = array_search($uid, array_column($rankList, 'spread_uid'));
if ($key !== false) {
$rank = (int)$key + 1;
}
}
foreach ($rankList as $key => $item) {
if ($item['spread_uid'] == $uid) $rank = $key + 1;
}
$user['rank_count'] = $rank;
$user['division_type'] = (int)sys_config('division_open') ? $user['division_type'] : 0;
$user['spread_count'] = $userServices->count(['spread_uid' => $uid]);
/** @var UserExtractServices $extractService */
$extractService = app()->make(UserExtractServices::class);
$user['extract_price'] = $extractService->sum(['uid' => $uid, 'status' => 1], 'extract_price');
$user['division_apply_open'] = (int)sys_config('division_apply_open');
if($user['agent_id'] == 0 && $user['division_type'] == 2){
$user['agent_id'] = $user['uid'];
}
return ['user' => $user, 'level_list' => $list, 'level_info' => $levelInfo];
}
/**
* 获取下一等级
* @param int $level_id
* @return array|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getNextLevelInfo(int $level_id = 0)
{
$grade = 0;
if ($level_id) {
$grade = $this->dao->value(['id' => $level_id, 'is_del' => 0, 'status' => 1], 'grade') ?: 0;
}
return $this->dao->getOne([['grade', '>', $grade], ['is_del', '=', 0], ['status', '=', 1]]);
}
/**
* 检测用户是否能升级
* @param int $uid
* @param array $uids
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function checkUserLevelFinish(int $uid, array $uids = [])
{
//商城分销是否开启
if (!sys_config('brokerage_func_status')) {
return false;
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserCacheInfo($uid);
if (!$userInfo) {
return false;
}
$list = $this->dao->getList(['is_del' => 0, 'status' => 1]);
if (!$list) {
return false;
}
if (!$uids) {
//获取上级uid 开启自购返回自己uid
$spread_uid = $userServices->getSpreadUid($uid, $userInfo);
$two_spread_uid = 0;
if ($spread_uid > 0 && $one_user_info = $userServices->getUserCacheInfo($spread_uid)) {
$two_spread_uid = $userServices->getSpreadUid($spread_uid, $one_user_info, false);
}
$uids = array_unique([$uid, $spread_uid, $two_spread_uid]);
}
foreach ($uids as $uid) {
if ($uid <= 0) continue;
if ($uid != $userInfo['uid']) {
$userInfo = $userServices->getUserCacheInfo($uid);
if (!$userInfo)
continue;
}
$now_grade = 0;
if ($userInfo['agent_level']) {
$now_grade = $this->dao->value(['id' => $userInfo['agent_level']], 'grade') ?: 0;
}
foreach ($list as $levelInfo) {
if (!$levelInfo || $levelInfo['grade'] <= $now_grade) {
continue;
}
/** @var AgentLevelTaskServices $levelTaskServices */
$levelTaskServices = app()->make(AgentLevelTaskServices::class);
$task_list = $levelTaskServices->getTaskList(['level_id' => $levelInfo['id'], 'is_del' => 0, 'status' => 1]);
if (!$task_list) {
continue;
}
foreach ($task_list as $task) {
$levelTaskServices->checkLevelTaskFinish($uid, (int)$task['id'], $task);
}
/** @var AgentLevelTaskRecordServices $levelTaskRecordServices */
$levelTaskRecordServices = app()->make(AgentLevelTaskRecordServices::class);
$ids = array_column($task_list, 'id');
$finish_task = $levelTaskRecordServices->count(['level_id' => $levelInfo['id'], 'uid' => $uid, 'task_id' => $ids]);
//任务完成升这一等级
if ($finish_task >= count($task_list)) {
$userServices->update($uid, ['agent_level' => $levelInfo['id']]);
} else {
break;
}
}
}
return true;
}
/**
* 分销等级上浮
* @param int $uid
* @param array $userInfo
* @return array|int[]
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getAgentLevelBrokerage(int $uid, $userInfo = [])
{
$one_brokerage_up = $two_brokerage_up = $spread_uid = $spread_two_uid = 0;
$data = [$one_brokerage_up, $two_brokerage_up, $spread_uid, $spread_two_uid];
if (!$uid) {
return $data;
}
//商城分销是否开启
if (!sys_config('brokerage_func_status')) {
return $data;
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
if (!$userInfo) {
$userInfo = $userServices->getUserCacheInfo($uid);
}
if (!$userInfo) {
return $data;
}
//获取上级uid 开启自购返回自己uid
$spread_uid = $userServices->getSpreadUid($uid, $userInfo);
$one_agent_level = 0;
$two_agent_level = 0;
if ($spread_uid > 0 && $one_user_info = $userServices->getUserInfo($spread_uid)) {
$one_agent_level = $one_user_info['agent_level'] ?? 0;
$spread_two_uid = $userServices->getSpreadUid($spread_uid, $one_user_info, false);
if ($spread_two_uid > 0 && $two_user_info = $userServices->getUserInfo($spread_two_uid)) {
$two_agent_level = $two_user_info['agent_level'] ?? 0;
}
}
//获取后台一级返佣比例
$storeBrokerageRatio = sys_config('store_brokerage_ratio');
//一级上浮之后的反佣比例
$storeBrokerageRatio = $one_agent_level ? bcadd($storeBrokerageRatio, bcmul($storeBrokerageRatio, bcdiv(($this->getLevelInfo($one_agent_level)['one_brokerage'] ?? 0), 100, 2), 2), 2) : $storeBrokerageRatio;
//获取二级返佣比例
$storeBrokerageTwo = sys_config('store_brokerage_two');
//二级上浮之后的反佣比例
$storeBrokerageTwo = $two_agent_level ? bcadd($storeBrokerageTwo, bcmul($storeBrokerageTwo, bcdiv(($this->getLevelInfo($two_agent_level)['two_brokerage'] ?? 0), 100, 2), 2), 2) : $storeBrokerageTwo;
return [$storeBrokerageRatio, $storeBrokerageTwo, $spread_uid, $spread_two_uid];
}
/**
* 计算一二级返佣比率上浮
* @param $ratio
* @param $brokerage
* @return int|string
*/
public function compoteBrokerage($ratio, $brokerage)
{
if (!$ratio) {
return 0;
}
$brokerage = bcdiv((string)$brokerage, '100', 4);
if ($brokerage) {
return bcmul((string)$ratio, bcadd('1', (string)$brokerage, 4), 2);
}
return $brokerage;
}
/**
* 添加等级表单
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function createForm()
{
$store_brokerage_ratio = sys_config('store_brokerage_ratio');
$store_brokerage_two = sys_config('store_brokerage_two');
$field[] = Form::input('name', '等级名称:')->col(24);
$field[] = Form::number('grade', '等级:', 0)->min(0)->precision(0);
$field[] = Form::frameImage('image', '背景图:', Url::buildUrl('admin/widget.images/index', array('fodder' => 'image')))->icon('ios-add')->width('960px')->height('505px')->modal(['footer-hide' => true])->appendValidate(Iview::validateStr()->required()->message('请选择背景图'));
$field[] = Form::color('color', '字体颜色:')->required('请选择字体颜色');
$field[] = Form::number('one_brokerage', '一级上浮:', 0)->info('在分销一级佣金基础上浮0-1000之间整数百分比目前一级返佣比率' . $store_brokerage_ratio . '%例如上浮10%,则返佣比率:一级返佣比率 * (1 + 一级上浮比率) = ' . $this->compoteBrokerage($store_brokerage_ratio, 10) . '%')->min(0)->max(1000);
$field[] = Form::number('two_brokerage', '二级上浮:', 0)->info('在分销二级佣金基础上浮0-1000之间整数百分比目前二级返佣比率' . $store_brokerage_two . '%例如上浮10%,则返佣比率:二级返佣比率 * (1 + 二级上浮比率) = ' . $this->compoteBrokerage($store_brokerage_two, 10) . '%')->min(0)->max(1000);
$field[] = Form::radio('status', '是否显示:', 1)->options([['value' => 1, 'label' => '显示'], ['value' => 0, 'label' => '隐藏']]);
return create_form('添加分销员等级', $field, Url::buildUrl('/agent/level'), 'POST');
}
/**
* 获取修改等级表单
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function editForm(int $id)
{
$store_brokerage_ratio = sys_config('store_brokerage_ratio');
$store_brokerage_two = sys_config('store_brokerage_two');
$levelInfo = $this->getLevelInfo($id);
if (!$levelInfo)
throw new AdminException('数据不存在');
$field = [];
$field[] = Form::hidden('id', $id);
$field[] = Form::input('name', '等级名称', $levelInfo['name'])->col(24);
$field[] = Form::number('grade', '等级', $levelInfo['grade'])->min(0)->precision(0);
$field[] = Form::frameImage('image', '背景图', Url::buildUrl('admin/widget.images/index', array('fodder' => 'image')), $levelInfo['image'])->icon('ios-add')->width('960px')->height('505px')->modal(['footer-hide' => true])->appendValidate(Iview::validateStr()->required()->message('请选择背景图'));
$field[] = Form::color('color', '字体颜色', $levelInfo['color'] ?? '')->required('请选择字体颜色');
$field[] = Form::number('one_brokerage', '一级上浮', $levelInfo['one_brokerage'])->info('在分销一级佣金基础上浮0-1000之间整数百分比目前一级返佣比率' . $store_brokerage_ratio . '%,上浮' . $levelInfo['one_brokerage'] . '%,则返佣比率:一级返佣比率 * (1 + 一级上浮比率) = ' . $this->compoteBrokerage($store_brokerage_ratio, $levelInfo['one_brokerage']) . '%')->min(0)->max(1000);
$field[] = Form::number('two_brokerage', '二级上浮', $levelInfo['two_brokerage'])->info('在分销二级佣金基础上浮0-1000之间整数百分比目前二级返佣比率' . $store_brokerage_two . '%,上浮' . $levelInfo['two_brokerage'] . '%,则返佣比率:二级返佣比率 * (1 + 二级上浮比率) = ' . $this->compoteBrokerage($store_brokerage_two, $levelInfo['two_brokerage']) . '%')->min(0)->max(1000);
$field[] = Form::radio('status', '是否显示', $levelInfo['status'])->options([['value' => 1, 'label' => '显示'], ['value' => 0, 'label' => '隐藏']]);
return create_form('编辑分销员等级', $field, Url::buildUrl('/agent/level/' . $id), 'PUT');
}
/**
* 赠送分销等级表单
* @param int $uid
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function levelForm(int $uid)
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserInfo($uid);
if (!$userInfo) {
throw new AdminException('分销员不存在');
}
$levelList = $this->dao->getList(['is_del' => 0, 'status' => 1]);
$setOptionLabel = function () use ($levelList) {
$menus = [];
foreach ($levelList as $level) {
$menus[] = ['value' => $level['id'], 'label' => $level['name']];
}
return $menus;
};
$field[] = Form::hidden('uid', $uid);
$field[] = Form::select('id', '分销等级', $userInfo['agent_level'] ?? 0)->setOptions(Form::setOptions($setOptionLabel))->filterable(true);
return create_form('赠送分销等级', $field, Url::buildUrl('/agent/give_level'), 'post');
}
/**
* 赠送分销等级
* @param int $uid
* @param int $id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function givelevel(int $uid, int $id)
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserInfo($uid);
if (!$userInfo) {
throw new AdminException('分销员不存在');
}
$levelInfo = $this->getLevelInfo($id);
if (!$levelInfo) {
throw new AdminException('分销等级不存在');
}
if ($userServices->update($uid, ['agent_level' => $id]) === false) {
throw new AdminException('赠送失败');
}
return true;
}
}