WIN-2308041133\Administrator 5 maanden geleden
bovenliggende
commit
935d03f68f

+ 315 - 0
app/admin/controller/user/UserAgentLevel.php

@@ -0,0 +1,315 @@
+<?php
+
+namespace app\admin\controller\agent;
+
+use app\admin\controller\AuthController;
+use think\facade\Route as Url;
+use crmeb\traits\CurdControllerTrait;
+use app\admin\model\system\SystemUserAgentLevel; // 代理等级配置模型
+use app\admin\model\user\UserAgentLevel; // 用户代理等级记录模型
+use crmeb\services\{UtilService, JsonService, FormBuilder as Form};
+
+/**
+ * 代理等级管理
+ * Class AgentLevel
+ * @package app\admin\controller\agent
+ */
+class AgentLevel extends AuthController
+{
+    use CurdControllerTrait;
+
+    /**
+     * 代理等级列表页面
+     * @return \think\response\View
+     */
+    public function index()
+    {
+        return $this->fetch();
+    }
+
+    /**
+     * 创建/编辑代理等级表单
+     * @param int $id 代理等级ID(编辑时传递)
+     * @return \think\response\View
+     */
+    public function create($id = 0)
+    {
+        $agentLevel = $id ? SystemUserAgentLevel::get($id) : null;
+        if ($id && !$agentLevel) {
+            return JsonService::fail('代理等级不存在');
+        }
+
+        $field = [];
+        // 等级名称
+        $field[] = Form::input('name', '代理等级名称', $agentLevel ? $agentLevel->name : '')
+            ->col(Form::col(24))
+            ->required('请输入代理等级名称');
+        // 直推返利比例
+        $field[] = Form::number('direct_rebates', '直推返利比例(%)', $agentLevel ? $agentLevel->direct_rebates : 0)
+            ->min(0)->max(100)
+            ->col(Form::col(8))
+            ->required('请输入直推返利比例');
+        // 间接返利比例
+        $field[] = Form::number('indirect_rebates', '间接返利比例(%)', $agentLevel ? $agentLevel->indirect_rebates : 0)
+            ->min(0)->max(100)
+            ->col(Form::col(8))
+            ->required('请输入间接返利比例');
+        // 伞下返利比例
+        $field[] = Form::number('umbrella_rebates', '伞下返利比例(%)', $agentLevel ? $agentLevel->umbrella_rebates : 0)
+            ->min(0)->max(100)
+            ->col(Form::col(8))
+            ->required('请输入伞下返利比例');
+        // 代理进货折扣
+        $field[] = Form::number('discount', '代理进货折扣(%)', $agentLevel ? $agentLevel->discount : 0)
+            ->min(0)->max(100)
+            ->col(Form::col(8))
+            ->required('请输入代理进货折扣');
+        // 是否永久有效
+        $field[] = Form::radio('is_forever', '是否永久有效', $agentLevel ? $agentLevel->is_forever : 0)
+            ->options([
+                ['label' => '永久', 'value' => 1],
+                ['label' => '非永久', 'value' => 0]
+            ])
+            ->col(Form::col(24));
+        // 有效天数(非永久时必填)
+        $field[] = Form::number('valid_days', '有效天数', $agentLevel ? $agentLevel->valid_days : 0)
+            ->min(0)
+            ->col(Form::col(8))
+            ->when($agentLevel && $agentLevel->is_forever == 0, function ($form) {
+                return $form->required('非永久有效时请输入有效天数');
+            })
+            ->when(!$agentLevel, function ($form) {
+                return $form->requiredIf('is_forever', 0, '非永久有效时请输入有效天数');
+            });
+        // 等级排序
+        $field[] = Form::number('grade', '等级排序', $agentLevel ? $agentLevel->grade : 0)
+            ->min(0)
+            ->col(Form::col(8))
+            ->required('请输入等级排序');
+        // 是否显示
+        $field[] = Form::radio('is_show', '是否显示', $agentLevel ? $agentLevel->is_show : 1)
+            ->options([
+                ['label' => '显示', 'value' => 1],
+                ['label' => '隐藏', 'value' => 0]
+            ])
+            ->col(Form::col(8));
+        // 返利规则说明
+        $field[] = Form::textarea('rule_explain', '返利规则说明', $agentLevel ? $agentLevel->rule_explain : '')
+            ->col(Form::col(24))
+            ->required('请输入返利规则说明');
+        // 等级图标
+        $field[] = Form::frameImageOne('icon', '等级图标', Url::buildUrl('admin/widget.images/index', ['fodder' => 'icon']), $agentLevel ? $agentLevel->icon : '')
+            ->icon('image')
+            ->width('100%')
+            ->height('500px')
+            ->required('请上传等级图标');
+
+        $formTitle = $id ? '编辑代理等级' : '添加代理等级';
+        $formUrl = Url::buildUrl('save', ['id' => $id]);
+        $form = Form::make_post_form($formTitle, $field, $formUrl, 2);
+
+        $this->assign(compact('form'));
+        return $this->fetch('public/form-builder');
+    }
+
+    /**
+     * 保存/编辑代理等级
+     * @param int $id 代理等级ID(编辑时传递)
+     * @return \crmeb\services\JsonService
+     */
+    public function save($id = 0)
+    {
+        $data = UtilService::postMore([
+            ['name', ''],
+            ['direct_rebates', 0],
+            ['indirect_rebates', 0],
+            ['umbrella_rebates', 0],
+            ['discount', 0],
+            ['is_forever', 0],
+            ['valid_days', 0],
+            ['grade', 0],
+            ['is_show', 0],
+            ['rule_explain', ''],
+            ['icon', ''],
+        ]);
+
+        // 参数验证
+        if (empty($data['name'])) return JsonService::fail('请输入代理等级名称');
+        if ($data['direct_rebates'] < 0 || $data['direct_rebates'] > 100) return JsonService::fail('直推返利比例请输入0-100之间的数值');
+        if ($data['indirect_rebates'] < 0 || $data['indirect_rebates'] > 100) return JsonService::fail('间接返利比例请输入0-100之间的数值');
+        if ($data['umbrella_rebates'] < 0 || $data['umbrella_rebates'] > 100) return JsonService::fail('伞下返利比例请输入0-100之间的数值');
+        if ($data['discount'] < 0 || $data['discount'] > 100) return JsonService::fail('代理进货折扣请输入0-100之间的数值');
+        if (empty($data['grade'])) return JsonService::fail('请输入等级排序');
+        if (empty($data['rule_explain'])) return JsonService::fail('请输入返利规则说明');
+        if (empty($data['icon'])) return JsonService::fail('请上传等级图标');
+        if ($data['is_forever'] == 0 && empty($data['valid_days'])) return JsonService::fail('非永久有效时,请输入有效天数');
+
+        // 等级排序唯一性校验
+        $exists = SystemUserAgentLevel::where('is_del', 0)
+            ->where('grade', $data['grade'])
+            ->when($id, function ($query) use ($id) {
+                $query->where('id', '<>', $id);
+            })
+            ->find();
+        if ($exists) return JsonService::fail('该等级排序已存在,请更换');
+
+        SystemUserAgentLevel::startTrans();
+        try {
+            if ($id) {
+                // 编辑操作
+                $result = SystemUserAgentLevel::update($data, ['id' => $id]);
+            } else {
+                // 新增操作
+                $data['add_time'] = time();
+                $result = SystemUserAgentLevel::create($data);
+            }
+
+            if ($result) {
+                SystemUserAgentLevel::commit();
+                return JsonService::successful($id ? '修改成功' : '添加成功');
+            } else {
+                SystemUserAgentLevel::rollback();
+                return JsonService::fail($id ? '修改失败' : '添加失败');
+            }
+        } catch (\Exception $e) {
+            SystemUserAgentLevel::rollback();
+            return JsonService::fail($e->getMessage());
+        }
+    }
+
+    /**
+     * 获取代理等级列表(分页+搜索)
+     * @return \crmeb\services\JsonService
+     */
+    public function get_system_agent_list()
+    {
+        $where = UtilService::getMore([
+            ['page', 1],
+            ['limit', 10],
+            ['name', ''],
+            ['is_show', ''],
+        ]);
+
+        $query = SystemUserAgentLevel::where('is_del', 0);
+        // 名称搜索
+        if ($where['name']) $query->where('name', 'like', "%{$where['name']}%");
+        // 显示状态筛选
+        if ($where['is_show'] !== '') $query->where('is_show', $where['is_show']);
+
+        $list = $query->order('grade asc')
+            ->paginate([
+                'page' => $where['page'],
+                'list_rows' => $where['limit'],
+            ]);
+
+        return JsonService::successlayui([
+            'count' => $list->total(),
+            'data' => $list->items(),
+        ]);
+    }
+
+    /**
+     * 删除代理等级(软删除)
+     * @param int $id 代理等级ID
+     * @return \crmeb\services\JsonService
+     */
+    public function delete($id = 0)
+    {
+        if (empty($id)) return JsonService::fail('缺少参数ID');
+
+        if (SystemUserAgentLevel::update(['is_del' => 1], ['id' => $id])) {
+            return JsonService::successful('删除成功');
+        } else {
+            return JsonService::fail('删除失败');
+        }
+    }
+
+    /**
+     * 设置代理等级显示状态
+     * @param int $is_show 显示状态(1=显示,0=隐藏)
+     * @param int $id 代理等级ID
+     * @return \crmeb\services\JsonService
+     */
+    public function set_show($is_show = '', $id = '')
+    {
+        if (empty($is_show) || empty($id)) return JsonService::fail('缺少参数');
+
+        $result = SystemUserAgentLevel::where('id', $id)->update(['is_show' => (int)$is_show]);
+        return $result ?
+            JsonService::successful($is_show == 1 ? '显示成功' : '隐藏成功') :
+            JsonService::fail($is_show == 1 ? '显示失败' : '隐藏失败');
+    }
+
+    /**
+     * 快速编辑代理等级字段
+     * @param string $field 字段名
+     * @param int $id 代理等级ID
+     * @param mixed $value 字段值
+     * @return \crmeb\services\JsonService
+     */
+    public function set_value($field = '', $id = '', $value = '')
+    {
+        if (empty($field) || empty($id) || $value === '') return JsonService::fail('缺少参数');
+
+        // 允许编辑的字段白名单
+        $allowFields = ['grade', 'direct_rebates', 'indirect_rebates', 'umbrella_rebates', 'discount', 'valid_days'];
+        if (!in_array($field, $allowFields)) return JsonService::fail('不允许修改的字段');
+
+        if (SystemUserAgentLevel::where('id', $id)->update([$field => $value])) {
+            return JsonService::successful('保存成功');
+        } else {
+            return JsonService::fail('保存失败');
+        }
+    }
+
+    /**
+     * 用户代理等级列表页面
+     * @return \think\response\View
+     */
+    public function user_agent_level_list()
+    {
+        $agentLevels = SystemUserAgentLevel::where('is_del', 0)
+            ->where('is_show', 1)
+            ->order('grade asc')
+            ->field('id, name')
+            ->select();
+        $this->assign('agentLevels', $agentLevels);
+        return $this->fetch();
+    }
+
+    /**
+     * 获取用户代理等级列表(分页+关联用户信息)
+     * @return \crmeb\services\JsonService
+     */
+    public function get_user_agent_list()
+    {
+        $where = UtilService::getMore([
+            ['page', 1],
+            ['limit', 10],
+            ['nickname', ''],
+            ['level_id', ''],
+        ]);
+
+        $list = SystemUserAgentLevel::alias('ual')
+            ->join('eb_user u', 'ual.uid = u.uid')
+            ->join('eb_agent_level al', 'ual.level_id = al.id')
+            ->where('ual.is_del', 0)
+            ->when($where['nickname'], function ($query) use ($where) {
+                $query->where('u.nickname', 'like', "%{$where['nickname']}%");
+            })
+            ->when($where['level_id'], function ($query) use ($where) {
+                $query->where('ual.level_id', $where['level_id']);
+            })
+            ->field('ual.*, u.nickname, al.name as level_name')
+            ->order('ual.add_time desc')
+            ->paginate([
+                'page' => $where['page'],
+                'list_rows' => $where['limit'],
+            ]);
+
+        return JsonService::successlayui([
+            'count' => $list->total(),
+            'data' => $list->items(),
+        ]);
+    }
+}

