feat(hjf): H5路由修复、分销等级显示优化、个人中心等级徽章
H5 部署与路由: - manifest.json: router.base 改为 "/" 适配 public/ 根目录部署 - nginx-crmeb.conf: 恢复与 feature/fsgx 一致的原始配置 - App.vue: PC端重定向路径改为动态推导,修复死循环加载问题 - static/html/pc.html: 动态推导 H5 根路径,适配本地/云端两种部署 H5登录: - pages/users/login/index.vue: H5端获取验证码跳过安全验证(条件编译) 分销等级展示修复: - AgentLevelServices: 新增 loadHjfUserListLevelMaps/pickHjfLevelRowForUserListDisplay 统一等级名称解析逻辑,优先返回 HJF 官方名称;新增 getUpgradeTasksForLevel 封装 - UserServices/MemberLevelServices: 改用统一解析方法,修复 protected $dao 访问错误 - api/hjf/MemberController: 直接取 eb_agent_level.name,新增 agent_level 原始值返回 - admin/v1/hjf/MemberController: team() 改用封装方法替代直接访问 protected dao 个人中心等级徽章: - pages/user/index.vue + member/index.vue: memberInfo 沿链路透传 - member/template1.vue: UID右侧显示HjfMemberBadge,直接读 userInfo.agent_level_name 无需等待异步 memberInfo,agentLevelGrade 计算属性从名称推导颜色等级 商品列表修复: - BaseController.php/Common.php: 恢复加密版,修复 CRMEB 授权检查失败导致的400错误 - StoreProduct model: 移除冲突的 model maker 回调 数据库: - hjf_migration.sql: 完善会员等级体系迁移脚本 - eb_agent_level.sql: 新增等级初始数据脚本 Made-with: Cursor
This commit is contained in:
@@ -27,7 +27,13 @@ use think\facade\Route as Url;
|
||||
|
||||
|
||||
/**
|
||||
* 分销等级
|
||||
* 分销等级(改造后同时作为 HJF 会员等级服务)
|
||||
*
|
||||
* PRD 改造说明:
|
||||
* - 将原有的"分销员等级"概念替换为"会员等级"
|
||||
* - 升级条件从"推广订单数/消费金额"改为"直推单数 + 伞下业绩单数"(通过 task type 6/7/8 实现)
|
||||
* - 佣金计算从"按比例返佣"改为"按等级发放固定积分"(通过 direct_reward_points / umbrella_reward_points 实现)
|
||||
*
|
||||
* Class AgentLevelServices
|
||||
* @package app\services\agent
|
||||
* @mixin AgentLevelDao
|
||||
@@ -55,6 +61,88 @@ class AgentLevelServices extends BaseServices
|
||||
return $this->dao->getOne(['id' => $id, 'is_del' => 0], $field, $with);
|
||||
}
|
||||
|
||||
/**
|
||||
* HJF 官方会员等级名称(与 database/hjf_migration.sql 插入数据一致)
|
||||
* 用于区分 CRMEB 默认「等级一/等级二…」与 HJF 创客/云店…
|
||||
*/
|
||||
public const HJF_OFFICIAL_LEVEL_NAMES = ['创客', '云店', '服务商', '分公司'];
|
||||
|
||||
/**
|
||||
* 一次查询并返回用户列表展示所需等级索引(供外部服务调用)
|
||||
* 不暴露 dao 属性,避免外部直接访问 protected $dao
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 is_del=0 的等级行列表构建用户列表展示用索引(一次查询后复用)
|
||||
*
|
||||
* @param array<int,array<string,mixed>> $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])) {
|
||||
// dao 已 order grade asc,id desc,同 grade 先出现者为较大 id
|
||||
$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 默认行(如 id=2「等级二」)时,按 grade 改用 HJF 官方行(如「云店」)
|
||||
* - 旧 id 已软删或误把 grade 写入 agent_level 时,按 byGradeAny 回退
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取等级列表
|
||||
* @param array $where
|
||||
@@ -270,13 +358,10 @@ class AgentLevelServices extends BaseServices
|
||||
}
|
||||
|
||||
/**
|
||||
* 分销等级上浮
|
||||
* @param int $uid
|
||||
* @param array $userInfo
|
||||
* @return array|int[]
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* 分销等级上浮(保留兼容,普通商品分销仍使用原逻辑)
|
||||
*
|
||||
* 注意:报单商品的积分奖励已由 PointsRewardServices 通过 direct_reward_points/umbrella_reward_points 处理,
|
||||
* 此方法仅用于普通商品的分销佣金计算。
|
||||
*/
|
||||
public function getAgentLevelBrokerage(int $uid, $userInfo = [])
|
||||
{
|
||||
@@ -285,7 +370,6 @@ class AgentLevelServices extends BaseServices
|
||||
if (!$uid) {
|
||||
return $data;
|
||||
}
|
||||
//商城分销是否开启
|
||||
if (!sys_config('brokerage_func_status')) {
|
||||
return $data;
|
||||
}
|
||||
@@ -297,7 +381,6 @@ class AgentLevelServices extends BaseServices
|
||||
if (!$userInfo) {
|
||||
return $data;
|
||||
}
|
||||
//获取上级uid || 开启自购返回自己uid
|
||||
$spread_uid = $userServices->getSpreadUid($uid, $userInfo);
|
||||
$one_agent_level = 0;
|
||||
$two_agent_level = 0;
|
||||
@@ -308,17 +391,66 @@ class AgentLevelServices extends BaseServices
|
||||
$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];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 agent_level ID 获取等级 grade(HJF 会员等级数字 0-4)
|
||||
*
|
||||
* @param int $agentLevelId eb_user.agent_level 值
|
||||
* @return int grade(0=普通会员, 1=创客, 2=云店, 3=服务商, 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算一二级返佣比率上浮
|
||||
* @param $ratio
|
||||
@@ -338,10 +470,7 @@ class AgentLevelServices extends BaseServices
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加等级表单
|
||||
* @param int $id
|
||||
* @return array
|
||||
* @throws \FormBuilder\Exception\FormBuilderException
|
||||
* 添加等级表单(改造后包含积分奖励字段)
|
||||
*/
|
||||
public function createForm()
|
||||
{
|
||||
@@ -352,17 +481,16 @@ class AgentLevelServices extends BaseServices
|
||||
$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('direct_reward_points', '直推奖励积分:', 0)->info('该等级会员每直推1单报单商品获得的冻结积分数')->min(0);
|
||||
$field[] = Form::number('umbrella_reward_points', '伞下奖励积分:', 0)->info('该等级会员伞下每入1单报单商品获得的冻结积分数(级差基数)')->min(0);
|
||||
$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');
|
||||
return create_form('添加会员等级', $field, Url::buildUrl('/agent/level'), 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取修改等级表单
|
||||
* @param int $id
|
||||
* @return array
|
||||
* @throws \FormBuilder\Exception\FormBuilderException
|
||||
* 获取修改等级表单(改造后包含积分奖励字段)
|
||||
*/
|
||||
public function editForm(int $id)
|
||||
{
|
||||
@@ -377,11 +505,13 @@ class AgentLevelServices extends BaseServices
|
||||
$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('direct_reward_points', '直推奖励积分', $levelInfo['direct_reward_points'] ?? 0)->info('该等级会员每直推1单报单商品获得的冻结积分数')->min(0);
|
||||
$field[] = Form::number('umbrella_reward_points', '伞下奖励积分', $levelInfo['umbrella_reward_points'] ?? 0)->info('该等级会员伞下每入1单报单商品获得的冻结积分数(级差基数)')->min(0);
|
||||
$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');
|
||||
return create_form('编辑会员等级', $field, Url::buildUrl('/agent/level/' . $id), 'PUT');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,10 +20,12 @@ use crmeb\services\FormBuilder as Form;
|
||||
use FormBuilder\Factory\Iview;
|
||||
use think\annotation\Inject;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\facade\Route as Url;
|
||||
|
||||
|
||||
/**
|
||||
* 分销等级任务(改造后同时支持 HJF 会员等级升级任务)
|
||||
*
|
||||
* Class AgentLevelTaskServices
|
||||
* @package app\services\agent
|
||||
@@ -33,12 +35,13 @@ class AgentLevelTaskServices extends BaseServices
|
||||
{
|
||||
/**
|
||||
* 任务类型
|
||||
* type 记录在数据库中用来区分任务
|
||||
* name 任务名 (任务名中的{$num}会自动替换成设置的数字 + 单位)
|
||||
* max_number 最大设定数值 0为不限定
|
||||
* min_number 最小设定数值
|
||||
* unit 单位
|
||||
* */
|
||||
*
|
||||
* type 1-5: 原 CRMEB 分销任务类型
|
||||
* type 6-8: HJF 会员等级升级任务类型(改造新增)
|
||||
* 6 = 直推报单单数(直推下级购买报单商品的订单数)
|
||||
* 7 = 伞下报单业绩(含业绩分离逻辑)
|
||||
* 8 = 最低直推人数
|
||||
*/
|
||||
protected array $TaskType = [
|
||||
[
|
||||
'type' => 1,
|
||||
@@ -90,6 +93,36 @@ class AgentLevelTaskServices extends BaseServices
|
||||
'unit' => '单',
|
||||
'image' => '/uploads/system/agent_spread_order.png'
|
||||
],
|
||||
[
|
||||
'type' => 6,
|
||||
'method' => 'directQueueOrderCount',
|
||||
'name' => '直推报单满{$num}',
|
||||
'real_name' => '直推报单单数',
|
||||
'max_number' => 0,
|
||||
'min_number' => 1,
|
||||
'unit' => '单',
|
||||
'image' => '/uploads/system/agent_spread_order.png'
|
||||
],
|
||||
[
|
||||
'type' => 7,
|
||||
'method' => 'umbrellaQueueOrderCount',
|
||||
'name' => '伞下报单满{$num}',
|
||||
'real_name' => '伞下报单业绩',
|
||||
'max_number' => 0,
|
||||
'min_number' => 1,
|
||||
'unit' => '单',
|
||||
'image' => '/uploads/system/agent_spread_order.png'
|
||||
],
|
||||
[
|
||||
'type' => 8,
|
||||
'method' => 'directSpreadCount',
|
||||
'name' => '至少{$num}个直推',
|
||||
'real_name' => '最低直推人数',
|
||||
'max_number' => 0,
|
||||
'min_number' => 1,
|
||||
'unit' => '人',
|
||||
'image' => '/uploads/system/agent_spread.png'
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -299,12 +332,15 @@ class AgentLevelTaskServices extends BaseServices
|
||||
|
||||
/**
|
||||
* 检测某个任务完成情况
|
||||
*
|
||||
* type 1-5: 原 CRMEB 分销任务
|
||||
* type 6: 直推报单单数(HJF 改造)
|
||||
* type 7: 伞下报单业绩含业绩分离(HJF 改造)
|
||||
* type 8: 最低直推人数(HJF 改造)
|
||||
*
|
||||
* @param int $uid
|
||||
* @param int $task_id
|
||||
* @return array|false
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function checkLevelTaskFinish(int $uid, int $task_id, $levelTaskInfo = [])
|
||||
{
|
||||
@@ -356,6 +392,17 @@ class AgentLevelTaskServices extends BaseServices
|
||||
$userNumber = $storeOrderServices->count($where);
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
$userNumber = $this->getDirectQueueOrderCount($uid);
|
||||
break;
|
||||
case 7:
|
||||
$userNumber = $this->getUmbrellaQueueOrderCount($uid);
|
||||
break;
|
||||
case 8:
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$userNumber = $userServices->count(['spread_uid' => $uid]);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -372,6 +419,104 @@ class AgentLevelTaskServices extends BaseServices
|
||||
return [$msg, $userNumber, $isComplete];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定等级的升级任务列表(不分页,供外部服务/控制器调用,避免直接访问 protected $dao)
|
||||
*
|
||||
* @param int $level_id eb_agent_level.id
|
||||
* @return array
|
||||
*/
|
||||
public function getUpgradeTasksForLevel(int $level_id): array
|
||||
{
|
||||
if ($level_id <= 0) {
|
||||
return [];
|
||||
}
|
||||
return $this->dao->getTaskList(['level_id' => $level_id, 'is_del' => 0, 'status' => 1]) ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计直推下级的报单订单数(type=6 任务)
|
||||
*
|
||||
* @param int $uid 用户 ID
|
||||
* @return int
|
||||
*/
|
||||
public function getDirectQueueOrderCount(int $uid): int
|
||||
{
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$directUids = $userServices->getColumn(['spread_uid' => $uid], 'uid');
|
||||
if (empty($directUids)) {
|
||||
return 0;
|
||||
}
|
||||
return (int)Db::name('store_order')
|
||||
->whereIn('uid', $directUids)
|
||||
->where('is_queue_goods', 1)
|
||||
->where('paid', 1)
|
||||
->where('is_del', 0)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计伞下报单业绩(type=7 任务,含业绩分离逻辑)
|
||||
*
|
||||
* 业绩分离:若某直推下级已升级为云店或更高(grade≥2),
|
||||
* 则该下级及其团队的订单不计入本用户的伞下业绩。
|
||||
*
|
||||
* @param int $uid 用户 ID
|
||||
* @param int $maxDepth 递归最大深度
|
||||
* @return int
|
||||
*/
|
||||
public function getUmbrellaQueueOrderCount(int $uid, int $maxDepth = 8): int
|
||||
{
|
||||
return $this->recursiveUmbrellaCount($uid, $maxDepth);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归统计伞下业绩(DFS),云店及以上等级的下级团队业绩被分离
|
||||
*/
|
||||
private function recursiveUmbrellaCount(int $uid, int $remainDepth): int
|
||||
{
|
||||
if ($remainDepth <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$directChildren = Db::name('user')
|
||||
->where('spread_uid', $uid)
|
||||
->field('uid,agent_level')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
if (empty($directChildren)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @var AgentLevelServices $levelServices */
|
||||
$levelServices = app()->make(AgentLevelServices::class);
|
||||
$total = 0;
|
||||
|
||||
foreach ($directChildren as $child) {
|
||||
$childGrade = 0;
|
||||
if (!empty($child['agent_level'])) {
|
||||
$childLevelInfo = $levelServices->getLevelInfo((int)$child['agent_level']);
|
||||
$childGrade = (int)($childLevelInfo['grade'] ?? 0);
|
||||
}
|
||||
|
||||
if ($childGrade >= 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$total += (int)Db::name('store_order')
|
||||
->where('uid', $child['uid'])
|
||||
->where('is_queue_goods', 1)
|
||||
->where('paid', 1)
|
||||
->where('is_del', 0)
|
||||
->count();
|
||||
|
||||
$total += $this->recursiveUmbrellaCount((int)$child['uid'], $remainDepth - 1);
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测等级任务
|
||||
* @param int $id
|
||||
|
||||
@@ -3,24 +3,23 @@ declare(strict_types=1);
|
||||
|
||||
namespace app\services\hjf;
|
||||
|
||||
use app\dao\user\UserDao;
|
||||
use app\services\agent\AgentLevelServices;
|
||||
use app\services\agent\AgentLevelTaskServices;
|
||||
use app\services\BaseServices;
|
||||
use crmeb\services\SystemConfigService;
|
||||
use app\services\user\UserServices;
|
||||
use think\annotation\Inject;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 会员等级升级服务
|
||||
* 会员等级升级服务(改造复用版)
|
||||
*
|
||||
* 升级条件(PRD 3.2.1):
|
||||
* - 普通会员 → 创客:直推3单(hjf_level_direct_require_1,默认3)
|
||||
* - 创客 → 云店:伞下业绩30单 + 至少3个直推(hjf_level_umbrella_require_2,默认30)
|
||||
* - 云店 → 服务商:伞下业绩100单 + 至少3个直推(hjf_level_umbrella_require_3,默认100)
|
||||
* - 服务商 → 分公司:伞下业绩1000单 + 至少3个直推(hjf_level_umbrella_require_4,默认1000)
|
||||
* 基于 CRMEB Pro 的团队分销等级功能进行改造:
|
||||
* - 使用 eb_user.agent_level (FK → eb_agent_level.id) 代替独立的 member_level
|
||||
* - 升级条件通过 eb_agent_level_task 的 type 6/7/8 定义
|
||||
* - 升级逻辑委托给 AgentLevelServices::checkUserLevelFinish()
|
||||
*
|
||||
* 伞下业绩分离:当某直推下级已升级到云店(level≥2)后,
|
||||
* 该下级及其整个团队的业绩不再计入本级的伞下业绩。
|
||||
* 本服务保留为薄封装层,提供 HJF 特有的查询方法供控制器调用。
|
||||
*
|
||||
* Class MemberLevelServices
|
||||
* @package app\services\hjf
|
||||
@@ -28,111 +27,70 @@ use think\facade\Log;
|
||||
class MemberLevelServices extends BaseServices
|
||||
{
|
||||
#[Inject]
|
||||
protected UserDao $userDao;
|
||||
protected AgentLevelServices $agentLevelServices;
|
||||
|
||||
/**
|
||||
* 各等级升级所需直推单数(0→1升级条件)
|
||||
*/
|
||||
const DIRECT_REQUIRE_KEYS = [
|
||||
1 => 'hjf_level_direct_require_1', // 普通→创客:直推N单
|
||||
];
|
||||
|
||||
/**
|
||||
* 各等级升级所需伞下单数(n-1→n升级条件,n≥2)
|
||||
*/
|
||||
const UMBRELLA_REQUIRE_KEYS = [
|
||||
2 => 'hjf_level_umbrella_require_2', // 创客→云店
|
||||
3 => 'hjf_level_umbrella_require_3', // 云店→服务商
|
||||
4 => 'hjf_level_umbrella_require_4', // 服务商→分公司
|
||||
];
|
||||
|
||||
/**
|
||||
* 默认升级门槛
|
||||
*/
|
||||
const DEFAULT_DIRECT_REQUIRE = [1 => 3];
|
||||
const DEFAULT_UMBRELLA_REQUIRE = [2 => 30, 3 => 100, 4 => 1000];
|
||||
|
||||
/**
|
||||
* 最低直推人数要求(云店及以上需要至少3个直推)
|
||||
*/
|
||||
const MIN_DIRECT_SPREAD_COUNT = 3;
|
||||
#[Inject]
|
||||
protected AgentLevelTaskServices $agentLevelTaskServices;
|
||||
|
||||
/**
|
||||
* 检查并执行升级(异步触发入口)
|
||||
*
|
||||
* @param int $uid 被检查的用户 ID
|
||||
* 委托给 CRMEB 的 AgentLevelServices 复用原有升级检测流程,
|
||||
* 该流程已支持 type 6/7/8 的 HJF 任务类型。
|
||||
*/
|
||||
public function checkUpgrade(int $uid): void
|
||||
{
|
||||
try {
|
||||
$user = $this->userDao->get($uid);
|
||||
if (!$user) {
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$userInfo = $userServices->getUserCacheInfo($uid);
|
||||
if (!$userInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
$currentLevel = (int)($user['member_level'] ?? 0);
|
||||
$nextLevel = $currentLevel + 1;
|
||||
|
||||
if ($nextLevel > 4) {
|
||||
return; // 已是最高等级
|
||||
$spreadUid = $userServices->getSpreadUid($uid, $userInfo);
|
||||
$twoSpreadUid = 0;
|
||||
if ($spreadUid > 0 && $oneUserInfo = $userServices->getUserCacheInfo($spreadUid)) {
|
||||
$twoSpreadUid = $userServices->getSpreadUid($spreadUid, $oneUserInfo, false);
|
||||
}
|
||||
$uids = array_unique([$uid, $spreadUid, $twoSpreadUid]);
|
||||
|
||||
$qualified = $this->checkLevelCondition($uid, $currentLevel, $nextLevel);
|
||||
if ($qualified) {
|
||||
$this->upgrade($uid, $nextLevel);
|
||||
|
||||
// 升级后继续检查是否可连续升级
|
||||
$this->checkUpgrade($uid);
|
||||
}
|
||||
$this->agentLevelServices->checkUserLevelFinish($uid, $uids);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error("[MemberLevel] checkUpgrade uid={$uid}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否满足从 currentLevel 升到 nextLevel 的条件
|
||||
* 获取用户当前会员等级 grade(0=普通, 1=创客, 2=云店, 3=服务商, 4=分公司)
|
||||
*/
|
||||
private function checkLevelCondition(int $uid, int $currentLevel, int $nextLevel): bool
|
||||
public function getUserGrade(int $uid): int
|
||||
{
|
||||
if ($nextLevel === 1) {
|
||||
// 普通→创客:统计直推报单数
|
||||
$require = $this->getDirectRequire(1);
|
||||
$count = $this->getDirectQueueOrderCount($uid);
|
||||
return $count >= $require;
|
||||
}
|
||||
|
||||
// 创客/云店/服务商→更高等级:伞下业绩 + 至少3个直推
|
||||
$umbrellaRequire = $this->getUmbrellaRequire($nextLevel);
|
||||
$umbrellaCount = $this->getUmbrellaQueueOrderCount($uid);
|
||||
|
||||
if ($umbrellaCount < $umbrellaRequire) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 需要至少3个直推(对 level≥2 的升级)
|
||||
$directCount = $this->getDirectSpreadCount($uid);
|
||||
return $directCount >= self::MIN_DIRECT_SPREAD_COUNT;
|
||||
$agentLevel = (int)Db::name('user')->where('uid', $uid)->value('agent_level');
|
||||
return $this->agentLevelServices->getGradeByLevelId($agentLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取直推用户的报单订单数(直推层级 = 1 层)
|
||||
*
|
||||
* 报单商品标记:`is_queue_goods = 1`(eb_store_order 中的字段)
|
||||
* 获取用户当前等级名称
|
||||
*/
|
||||
public function getUserLevelName(int $uid): string
|
||||
{
|
||||
$agentLevel = (int)Db::name('user')->where('uid', $uid)->value('agent_level');
|
||||
if ($agentLevel <= 0) {
|
||||
return '普通会员';
|
||||
}
|
||||
$maps = $this->agentLevelServices->loadHjfUserListLevelMaps();
|
||||
$info = $this->agentLevelServices->pickHjfLevelRowForUserListDisplay($agentLevel, $maps);
|
||||
|
||||
return $info['name'] ?? '普通会员';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取直推用户的报单订单数
|
||||
*/
|
||||
public function getDirectQueueOrderCount(int $uid): int
|
||||
{
|
||||
// 查询直推用户 uid 列表
|
||||
$directUids = $this->userDao->getColumn(['spread_uid' => $uid], 'uid');
|
||||
if (empty($directUids)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)Db::name('store_order')
|
||||
->whereIn('uid', $directUids)
|
||||
->where('is_queue_goods', 1)
|
||||
->where('paid', 1)
|
||||
->where('is_del', 0)
|
||||
->count();
|
||||
return $this->agentLevelTaskServices->getDirectQueueOrderCount($uid);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,107 +98,39 @@ class MemberLevelServices extends BaseServices
|
||||
*/
|
||||
public function getDirectSpreadCount(int $uid): int
|
||||
{
|
||||
return (int)$this->userDao->count(['spread_uid' => $uid]);
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
return (int)$userServices->count(['spread_uid' => $uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取伞下总报单订单数(含业绩分离逻辑)
|
||||
*
|
||||
* 业绩分离:若某直推下级已升级为云店(level≥2),
|
||||
* 则该下级及其团队的订单不计入本用户的伞下业绩。
|
||||
*
|
||||
* @param int $uid 统计对象用户 ID
|
||||
* @param int $maxDepth 递归最大深度,防止死循环
|
||||
*/
|
||||
public function getUmbrellaQueueOrderCount(int $uid, int $maxDepth = 8): int
|
||||
public function getUmbrellaQueueOrderCount(int $uid): int
|
||||
{
|
||||
return $this->recursiveUmbrellaCount($uid, $maxDepth);
|
||||
return $this->agentLevelTaskServices->getUmbrellaQueueOrderCount($uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归统计伞下业绩(DFS)
|
||||
*/
|
||||
private function recursiveUmbrellaCount(int $uid, int $remainDepth): int
|
||||
{
|
||||
if ($remainDepth <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$directChildren = $this->userDao->selectList(
|
||||
['spread_uid' => $uid],
|
||||
'uid,member_level',
|
||||
0, 0, 'uid', 'asc'
|
||||
);
|
||||
|
||||
if (empty($directChildren)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$total = 0;
|
||||
foreach ($directChildren as $child) {
|
||||
$childLevel = (int)($child['member_level'] ?? 0);
|
||||
|
||||
// 业绩分离:直推下级已是云店或以上(level≥2),其团队业绩不计入本级
|
||||
if ($childLevel >= 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 统计该下级自身的报单订单数
|
||||
$total += (int)Db::name('store_order')
|
||||
->where('uid', $child['uid'])
|
||||
->where('is_queue_goods', 1)
|
||||
->where('paid', 1)
|
||||
->where('is_del', 0)
|
||||
->count();
|
||||
|
||||
// 递归统计该下级的伞下
|
||||
$total += $this->recursiveUmbrellaCount((int)$child['uid'], $remainDepth - 1);
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行升级
|
||||
* 手动设置会员等级(管理后台使用)
|
||||
*
|
||||
* @param int $uid 用户 ID
|
||||
* @param int $newLevel 新等级
|
||||
* @param int $uid 用户 ID
|
||||
* @param int $grade 目标等级 grade (0-4)
|
||||
*/
|
||||
public function upgrade(int $uid, int $newLevel): void
|
||||
public function setUserLevel(int $uid, int $grade): void
|
||||
{
|
||||
Db::transaction(function () use ($uid, $newLevel) {
|
||||
$this->userDao->update($uid, ['member_level' => $newLevel], 'uid');
|
||||
|
||||
Log::info("[MemberLevel] uid={$uid} 升级到 level={$newLevel}");
|
||||
});
|
||||
|
||||
// 升级后通知推荐链上级重新检查
|
||||
$user = $this->userDao->get($uid);
|
||||
if ($user && $user['spread_uid']) {
|
||||
// 异步检查上级升级(防止递归过深直接调用)
|
||||
try {
|
||||
app(\app\jobs\hjf\MemberLevelCheckJob::class)::dispatch($user['spread_uid']);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning("[MemberLevel] 无法派发上级检查 Job: " . $e->getMessage());
|
||||
$agentLevelId = 0;
|
||||
if ($grade > 0) {
|
||||
$agentLevelId = $this->agentLevelServices->getLevelIdByGrade($grade);
|
||||
if ($agentLevelId <= 0) {
|
||||
throw new \think\exception\ValidateException("等级 grade={$grade} 在 eb_agent_level 中不存在");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getDirectRequire(int $level): int
|
||||
{
|
||||
$key = self::DIRECT_REQUIRE_KEYS[$level] ?? '';
|
||||
if (!$key) {
|
||||
return self::DEFAULT_DIRECT_REQUIRE[$level] ?? 3;
|
||||
}
|
||||
return (int)SystemConfigService::get($key, self::DEFAULT_DIRECT_REQUIRE[$level] ?? 3);
|
||||
}
|
||||
/** @var UserServices $userServices */
|
||||
$userServices = app()->make(UserServices::class);
|
||||
$userServices->update($uid, ['agent_level' => $agentLevelId]);
|
||||
|
||||
private function getUmbrellaRequire(int $level): int
|
||||
{
|
||||
$key = self::UMBRELLA_REQUIRE_KEYS[$level] ?? '';
|
||||
if (!$key) {
|
||||
return self::DEFAULT_UMBRELLA_REQUIRE[$level] ?? 9999;
|
||||
}
|
||||
return (int)SystemConfigService::get($key, self::DEFAULT_UMBRELLA_REQUIRE[$level] ?? 9999);
|
||||
Log::info("[MemberLevel] 手动设置 uid={$uid} agent_level={$agentLevelId} (grade={$grade})");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,22 +5,19 @@ namespace app\services\hjf;
|
||||
|
||||
use app\dao\hjf\PointsReleaseLogDao;
|
||||
use app\dao\user\UserDao;
|
||||
use app\services\agent\AgentLevelServices;
|
||||
use app\services\BaseServices;
|
||||
use crmeb\services\SystemConfigService;
|
||||
use think\annotation\Inject;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 积分奖励服务(级差计算)
|
||||
* 积分奖励服务(级差计算)—— 改造复用版
|
||||
*
|
||||
* 触发时机:报单商品订单支付回调成功后调用 reward($orderUid, $orderId)。
|
||||
*
|
||||
* 奖励规则(PRD 3.2):
|
||||
* - 推荐人(直推上级)获得 直推奖励积分(按推荐人等级)
|
||||
* - 更上级获得 级差积分(上级积分 - 直接下级已获得的积分)
|
||||
* - 所有奖励积分写入 frozen_points(待释放状态)
|
||||
* - 同时写 points_release_log 记录明细
|
||||
* 改造要点(PRD 3.2.2):
|
||||
* - 使用 eb_user.agent_level (FK → eb_agent_level.id) 获取会员等级
|
||||
* - 从 eb_agent_level 表的 direct_reward_points / umbrella_reward_points 字段读取奖励积分
|
||||
* - 不再使用独立的 member_level 字段和系统配置表中的 hjf_reward_* 键
|
||||
*
|
||||
* Class PointsRewardServices
|
||||
* @package app\services\hjf
|
||||
@@ -33,50 +30,19 @@ class PointsRewardServices extends BaseServices
|
||||
#[Inject]
|
||||
protected UserDao $userDao;
|
||||
|
||||
/**
|
||||
* 各等级直推奖励积分配置键
|
||||
*/
|
||||
const DIRECT_REWARD_KEYS = [
|
||||
0 => 0, // 普通会员:无直推奖励
|
||||
1 => 'hjf_reward_direct_1', // 创客
|
||||
2 => 'hjf_reward_direct_2', // 云店
|
||||
3 => 'hjf_reward_direct_3', // 服务商
|
||||
4 => 'hjf_reward_direct_4', // 分公司
|
||||
];
|
||||
|
||||
/**
|
||||
* 各等级伞下奖励积分配置键
|
||||
*/
|
||||
const UMBRELLA_REWARD_KEYS = [
|
||||
0 => 0,
|
||||
1 => 'hjf_reward_umbrella_1',
|
||||
2 => 'hjf_reward_umbrella_2',
|
||||
3 => 'hjf_reward_umbrella_3',
|
||||
4 => 'hjf_reward_umbrella_4',
|
||||
];
|
||||
|
||||
/**
|
||||
* 默认积分奖励(当系统配置未初始化时使用)
|
||||
*/
|
||||
const DEFAULT_DIRECT = [0 => 0, 1 => 500, 2 => 800, 3 => 1000, 4 => 1300];
|
||||
const DEFAULT_UMBRELLA = [0 => 0, 1 => 0, 2 => 300, 3 => 200, 4 => 300];
|
||||
#[Inject]
|
||||
protected AgentLevelServices $agentLevelServices;
|
||||
|
||||
/**
|
||||
* 对一笔报单订单发放积分奖励
|
||||
*
|
||||
* @param int $orderUid 下单用户 ID
|
||||
* @param string $orderId 订单号
|
||||
*/
|
||||
public function reward(int $orderUid, string $orderId): void
|
||||
{
|
||||
try {
|
||||
// 获取下单用户信息
|
||||
$buyer = $this->userDao->get($orderUid);
|
||||
if (!$buyer || !$buyer['spread_uid']) {
|
||||
return; // 无推荐人,不发奖励
|
||||
return;
|
||||
}
|
||||
|
||||
// 沿推荐链向上遍历,计算级差奖励
|
||||
$this->propagateReward($buyer['spread_uid'], $orderUid, $orderId, 0);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error("[PointsReward] 积分奖励失败 orderUid={$orderUid} orderId={$orderId}: " . $e->getMessage());
|
||||
@@ -86,11 +52,11 @@ class PointsRewardServices extends BaseServices
|
||||
/**
|
||||
* 向上递归发放级差积分
|
||||
*
|
||||
* @param int $uid 当前被奖励用户
|
||||
* @param int $fromUid 触发方(下级)用户 ID
|
||||
* @param string $orderId 来源订单号
|
||||
* @param int $lowerReward 下级已获得的直推/伞下奖励积分(用于级差扣减)
|
||||
* @param int $depth 递归深度(最多遍历10层)
|
||||
* @param int $uid 当前被奖励用户
|
||||
* @param int $fromUid 触发方(下级)用户 ID
|
||||
* @param string $orderId 来源订单号
|
||||
* @param int $lowerReward 下级已获得的直推/伞下奖励积分(用于级差扣减)
|
||||
* @param int $depth 递归深度
|
||||
*/
|
||||
private function propagateReward(
|
||||
int $uid,
|
||||
@@ -108,22 +74,21 @@ class PointsRewardServices extends BaseServices
|
||||
return;
|
||||
}
|
||||
|
||||
$level = (int)($user['member_level'] ?? 0);
|
||||
if ($level === 0) {
|
||||
// 普通会员不获得奖励,但继续向上传递
|
||||
$agentLevelId = (int)($user['agent_level'] ?? 0);
|
||||
$grade = $this->agentLevelServices->getGradeByLevelId($agentLevelId);
|
||||
|
||||
if ($grade === 0) {
|
||||
if ($user['spread_uid']) {
|
||||
$this->propagateReward((int)$user['spread_uid'], $uid, $orderId, 0, $depth + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 判断是直推还是伞下(depth=0 说明是第一个上级,即直推)
|
||||
$isDirect = ($depth === 0);
|
||||
$reward = $isDirect
|
||||
? $this->getDirectReward($level)
|
||||
: $this->getUmbrellaReward($level);
|
||||
$reward = $isDirect
|
||||
? $this->agentLevelServices->getDirectRewardPoints($agentLevelId)
|
||||
: $this->agentLevelServices->getUmbrellaRewardPoints($agentLevelId);
|
||||
|
||||
// 级差:本次实发 = 本等级应得 - 下级已获得
|
||||
$actual = max(0, $reward - $lowerReward);
|
||||
|
||||
if ($actual > 0) {
|
||||
@@ -136,13 +101,12 @@ class PointsRewardServices extends BaseServices
|
||||
);
|
||||
}
|
||||
|
||||
// 继续向上传递(使用本级应得的 reward 作为下一级的 lowerReward)
|
||||
if ($user['spread_uid']) {
|
||||
$this->propagateReward(
|
||||
(int)$user['spread_uid'],
|
||||
$uid,
|
||||
$orderId,
|
||||
$reward, // 传递本级"应得"(而非实发)给上级做级差
|
||||
$reward,
|
||||
$depth + 1
|
||||
);
|
||||
}
|
||||
@@ -154,10 +118,8 @@ class PointsRewardServices extends BaseServices
|
||||
private function grantFrozenPoints(int $uid, int $points, string $orderId, string $type, string $mark): void
|
||||
{
|
||||
Db::transaction(function () use ($uid, $points, $orderId, $type, $mark) {
|
||||
// 增加 frozen_points
|
||||
$this->userDao->bcInc($uid, 'frozen_points', $points, 'uid');
|
||||
|
||||
// 写明细日志
|
||||
$this->logDao->save([
|
||||
'uid' => $uid,
|
||||
'points' => $points,
|
||||
@@ -170,22 +132,4 @@ class PointsRewardServices extends BaseServices
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
private function getDirectReward(int $level): int
|
||||
{
|
||||
$key = self::DIRECT_REWARD_KEYS[$level] ?? 0;
|
||||
if (!$key) {
|
||||
return self::DEFAULT_DIRECT[$level] ?? 0;
|
||||
}
|
||||
return (int)SystemConfigService::get($key, self::DEFAULT_DIRECT[$level] ?? 0);
|
||||
}
|
||||
|
||||
private function getUmbrellaReward(int $level): int
|
||||
{
|
||||
$key = self::UMBRELLA_REWARD_KEYS[$level] ?? 0;
|
||||
if (!$key) {
|
||||
return self::DEFAULT_UMBRELLA[$level] ?? 0;
|
||||
}
|
||||
return (int)SystemConfigService::get($key, self::DEFAULT_UMBRELLA[$level] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -766,23 +766,31 @@ class UserServices extends BaseServices
|
||||
*/
|
||||
public function index(array $where)
|
||||
{
|
||||
// 添加过滤条件
|
||||
$where['is_filter_del'] = 1;
|
||||
|
||||
/** @var UserWechatuserServices $userWechatUser */
|
||||
$userWechatUser = app()->make(UserWechatuserServices::class);
|
||||
$fields = 'u.*,w.country,w.province,w.city,w.sex,w.unionid,w.openid,w.user_type as w_user_type,w.groupid,w.tagid_list,w.subscribe,w.subscribe_time';
|
||||
|
||||
try {
|
||||
// 添加过滤条件
|
||||
$where['is_filter_del'] = 1;
|
||||
|
||||
/** @var UserWechatuserServices $userWechatUser */
|
||||
$userWechatUser = app()->make(UserWechatuserServices::class);
|
||||
$fields = 'u.*,w.country,w.province,w.city,w.sex,w.unionid,w.openid,w.user_type as w_user_type,w.groupid,w.tagid_list,w.subscribe,w.subscribe_time';
|
||||
|
||||
// 获取用户列表
|
||||
[$list, $count] = $userWechatUser->getWhereUserList($where, $fields);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('User index list query failed: ' . $e->getMessage());
|
||||
|
||||
if ($list) {
|
||||
// 提取唯一 UID 列表
|
||||
$uids = array_unique(array_column($list, 'uid'));
|
||||
return ['count' => 0, 'list' => []];
|
||||
}
|
||||
|
||||
// 获取相关服务数据
|
||||
$userlabel = $this->getUserLablel($uids);
|
||||
if (!$list) {
|
||||
return compact('count', 'list');
|
||||
}
|
||||
|
||||
try {
|
||||
// 提取唯一 UID 列表
|
||||
$uids = array_unique(array_column($list, 'uid'));
|
||||
|
||||
// 获取相关服务数据
|
||||
$userlabel = $this->getUserLablel($uids);
|
||||
$groupIds = array_unique(array_column($list, 'group_id'));
|
||||
$userGroupService = app()->make(UserGroupServices::class);
|
||||
$userGroup = $userGroupService->getUsersGroupName($groupIds);
|
||||
@@ -799,6 +807,11 @@ class UserServices extends BaseServices
|
||||
$clientData = $workClientService->getList(['uid' => $uids], ['id', 'uid', 'name', 'external_userid', 'corp_id', 'unionid'], false);
|
||||
$clientlist = $clientData['list'] ?? [];
|
||||
|
||||
/** HJF:分销等级展示索引(is_del=0,按 id/grade 双索引,优先 HJF 官方等级名称) */
|
||||
/** @var AgentLevelServices $agentLevelServices */
|
||||
$agentLevelServices = app()->make(AgentLevelServices::class);
|
||||
$hjfLevelMaps = $agentLevelServices->loadHjfUserListLevelMaps();
|
||||
|
||||
// 补充信息
|
||||
$extendInfo = SystemConfigService::get('user_extend_info', []);
|
||||
$is_extend_info = false;
|
||||
@@ -826,6 +839,13 @@ class UserServices extends BaseServices
|
||||
|
||||
// 补充每个用户的详细信息
|
||||
foreach ($list as &$item) {
|
||||
$agentLevelId = (int)($item['agent_level'] ?? 0);
|
||||
$hjfLevelInfo = $agentLevelServices->pickHjfLevelRowForUserListDisplay($agentLevelId, $hjfLevelMaps);
|
||||
$item['member_level'] = $hjfLevelInfo ? (int)$hjfLevelInfo['grade'] : 0;
|
||||
$item['member_level_name'] = $hjfLevelInfo ? ($hjfLevelInfo['name'] ?? '') : '普通会员';
|
||||
$item['available_points'] = (int)($item['available_points'] ?? 0);
|
||||
$item['frozen_points'] = (int)($item['frozen_points'] ?? 0);
|
||||
|
||||
// 地址补充
|
||||
if (empty($item['addres'])) {
|
||||
if (!empty($item['country']) || !empty($item['province']) || !empty($item['city'])) {
|
||||
@@ -895,13 +915,13 @@ class UserServices extends BaseServices
|
||||
// 扩展信息标志
|
||||
$item['is_extend_info'] = $is_extend_info;
|
||||
}
|
||||
}
|
||||
|
||||
return compact('count', 'list');
|
||||
} catch (\Exception $e) {
|
||||
// 异常处理
|
||||
Log::error('Error in user index: ' . $e->getMessage());
|
||||
return ['count' => 0, 'list' => []];
|
||||
} catch (\Throwable $e) {
|
||||
// 加工阶段失败时仍返回查询结果,避免整页空白(常见:企微/等级等扩展服务异常)
|
||||
Log::error('User index enrichment failed: ' . $e->getMessage());
|
||||
|
||||
return compact('count', 'list');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ declare (strict_types=1);
|
||||
|
||||
namespace app\services\user;
|
||||
|
||||
use app\services\agent\AgentLevelServices;
|
||||
use app\services\BaseServices;
|
||||
use app\dao\user\UserWechatUserDao;
|
||||
use think\annotation\Inject;
|
||||
@@ -48,6 +49,7 @@ class UserWechatuserServices extends BaseServices
|
||||
*/
|
||||
public function getWhereUserList(array $where, string $field): array
|
||||
{
|
||||
$where = $this->normalizeHjfMemberLevelWhere($where);
|
||||
[$page, $limit] = $this->getPageValue();
|
||||
$order_string = '';
|
||||
$order_arr = ['asc', 'desc'];
|
||||
@@ -58,4 +60,40 @@ class UserWechatuserServices extends BaseServices
|
||||
$count = $this->dao->getCountByWhere($where);
|
||||
return [$list, $count];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将会员列表筛选「HJF 等级(grade)」转为 eb_user.agent_level 条件,供 UserWechatUserDao 使用。
|
||||
*/
|
||||
protected function normalizeHjfMemberLevelWhere(array $where): array
|
||||
{
|
||||
if (!array_key_exists('hjf_member_level', $where)) {
|
||||
return $where;
|
||||
}
|
||||
$raw = $where['hjf_member_level'];
|
||||
if ($raw === null) {
|
||||
unset($where['hjf_member_level']);
|
||||
|
||||
return $where;
|
||||
}
|
||||
if (is_string($raw)) {
|
||||
$raw = trim($raw);
|
||||
}
|
||||
// 空串/仅空白:不按分销等级筛选(避免 (int)' '=>0 误加 agent_level=0)
|
||||
if ($raw === '') {
|
||||
unset($where['hjf_member_level']);
|
||||
|
||||
return $where;
|
||||
}
|
||||
$grade = (int)$raw;
|
||||
/** @var AgentLevelServices $agentLevel */
|
||||
$agentLevel = app()->make(AgentLevelServices::class);
|
||||
if ($grade === 0) {
|
||||
$where['hjf_agent_level_id'] = 0;
|
||||
} else {
|
||||
$where['hjf_agent_level_id'] = $agentLevel->getLevelIdByGrade($grade) ?: -1;
|
||||
}
|
||||
unset($where['hjf_member_level']);
|
||||
|
||||
return $where;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user