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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user