+ 121 - 0
app/admin/model/system/SystemUserAgentLevel.php

@@ -0,0 +1,121 @@
+<?php
+/**
+ * 代理等级设置模型(对应表:system_agent_level)
+ * 作用:管理代理等级的基础配置(名称、返利比例、有效期等)
+ * @author: yourname
+ * @day: 2025/10
+ */
+
+namespace app\admin\model\system;
+
+use crmeb\traits\ModelTrait;
+use crmeb\basic\BaseModel;
+
+class SystemUserAgentLevel extends BaseModel
+{
+    /**
+     * 数据表主键
+     * @var string
+     */
+    protected $pk = 'id';
+
+    /**
+     * 模型关联的数据表名
+     * @var string
+     */
+    protected $name = 'system_agent_level';
+
+    use ModelTrait;
+
+    /**
+     * add_time 字段修改器:新增时自动填充当前时间戳
+     * @return int
+     */
+    public static function setAddTimeAttr()
+    {
+        return time();
+    }
+
+    /**
+     * add_time 字段获取器:时间戳转格式化时间
+     * @param int $value 时间戳
+     * @return string 格式化时间(Y-m-d H:i:s)
+     */
+    public static function getAddTimeAttr($value)
+    {
+        return $value ? date('Y-m-d H:i:s', $value) : '';
+    }
+
+    /**
+     * 构建代理等级查询条件(过滤删除、支持显示状态/名称搜索)
+     * @param array $where 查询条件(如is_show、name等)
+     * @param string $alias 表别名(联查时用)
+     * @param SystemAgentLevel|null $model 模型实例(支持链式调用)
+     * @return SystemAgentLevel
+     */
+    public static function setWhere($where, $alias = '', $model = null)
+    {
+        $model = $model === null ? new self() : $model;
+        // 处理表别名
+        if ($alias) {
+            $model = $model->alias($alias);
+            $alias .= '.';
+        }
+        // 基础条件:未删除
+        $model = $model->where("{$alias}is_del", 0);
+        // 筛选:显示状态(1=显示,0=隐藏)
+        if (isset($where['is_show']) && $where['is_show'] !== '') {
+            $model = $model->where("{$alias}is_show", $where['is_show']);
+        }
+        // 搜索:代理等级名称(模糊匹配)
+        if (isset($where['name']) && $where['name']) {
+            $model = $model->where("{$alias}name", 'LIKE', "%{$where['name']}%");
+        }
+        return $model;
+    }
+
+    /**
+     * 获取代理等级列表(带分页、排序)
+     * @param array $where 分页及筛选条件(page、limit、is_show、name等)
+     * @return array 格式:['data' => 列表数据, 'count' => 总数]
+     */
+    public static function getSystemAgentList($where)
+    {
+        // 分页查询:按等级排序(grade越大等级越高)
+        $data = self::setWhere($where)
+            ->order('grade desc')
+            ->page((int)$where['page'], (int)$where['limit'])
+            ->select();
+        $data = $data ? $data->toArray() : [];
+
+        // 补充格式化字段(如有效期文本)
+        foreach ($data as &$item) {
+            $item['is_forever_text'] = $item['is_forever'] ? '永久有效' : '限时有效';
+            $item['valid_days_text'] = $item['is_forever'] ? '—' : "{$item['valid_days']}天";
+        }
+
+        // 统计符合条件的总数
+        $count = self::setWhere($where)->count();
+
+        return compact('data', 'count');
+    }
+
+    /**
+     * 根据等级ID获取单个代理等级配置详情
+     * @param int $id 代理等级ID
+     * @return array|null 等级详情(含格式化字段)
+     */
+    public static function getAgentLevelConfig($id)
+    {
+        $level = self::setWhere([])->where('id', $id)->find();
+        if (!$level) return null;
+
+        $level = $level->toArray();
+        // 格式化显示字段
+        $level['is_forever_text'] = $level['is_forever'] ? '永久有效' : '限时有效';
+        $level['valid_days_text'] = $level['is_forever'] ? '—' : "{$level['valid_days']}天";
+        $level['rebates_text'] = "直推:{$level['direct_rebates']}% | 间接:{$level['indirect_rebates']}% | 伞下:{$level['umbrella_rebates']}%";
+
+        return $level;
+    }
+}

+ 124 - 0
app/admin/model/user/UserAgentLevel.php

@@ -0,0 +1,124 @@
+<?php
+
+namespace app\admin\model\user;
+
+use app\admin\model\system\SystemUserAgentLevel;
+use crmeb\traits\ModelTrait;
+use crmeb\basic\BaseModel;
+use app\admin\model\user\User; // 关联用户表模型
+
+/**
+ * 用户代理等级 Model
+ * 负责管理用户与代理等级的关联数据查询、统计等逻辑
+ * Class UserAgentLevel
+ * @package app\admin\model\agent
+ */
+class UserAgentLevel extends BaseModel
+{
+    /**
+     * 数据表主键
+     * @var string
+     */
+    protected $pk = 'id';
+
+    /**
+     * 模型关联的数据表名
+     * @var string
+     */
+    protected $name = 'user_agent_level';
+
+    use ModelTrait;
+
+    /**
+     * 构建代理等级查询条件
+     * @param array $where 查询条件(如昵称、等级ID等)
+     * @param string $alias 当前表别名
+     * @param string $userAlias 用户表别名前缀
+     * @param BaseModel|null $model 模型实例(支持链式调用)
+     * @return BaseModel
+     */
+    public static function setWhere($where, $alias = '', $userAlias = 'u.', $model = null)
+    {
+        $model = is_null($model) ? new self() : $model;
+        if ($alias) {
+            $model = $model->alias($alias);
+            $alias .= '.';
+        }
+        // 昵称模糊查询
+        if (isset($where['nickname']) && $where['nickname'] !== '') {
+            $model = $model->where("{$userAlias}nickname", 'like', "%{$where['nickname']}%");
+        }
+        // 代理等级ID精确查询
+        if (isset($where['level_id']) && $where['level_id'] !== '') {
+            $model = $model->where("{$alias}level_id", $where['level_id']);
+        }
+        // 过滤:状态正常 + 未删除
+        return $model->where("{$alias}status", 1)->where("{$alias}is_del", 0);
+    }
+
+    /**
+     * 查询用户代理等级列表(带分页、关联信息)
+     * @param array $where 分页及筛选条件(page、limit、nickname、level_id等)
+     * @return array 格式:['data' => 列表数据, 'count' => 总数]
+     */
+    public static function getUserAgentList($where)
+    {
+        // 关联查询:用户表 + 代理等级配置表
+        $query = self::setWhere($where, 'a')
+            ->group('a.uid')
+            ->order('a.grade desc')
+            ->field('a.*, u.nickname, u.avatar')
+            ->join('user u', 'a.uid = u.uid');
+
+        // 分页查询数据
+        $data = $query->page((int)$where['page'], (int)$where['limit'])->select();
+        $data = $data ? $data->toArray() : [];
+
+        // 补充代理等级名称、图标 + 格式化有效期显示
+        foreach ($data as &$item) {
+            $levelInfo = SystemUserAgentLevel::where('id', $item['level_id'])->find();
+            if ($levelInfo) {
+                $item['level_name'] = $levelInfo['name'];
+                $item['level_icon'] = $levelInfo['icon'];
+            }
+            $item['is_forever_text'] = $item['is_forever'] ? '永久代理' : '限时代理';
+            $item['valid_time_text'] = $item['is_forever'] ? '永久' : (date('Y-m-d H:i:s', $item['valid_time']) ?: '');
+        }
+
+        // 统计符合条件的总数
+        $count = $query->count();
+
+        return compact('data', 'count');
+    }
+
+    /**
+     * 清除用户代理等级(软删除 + 更新用户表标识)
+     * @param int $uid 用户ID
+     * @return bool|string 成功返回true,失败返回错误信息
+     */
+    public static function cleanUpAgentLevel($uid)
+    {
+        self::startTrans(); // 开启事务
+        try {
+            // 1. 软删除用户代理等级记录
+            $delRes = self::where('uid', $uid)->update(['is_del' => 1]);
+
+            // 【可选】若有“代理任务完成记录”,需同步删除(示例:假设表为 user_agent_task_finish)
+            // $taskDelRes = UserAgentTaskFinish::where('uid', $uid)->delete();
+            $taskDelRes = true; // 暂无需删除任务,模拟成功
+
+            // 2. 所有操作成功时,更新用户表的代理等级为0
+            if ($delRes && $taskDelRes) {
+                User::where('uid', $uid)->update(['agent_level' => 0]);
+                self::commitTrans(); // 提交事务
+                return true;
+            } else {
+                self::rollbackTrans(); // 回滚事务
+                return self::setErrorInfo('清除代理等级失败');
+            }
+        } catch (\Exception $e) {
+            self::rollbackTrans(); // 异常时回滚
+            return self::setErrorInfo($e->getMessage());
+        }
+    }
+}

+ 199 - 0
app/admin/view/user/user_agent_level/index.php

@@ -0,0 +1,199 @@
+{extend name="public/container"}
+{block name="content"}
+<div class="layui-fluid">
+    <div class="layui-row layui-col-space15"  id="app">
+        <div class="layui-col-md12">
+            <div class="layui-card">
+                <div class="layui-card-header">搜索条件</div>
+                <div class="layui-card-body">
+                    <form class="layui-form layui-form-pane" action="">
+                        <div class="layui-form-item">
+                            <div class="layui-inline">
+                                <label class="layui-form-label">是否显示</label>
+                                <div class="layui-input-block">
+                                    <select name="is_show">
+                                        <option value="">是否显示</option>
+                                        <option value="1">显示</option>
+                                        <option value="0">不显示</option>
+                                    </select>
+                                </div>
+                            </div>
+                            <div class="layui-inline">
+                                <label class="layui-form-label">代理等级名称</label>
+                                <div class="layui-input-block">
+                                    <input type="text" name="name" class="layui-input" placeholder="请输入代理等级名称">
+                                </div>
+                            </div>
+                            <div class="layui-inline">
+                                <div class="layui-input-inline">
+                                    <button class="layui-btn layui-btn-sm layui-btn-normal" lay-submit="search" lay-filter="search">
+                                        <i class="layui-icon layui-icon-search"></i>搜索</button>
+                                </div>
+                            </div>
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </div>
+        <!-- 代理等级列表 -->
+        <div class="layui-col-md12">
+            <div class="layui-card">
+                <div class="layui-card-header">代理等级列表</div>
+                <div class="layui-card-body">
+                    <div class="layui-btn-container">
+                        <button class="layui-btn layui-btn-sm" onclick="$eb.createModalFrame(this.innerText,'{:Url('create')}')">添加代理等级</button>
+                    </div>
+                    <table class="layui-hide" id="List" lay-filter="List"></table>
+                    <script type="text/html" id="icon">
+                        <img style="cursor: pointer;max-width: 50px;" lay-event='open_image' src="{{d.icon}}">
+                    </script>
+                    <script type="text/html" id="is_forever">
+                        <input type='checkbox' name='id' disabled lay-skin='switch' value="{{d.id}}" lay-filter='is_forever' lay-text='永久|非永久'  {{ d.is_forever == 1 ? 'checked' : '' }}>
+                    </script>
+                    <script type="text/html" id="is_show">
+                        <input type='checkbox' name='id' lay-skin='switch' value="{{d.id}}" lay-filter='is_show' lay-text='开启|关闭'  {{ d.is_show == 1 ? 'checked' : '' }}>
+                    </script>
+                    <script type="text/html" id="act">
+                        <button type="button" class="layui-btn layui-btn-xs" onclick="dropdown(this)">操作 <span class="caret"></span></button>
+                        <ul class="layui-nav-child layui-anim layui-anim-upbit">
+                            <li>
+                                <a href="javascript:void(0)" onclick="$eb.createModalFrame(this.innerText,'{:Url(\'create\')}?id={{d.id}}')">
+                                    <i class="fa fa-edit"></i> 编辑等级
+                                </a>
+                            </li>
+                            <li>
+                                <a lay-event='delete' href="javascript:void(0)" >
+                                    <i class="fa fa-times"></i> 删除等级
+                                </a>
+                            </li>
+                        </ul>
+                    </script>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script src="{__ADMIN_PATH}js/layuiList.js"></script>
+{/block}
+{block name="script"}
+<script>
+    // 实例化form
+    layList.form.render();
+    // 加载列表(接口为代理等级列表接口,需与后端对应)
+    layList.tableList('List',"{:Url('get_system_agent_list')}",function (){
+        return [
+            {field: 'id', title: '编号', sort: true, event:'id', width:'6%', align:"center"},
+            {field: 'icon', title: '等级图标', templet:'#icon', align:"center", width:'10%'},
+            {field: 'name', title: '代理等级名称', edit:'name', width:'8%', align:"center"},
+            {field: 'grade', title: '等级排序', edit:'grade', width:'6%', align:"center"},
+            {field: 'direct_rebates', title: '直推返利(%)', width:'8%', align:"center"},
+            {field: 'indirect_rebates', title: '间接返利(%)', width:'8%', align:"center"},
+            {field: 'umbrella_rebates', title: '伞下返利(%)', width:'8%', align:"center"},
+            {field: 'discount', title: '进货折扣(%)', edit:'discount', width:'8%', align:"center"},
+            {field: 'valid_days', title: '有效天数', width:'8%', align:"center"},
+            {field: 'is_forever', title: '是否永久', templet:'#is_forever', width:'8%', align:"center"},
+            {field: 'is_show', title: '是否显示', templet:'#is_show', width:'8%', align:"center"},
+            {field: 'rule_explain', title: '返利规则说明', align:"center"},
+            {field: 'right', title: '操作', align:'center', toolbar:'#act', width:'8%'},
+        ];
+    });
+    // 自定义方法:快速编辑字段
+    var action= {
+        set_value: function (field, id, value) {
+            layList.baseGet(layList.Url({
+                a: 'set_value',
+                q: {field: field, id: id, value: value}
+            }), function (res) {
+                layList.msg(res.msg);
+            });
+        },
+    }
+    // 搜索回调
+    layList.search('search',function(where){
+        layList.reload(where,true);
+    });
+    // 开关-显示状态
+    layList.switch('is_show',function (odj,value) {
+        if(odj.elem.checked==true){
+            layList.baseGet(layList.Url({a:'set_show',p:{is_show:1,id:value}}),function (res) {
+                layList.msg(res.msg);
+            });
+        }else{
+            layList.baseGet(layList.Url({a:'set_show',p:{is_show:0,id:value}}),function (res) {
+                layList.msg(res.msg);
+            });
+        }
+    });
+    // 快速编辑回调
+    layList.edit(function (obj) {
+        var id=obj.data.id,value=obj.value;
+        switch (obj.field) {
+            case 'name':
+                action.set_value('name',id,value);
+                break;
+            case 'grade':
+                action.set_value('grade',id,value);
+                break;
+            case 'discount':
+                action.set_value('discount',id,value);
+                break;
+        }
+    });
+    // 排序监听
+    layList.sort(['id','grade'],true);
+    // 行工具事件
+    layList.tool(function (event,data,obj) {
+        switch (event) {
+            case 'delete':
+                var url=layList.U({a:'delete',q:{id:data.id}});
+                $eb.$swal('delete',function(){
+                    $eb.axios.get(url).then(function(res){
+                        if(res.status == 200 && res.data.code == 200) {
+                            $eb.$swal('success',res.data.msg);
+                            obj.del();
+                        }else
+                            return Promise.reject(res.data.msg || '删除失败')
+                    }).catch(function(err){
+                        $eb.$swal('error',err);
+                    });
+                })
+                break;
+            case 'open_image':
+                $eb.openImage(data.icon);
+                break;
+        }
+    })
+    // 下拉操作框-点击收起
+    $(document).click(function (e) {
+        $('.layui-nav-child').hide();
+    })
+    // 下拉操作框-位置计算
+    function dropdown(that){
+        var oEvent = arguments.callee.caller.arguments[0] || event;
+        oEvent.stopPropagation();
+        var offset = $(that).offset();
+        var top=offset.top-$(window).scrollTop();
+        var index = $(that).parents('tr').data('index');
+        $('.layui-nav-child').each(function (key) {
+            if (key != index) {
+                $(this).hide();
+            }
+        })
+        if($(document).height() < top+$(that).next('ul').height()){
+            $(that).next('ul').css({
+                'padding': 10,
+                'top': - ($(that).parent('td').height() / 2 + $(that).height() + $(that).next('ul').height()/2),
+                'min-width': 'inherit',
+                'position': 'absolute'
+            }).toggle();
+        }else{
+            $(that).next('ul').css({
+                'padding': 10,
+                'top':$(that).parent('td').height() / 2 + $(that).height(),
+                'min-width': 'inherit',
+                'position': 'absolute'
+            }).toggle();
+        }
+    }
+</script>
+{/block}

+ 265 - 0
app/models/system/SystemUserAgentLevel.php

@@ -0,0 +1,265 @@
+<?php
+
+namespace app\models\system;
+
+use app\models\user\UserAgentLevel; // 用户代理等级记录表
+//use app\models\user\SystemAgentTask; // 代理任务表(关联升级任务)
+use crmeb\traits\ModelTrait;
+use crmeb\basic\BaseModel;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+
+/**
+ * 代理等级设置模型(对应表:system_user_agent_level)
+ * 管理代理等级配置:名称、返利比例、折扣、有效期等核心规则
+ * Class SystemUserAgentLevel
+ * @package app\models\system
+ */
+class SystemUserAgentLevel extends BaseModel
+{
+    /**
+     * 数据表主键
+     * @var string
+     */
+    protected $pk = 'id';
+
+    /**
+     * 模型对应数据表名
+     * @var string
+     */
+    protected $name = 'system_user_agent_level';
+
+    use ModelTrait;
+
+    /**
+     * 时间字段格式化(add_time)
+     * @param int $value 时间戳
+     * @return string 格式化时间(Y-m-d H:i:s)
+     */
+    public function getAddTimeAttr($value)
+    {
+        return $value ? date('Y-m-d H:i:s', $value) : '';
+    }
+
+    /**
+     * 直推返利比例格式化(确保浮点型)
+     * @param mixed $value 数据库存储值
+     * @return float 返利比例(百分比)
+     */
+    public function getDirectRebatesAttr($value)
+    {
+        return (float)$value;
+    }
+
+    /**
+     * 间接返利比例格式化
+     * @param mixed $value 数据库存储值
+     * @return float 返利比例(百分比)
+     */
+    public function getIndirectRebatesAttr($value)
+    {
+        return (float)$value;
+    }
+
+    /**
+     * 伞下返利比例格式化
+     * @param mixed $value 数据库存储值
+     * @return float 返利比例(百分比)
+     */
+    public function getUmbrellaRebatesAttr($value)
+    {
+        return (float)$value;
+    }
+
+    /**
+     * 进货折扣格式化(确保整数)
+     * @param mixed $value 数据库存储值
+     * @return int 折扣(百分比)
+     */
+    public function getDiscountAttr($value)
+    {
+        return (int)$value;
+    }
+
+    /**
+     * 构建代理等级查询基础条件(显示中、未删除)
+     * @param string $alias 表别名(联查时使用)
+     * @param self|null $model 模型实例(支持链式调用)
+     * @return self
+     */
+    public static function setWhere($alias = '', self $model = null)
+    {
+        $model = $model ?? new self();
+        if ($alias) {
+            $model->alias($alias);
+            $alias .= '.';
+        }
+        // 筛选条件:显示状态(1=显示)、未删除(0=正常)
+        return $model->where("{$alias}is_show", 1)->where("{$alias}is_del", 0);
+    }
+
+    /**
+     * 获取指定代理等级的进货折扣
+     * @param int $id 代理等级ID(0则取最低等级折扣)
+     * @return int 折扣值(百分比)
+     */
+    public static function getAgentDiscount($id = 0)
+    {
+        $query = self::setWhere();
+        $id ? $query->where('id', $id) : $query->order('grade', 'asc');
+        return $query->value('discount') ?: 0;
+    }
+
+    /**
+     * 获取指定代理等级的返利比例(按类型区分)
+     * @param int $id 代理等级ID
+     * @param string $type 返利类型(direct=直推、indirect=间接、umbrella=伞下)
+     * @return float 返利比例(百分比)
+     */
+    public static function getAgentRebates($id = 0, $type = 'direct')
+    {
+        if (!$id) return 0.00;
+
+        // PHP7.3 用 switch 替代 match 表达式,实现类型→字段的映射
+        switch ($type) {
+            case 'indirect':
+                $field = 'indirect_rebates';
+                break;
+            case 'umbrella':
+                $field = 'umbrella_rebates';
+                break;
+            default:
+                // 默认返回直推返利(对应原 match 的 default 分支)
+                $field = 'direct_rebates';
+                break;
+        }
+
+        // 查询并返回对应返利比例
+        return self::setWhere()->where('id', $id)->value($field) ?: 0.00;
+    }
+
+    /**
+     * 获取用户当前代理等级信息(含等级配置+任务)
+     * @param int $uid 用户ID
+     * @param bool $isArray 是否返回[等级列表, 任务列表]
+     * @return array|bool|array|null 等级信息
+     * @throws DataNotFoundException|ModelNotFoundException|DbException
+     */
+    public static function getAgentLevelInfo($uid, $isArray = false)
+    {
+        $level = ['id' => 0, 'name' => '普通用户', 'grade' => 0]; // 默认无等级
+        $task = [];
+
+        // 获取用户当前有效代理等级记录
+        $agentLevelId = UserAgentLevel::getUserAgentLevel($uid);
+        if ($agentLevelId !== false) {
+            $level = UserAgentLevel::getUserAgentLevelInfo($agentLevelId) ?: $level;
+        }
+
+        // 获取代理等级列表(含升级标记)
+        $levelList = self::getAgentLevelListAndGrade($level['id'], $isArray);
+
+        // 关联当前等级任务(若需要)
+//        if ($isArray && isset($levelList[0])) {
+//            $task = SystemAgentTask::getTaskList($levelList[0]['id'], $uid, $level);
+//        }
+
+        return $isArray ? [$levelList, $task] : ($level['id'] ? $level : false);
+    }
+
+    /**
+     * 获取代理等级的排序值(grade)
+     * @param int $id 代理等级ID
+     * @return int 排序值(数值越大等级越高)
+     */
+    public static function getAgentLevelGrade($id)
+    {
+        return self::setWhere()->where('id', $id)->value('grade') ?: 0;
+    }
+
+    /**
+     * 获取代理等级列表(含当前等级标记、可升级状态)
+     * @param int $currentLevelId 当前等级ID
+     * @param bool $isArray 是否需要任务列表字段
+     * @param string $order 排序方式
+     * @return array 等级列表
+     * @throws DataNotFoundException|ModelNotFoundException|DbException
+     */
+    public static function getAgentLevelListAndGrade($currentLevelId, $isArray, $order = 'grade asc')
+    {
+        // 查询所有有效代理等级
+        $list = self::setWhere()
+            ->field('id, name, direct_rebates, indirect_rebates, umbrella_rebates, discount, icon, image, explain, grade, is_forever, valid_days')
+            ->order($order)
+            ->select()
+            ->toArray() ?: [];
+
+        $currentGrade = $currentLevelId ? self::getAgentLevelGrade($currentLevelId) : 0;
+
+        // 处理列表字段:标记当前等级、可升级状态、格式化有效期
+        foreach ($list as &$item) {
+            $item['is_current'] = ($item['id'] == $currentLevelId);
+            $item['is_clear'] = ($currentGrade < $item['grade']); // 是否可升级
+            $item['valid_text'] = $item['is_forever'] ? '永久有效' : "{$item['valid_days']}天";
+//            if ($isArray) $item['task_list'] = []; // 预留任务列表字段
+        }
+
+        // 若无当前等级,默认选中最低等级
+        if (!$currentLevelId && $list) $list[0]['is_current'] = true;
+
+        return $list;
+    }
+
+    /**
+     * 判断目标等级是否可升级(基于当前等级)
+     * @param int $targetLevelId 目标等级ID
+     * @param int $currentLevelId 当前等级ID
+     * @param array|null $levelList 已查询的等级列表(可选)
+     * @return bool 是否可升级
+     * @throws DataNotFoundException|ModelNotFoundException|DbException
+     */
+    public static function getAgentClear($targetLevelId, $currentLevelId, $levelList = null)
+    {
+        $levelList = $levelList ?? self::getAgentLevelListAndGrade($currentLevelId, false);
+        foreach ($levelList as $item) {
+            if ($item['id'] == $targetLevelId) return $item['is_clear'];
+        }
+        return false;
+    }
+
+    /**
+     * 获取当前等级的下一个可升级等级ID
+     * @param int $currentLevelId 当前等级ID
+     * @param int $maxGrade 最高等级排序限制
+     * @return int 下一级等级ID(0=无)
+     * @throws DataNotFoundException|ModelNotFoundException|DbException
+     */
+    public static function getNextAgentLevelId($currentLevelId, $maxGrade)
+    {
+        $levelList = self::getAgentLevelListAndGrade($currentLevelId, false, 'grade desc');
+        $currentGrade = self::getAgentLevelGrade($currentLevelId);
+        $higherLevels = [];
+
+        // 筛选:等级高于当前且不超过最高限制
+        foreach ($levelList as $item) {
+            if ($item['grade'] > $currentGrade && $item['grade'] <= $maxGrade) {
+                $higherLevels[] = $item['id'];
+            }
+        }
+
+        return $higherLevels ? end($higherLevels) : 0;
+    }
+
+//    /**
+//     * 获取用户代理等级列表+任务列表(前端展示用)
+//     * @param int $uid 用户ID
+//     * @return array 格式:['list' => 等级列表, 'task' => 当前任务]
+//     * @throws DataNotFoundException|ModelNotFoundException|DbException
+//     */
+//    public static function getAgentLevelList($uid)
+//    {
+//        list($levelList, $task) = self::getAgentLevelInfo($uid, true);
+//        return compact('levelList', 'task');
+//    }
+}

+ 194 - 0
app/models/user/UserAgentLevel.php

@@ -0,0 +1,194 @@
+<?php
+
+namespace app\models\user;
+
+use app\admin\model\system\SystemUserAgentLevel;
+use app\models\user\User; // 假设用户表模型为User
+use crmeb\basic\BaseModel;
+use crmeb\traits\ModelTrait;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+use think\Model;
+
+/**
+ * 用户代理等级记录模型(对应表:user_agent_level)
+ * 作用:管理用户与代理等级的关联关系(有效期、状态等)
+ */
+class UserAgentLevel extends BaseModel
+{
+    /**
+     * 数据表主键
+     * @var string
+     */
+    protected $pk = 'id';
+
+    /**
+     * 模型关联的数据表名
+     * @var string
+     */
+    protected $name = 'user_agent_level';
+
+    use ModelTrait;
+
+    /**
+     * 设置“有效且未删除”的查询条件
+     * @param string $alias 表别名(联查时用)
+     * @param Model|null $model 模型实例(可选,用于链式调用)
+     * @return UserAgentLevel
+     */
+    public static function validWhere($alias = '', $model = null)
+    {
+        $model = is_null($model) ? new self() : $model;
+        if ($alias) {
+            $model = $model->alias($alias);
+            $alias .= '.';
+        }
+        return $model->where("{$alias}status", 1)->where("{$alias}is_del", 0);
+    }
+
+    /**
+     * 为用户设置代理等级(支持“续期”或“新增”)
+     * @param int $uid 用户ID
+     * @param int $levelId 要设置的代理等级ID
+     * @return bool|Model 成功返回模型实例,失败返回false
+     * @throws DataNotFoundException
+     * @throws ModelNotFoundException
+     * @throws DbException
+     */
+    public static function setUserAgentLevel($uid, $levelId)
+    {
+        // 1. 验证代理等级配置是否存在
+        $agentLevel = SystemUserAgentLevel::get($levelId);
+        if (!$agentLevel) return false;
+
+        // 2. 验证用户是否存在
+        $user = User::find($uid);
+        if (!$user) return false;
+
+        // 3. 计算有效期(支持“永久”或“按天续期”)
+        $addValidTime = (int)$agentLevel->valid_days * 86400; // 有效天数 → 秒数
+        $userAgentInfo = self::validWhere()->where('uid', $uid)->where('level_id', $levelId)->find();
+
+        if ($userAgentInfo) {
+            // 若已存在该等级记录:计算剩余有效期并叠加
+            $stayTime = 0;
+            if (time() < $userAgentInfo->valid_time) {
+                $stayTime = $userAgentInfo->valid_time - time();
+            }
+            $addValidTime = $stayTime + $addValidTime + time();
+
+            $data = [
+                'is_forever' => $agentLevel->is_forever,
+                'valid_time' => $addValidTime
+            ];
+
+            // 更新用户表的代理等级标识(假设用户表有`agent_level`字段)
+            User::where('uid', $uid)->update(['agent_level' => $levelId]);
+            return self::where('uid', $uid)->where('level_id', $levelId)->update($data);
+        } else {
+            // 若不存在该等级记录:新增代理等级关联
+            $data = [
+                'is_forever' => $agentLevel->is_forever,
+                'status' => 1,
+                'is_del' => 0,
+                'grade' => $agentLevel->grade,
+                'uid' => $uid,
+                'add_time' => time(),
+                'level_id' => $levelId,
+                'discount' => $agentLevel->discount,
+                'mark' => "用户【{$user['nickname']}】于" . date('Y-m-d H:i:s') . "成为【{$agentLevel['name']}】代理"
+            ];
+
+            if ($data['is_forever']) {
+                $data['valid_time'] = 0; // 永久有效
+            } else {
+                $data['valid_time'] = $addValidTime + time(); // 计算过期时间戳
+            }
+
+            $res = self::create($data);
+            if (!$res) return false;
+
+            // 更新用户表的代理等级
+            User::where('uid', $uid)->update(['agent_level' => $levelId]);
+            return $res;
+        }
+    }
+
+    /**
+     * 获取用户当前“有效代理等级ID”(自动处理过期逻辑)
+     * @param int $uid 用户ID
+     * @param int $grade 等级排序(用于递归查找更低等级,默认0)
+     * @return bool|int 有效等级ID,无则返回false
+     * @throws DataNotFoundException
+     * @throws ModelNotFoundException
+     * @throws DbException
+     */
+    public static function getUserAgentLevel($uid, $grade = 0)
+    {
+        $model = self::validWhere();
+        if ($grade) {
+            $model = $model->where('grade', '<', $grade);
+        }
+
+        $level = $model->where('uid', $uid)
+            ->order('grade desc')
+            ->field('id, is_forever, valid_time, status, grade')
+            ->find();
+
+        if (!$level) return false;
+
+        if ($level->is_forever) {
+            return $level->id; // 永久有效,直接返回
+        }
+
+        // 检查是否过期
+        if (time() > $level->valid_time) {
+            if ($level->status == 1) {
+                $level->status = 0; // 标记为“过期”
+                $level->save();
+            }
+            // 递归查找“更低等级”的有效代理等级
+            return self::getUserAgentLevel($uid, $level->grade);
+        } else {
+            return $level->id; // 未过期,返回当前等级
+        }
+    }
+
+    /**
+     * 获取代理等级详细信息(关联代理等级配置表)
+     * @param int $id 用户代理等级记录ID
+     * @param string $keyName 需单独获取的字段名(可选,如只取`discount`)
+     * @return array|mixed|string|Model|null 等级详情数组,或指定字段值
+     * @throws DataNotFoundException
+     * @throws ModelNotFoundException
+     * @throws DbException
+     */
+    public static function getUserAgentLevelInfo($id, $keyName = '')
+    {
+        $agentInfo = self::validWhere('ual')
+            ->where('ual.id', $id)
+            ->field('al.id, ual.add_time, al.discount, ual.level_id, al.name, al.direct_rebates, al.icon')
+            ->join('system_agent_level al', 'ual.level_id = al.id')
+            ->find();
+
+        if ($keyName && $agentInfo) {
+            return $agentInfo[$keyName] ?? '';
+        }
+        return $agentInfo;
+    }
+
+    /**
+     * 获取用户已拥有的“代理等级ID集合”(去重)
+     * @param int $uid 用户ID
+     * @return array 格式:[等级ID => 等级ID, ...]
+     */
+    public static function getUserAgentLevelIds($uid)
+    {
+        return self::validWhere()
+            ->group('level_id')
+            ->where('uid', $uid)
+            ->order('grade asc')
+            ->column('level_id', 'level_id');
+    }
+}