diff --git a/app/admin/controller/api/Account.php b/app/admin/controller/api/Account.php new file mode 100644 index 0000000..d2ac1c5 --- /dev/null +++ b/app/admin/controller/api/Account.php @@ -0,0 +1,272 @@ +_vali([ + 'email.require' => '邮箱不能为空', + 'password.require' => '密码不能为空', + 'first_name.require' => '名字不能为空', + 'last_name.require' => '姓氏不能为空', + 'access_token.require' => 'access_token不能为空', + 'refresh_token.require' => 'refresh_token不能为空', + 'registration_time.require' => '注册时间不能为空' + ]); + + // 检查邮箱是否已存在 + if (Db::name('cursor_accounts')->where('email', $data['email'])->find()) { + return json([ + 'code' => 400, + 'msg' => '该邮箱已存在' + ]); + } + + // 准备插入数据 + $insertData = [ + 'email' => $data['email'], + 'password' => $data['password'], + 'first_name' => $data['first_name'], + 'last_name' => $data['last_name'], + 'access_token' => $data['access_token'], + 'refresh_token' => $data['refresh_token'], + 'machine_id' => $data['machine_id'] ?? '', + 'user_agent' => $data['user_agent'] ?? '', + 'registration_time' => $data['registration_time'], + 'created_at' => date('Y-m-d H:i:s'), + 'is_used' => 0 + ]; + + // 插入数据 + $id = Db::name('cursor_accounts')->insertGetId($insertData); + + return json([ + 'code' => 200, + 'msg' => '添加成功', + 'data' => ['id' => $id] + ]); + + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => $e->getMessage() + ]); + } + } + + /** + * 获取未使用的账号 + */ + public function getUnused() + { + try { + // 获取machine_id + $machineId = input('machine_id', ''); + if (empty($machineId)) { + return json([ + 'code' => 401, + 'msg' => '设备ID不能为空' + ]); + } + + // 检查设备是否在冷却期 + $cooldownKey = "device_cooldown_{$machineId}"; + $isInCooldown = \think\facade\Cache::get($cooldownKey); + + if ($isInCooldown) { + return json([ + 'code' => 429, + 'msg' => '请求过于频繁,请稍后再试', + 'data' => [ + 'cooldown_expires' => date('Y-m-d H:i:s', $isInCooldown) + ] + ]); + } + + // 查询设备激活状态 + $activations = Db::name('cursor_activation_codes') + ->where('used_by', '=', $machineId) + ->where('is_used', '=', 1) + ->order('used_at desc') + ->select() + ->toArray(); + + if (empty($activations)) { + return json([ + 'code' => 401, + 'msg' => '设备未激活' + ]); + } + + // 计算总天数和到期时间 + $totalDays = array_sum(array_column($activations, 'days')); + $firstActivationTime = strtotime($activations[count($activations)-1]['used_at']); + $expireTime = $firstActivationTime + ($totalDays * 24 * 3600); + + // 检查是否过期 + if (time() > $expireTime) { + return json([ + 'code' => 401, + 'msg' => '设备授权已过期' + ]); + } + + // 检查缓存中是否有该设备最近使用的账号 + $cacheKey = "device_account_{$machineId}"; + $cachedAccount = \think\facade\Cache::get($cacheKey); + + if ($cachedAccount) { + // 检查账号是否仍然可用 + $account = Db::name('cursor_accounts') + ->where('id', $cachedAccount['id']) + ->find(); + + if ($account) { + // 更新缓存时间 + \think\facade\Cache::set($cacheKey, $account, 600); // 10分钟缓存 + + // 返回账号信息 + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => [ + 'email' => $account['email'], + 'password' => $account['password'], + 'access_token' => $account['access_token'], + 'refresh_token' => $account['refresh_token'], + 'expire_time' => date('Y-m-d H:i:s', $expireTime), + 'days_left' => ceil(($expireTime - time()) / 86400) + ] + ]); + } + } + + // 记录冷却期 + $cooldownExpires = time() + 1800; // 30分钟冷却期 + \think\facade\Cache::set($cooldownKey, $cooldownExpires, 1800); + + // 开启事务 + Db::startTrans(); + try { + // 获取一个未使用的账号 + $account = Db::name('cursor_accounts') + ->where('is_used', 0) + ->lock(true) + ->find(); + + if (empty($account)) { + Db::rollback(); + return json([ + 'code' => 404, + 'msg' => '没有可用的账号' + ]); + } + + // 如果有旧账号,将其标记为未使用 + if ($cachedAccount) { + Db::name('cursor_accounts') + ->where('id', $cachedAccount['id']) + ->update([ + 'is_used' => 0, + 'used_at' => null, + 'used_by' => '' + ]); + } + + // 更新新账号状态 + Db::name('cursor_accounts') + ->where('id', $account['id']) + ->update([ + 'is_used' => 1, + 'used_at' => date('Y-m-d H:i:s'), + 'used_by' => $machineId + ]); + + Db::commit(); + + // 缓存新账号信息 + \think\facade\Cache::set($cacheKey, $account, 600); // 10分钟缓存 + + // 返回账号信息 + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => [ + 'email' => $account['email'], + 'password' => $account['password'], + 'access_token' => $account['access_token'], + 'refresh_token' => $account['refresh_token'], + 'expire_time' => date('Y-m-d H:i:s', $expireTime), + 'days_left' => ceil(($expireTime - time()) / 86400) + ] + ]); + + } catch (\Exception $e) { + Db::rollback(); + throw $e; + } + + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => $e->getMessage() + ]); + } + } + + /** + * 后台强制重置设备账号 + * @auth true + */ + public function resetDeviceAccount() + { + try { + $data = $this->_vali([ + 'machine_id.require' => '设备ID不能为空' + ]); + + $machineId = $data['machine_id']; + + // 清除设备的账号缓存 + $cacheKey = "device_account_{$machineId}"; + \think\facade\Cache::delete($cacheKey); + + // 清除冷却期 + $cooldownKey = "device_cooldown_{$machineId}"; + \think\facade\Cache::delete($cooldownKey); + + // 将该设备使用的账号标记为未使用 + Db::name('cursor_accounts') + ->where('used_by', $machineId) + ->update([ + 'is_used' => 0, + 'used_at' => null, + 'used_by' => '' + ]); + + return json([ + 'code' => 200, + 'msg' => '重置成功' + ]); + + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => $e->getMessage() + ]); + } + } +} diff --git a/app/admin/controller/api/Agent.php b/app/admin/controller/api/Agent.php new file mode 100644 index 0000000..caa1698 --- /dev/null +++ b/app/admin/controller/api/Agent.php @@ -0,0 +1,225 @@ +_vali([ + 'username.require' => '用户名不能为空', + 'password.require' => '密码不能为空' + ]); + + // 查询代理商 + $agent = Db::name('cursor_agents') + ->where('username', $data['username']) + ->find(); + + if (empty($agent)) { + return json(['code' => 400, 'msg' => '用户名或密码错误']); + } + + // 验证密码 + if (!password_verify($data['password'], $agent['password'])) { + return json(['code' => 400, 'msg' => '用户名或密码错误']); + } + + // 检查状态 + if ($agent['status'] != 1) { + return json(['code' => 400, 'msg' => '账号已被禁用']); + } + + // 生成token + $token = md5($agent['id'] . time() . rand(1000, 9999)); + + // 存储token,有效期7天 + Cache::set('agent_token_' . $token, $agent['id'], 7 * 24 * 3600); + + // 更新登录信息 + Db::name('cursor_agents') + ->where('id', $agent['id']) + ->update([ + 'last_login_time' => date('Y-m-d H:i:s'), + 'last_login_ip' => $this->request->ip(), + 'updated_at' => date('Y-m-d H:i:s') + ]); + + // 返回登录信息 + return json([ + 'code' => 200, + 'msg' => '登录成功', + 'data' => [ + 'token' => $token, + 'agent' => [ + 'id' => $agent['id'], + 'username' => $agent['username'], + 'nickname' => $agent['nickname'], + 'level' => $agent['level'], + 'balance' => $agent['balance'], + 'commission_rate' => $agent['commission_rate'] + ] + ] + ]); + + } catch (\Exception $e) { + return json(['code' => 500, 'msg' => $e->getMessage()]); + } + } + + /** + * 验证token + */ + protected function checkToken() + { + $token = $this->request->header('token'); + if (empty($token)) { + return json(['code' => 401, 'msg' => '请先登录']); + } + + $agentId = Cache::get('agent_token_' . $token); + if (empty($agentId)) { + return json(['code' => 401, 'msg' => '登录已过期,请重新登录']); + } + + return $agentId; + } + + /** + * 获取代理商信息 + */ + public function info() + { + try { + $agentId = $this->checkToken(); + if (!is_numeric($agentId)) { + return $agentId; // 返回错误信息 + } + + $agent = Db::name('cursor_agents') + ->where('id', $agentId) + ->find(); + + if (empty($agent)) { + return json(['code' => 400, 'msg' => '代理商不存在']); + } + + // 获取激活码统计 + $codeStats = Db::name('cursor_agent_codes') + ->where('agent_id', $agentId) + ->field([ + 'COUNT(*) as total_codes', + 'SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as settled_codes', + 'SUM(commission) as total_commission', + 'SUM(CASE WHEN status = 1 THEN commission ELSE 0 END) as settled_commission' + ]) + ->find(); + + // 获取未使用的激活码 + $unusedCodes = Db::name('cursor_agent_codes') + ->alias('ac') + ->join('cursor_activation_codes c', 'ac.code_id = c.id') + ->where('ac.agent_id', $agentId) + ->where('c.is_used', 0) + ->field('c.code, c.days, ac.price') + ->select() + ->toArray(); + + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => [ + 'agent' => [ + 'id' => $agent['id'], + 'username' => $agent['username'], + 'nickname' => $agent['nickname'], + 'level' => $agent['level'], + 'balance' => $agent['balance'], + 'total_income' => $agent['total_income'], + 'commission_rate' => $agent['commission_rate'], + 'last_login_time' => $agent['last_login_time'] + ], + 'stats' => [ + 'total_codes' => $codeStats['total_codes'] ?? 0, + 'settled_codes' => $codeStats['settled_codes'] ?? 0, + 'total_commission' => $codeStats['total_commission'] ?? 0, + 'settled_commission' => $codeStats['settled_commission'] ?? 0 + ], + 'unused_codes' => $unusedCodes + ] + ]); + + } catch (\Exception $e) { + return json(['code' => 500, 'msg' => $e->getMessage()]); + } + } + + /** + * 获取代理商的激活码列表 + */ + public function codes() + { + try { + $agentId = $this->checkToken(); + if (!is_numeric($agentId)) { + return $agentId; + } + + $page = input('page/d', 1); + $limit = input('limit/d', 20); + $status = input('status'); + + $query = Db::name('cursor_agent_codes') + ->alias('ac') + ->join('cursor_activation_codes c', 'ac.code_id = c.id') + ->where('ac.agent_id', $agentId); + + if (isset($status)) { + $query = $query->where('c.is_used', intval($status)); + } + + $total = $query->count(); + $list = $query->field([ + 'c.code', + 'c.days', + 'c.is_used', + 'c.used_at', + 'c.used_by', + 'ac.price', + 'ac.commission', + 'ac.status as settle_status', + 'ac.created_at' + ]) + ->page($page, $limit) + ->order('ac.created_at desc') + ->select() + ->toArray(); + + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => [ + 'list' => $list, + 'total' => $total, + 'page' => $page, + 'limit' => $limit + ] + ]); + + } catch (\Exception $e) { + return json(['code' => 500, 'msg' => $e->getMessage()]); + } + } +} \ No newline at end of file diff --git a/app/admin/controller/api/Mail.php b/app/admin/controller/api/Mail.php index 3ac156c..a6e75db 100644 --- a/app/admin/controller/api/Mail.php +++ b/app/admin/controller/api/Mail.php @@ -35,6 +35,16 @@ class Mail extends Controller 'BROWSER_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36', 'MAIL_SERVER' => 'https://tempmail.plus' ] + ], + [ + 'name' => '备用配置2', + 'config' => [ + 'DOMAIN' => 'wuen.site', + 'TEMP_MAIL' => 'actoke', + 'TEMP_MAIL_EXT' => '@mailto.plus', + 'BROWSER_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36', + 'MAIL_SERVER' => 'https://tempmail.plus' + ] ] ]; diff --git a/app/admin/controller/api/Member.php b/app/admin/controller/api/Member.php index 268171b..3489061 100644 --- a/app/admin/controller/api/Member.php +++ b/app/admin/controller/api/Member.php @@ -5,6 +5,7 @@ namespace app\admin\controller\api; use app\manager\model\Member as MemberModel; use think\admin\Controller; +use think\facade\Db; /** @@ -82,4 +83,208 @@ class Member extends Controller ] ]); } + + /** + * 激活码验证并激活 + * @return void + */ + public function activate() + { + try { + // 验证激活码 + $code = input('code'); + if (empty($code)) { + return json(['code' => 400, 'msg' => '激活码不能为空']); + } + + // 获取设备信息 + $machineId = input('machine_id', ''); + if (empty($machineId)) { + return json(['code' => 400, 'msg' => '设备ID不能为空']); + } + + // 收集设备详细信息 + $deviceInfo = [ + 'machine_id' => $machineId, + 'os' => input('os', ''), // 操作系统信息 + 'device_name' => input('device_name', ''), // 设备名称 + 'ip' => $this->request->ip(), // IP地址 + 'user_agent' => $this->request->header('user-agent', ''), // UA信息 + 'location' => input('location', '') // 地理位置(如果有) + ]; + + // 获取激活码信息 + $activation = Db::name('cursor_activation_codes') + ->where('code', $code) + ->where('is_used', 0) + ->find(); + + if (empty($activation)) { + return json(['code' => 400, 'msg' => '激活码无效或已被使用']); + } + + // 开启事务 + Db::startTrans(); + try { + // 标记新激活码为已使用 + Db::name('cursor_activation_codes') + ->where('id', $activation['id']) + ->update([ + 'is_used' => 1, + 'used_at' => date('Y-m-d H:i:s'), + 'used_by' => $machineId, + 'device_info' => json_encode($deviceInfo) + ]); + + Db::commit(); + + // 获取该设备所有的激活记录 + $activations = Db::name('cursor_activation_codes') + ->where('used_by', '=', $machineId) + ->where('is_used', '=', 1) + ->order('used_at desc') + ->select() + ->toArray(); + + // 计算总天数 + $totalDays = array_sum(array_column($activations, 'days')); + + // 计算最终到期时间 + $now = time(); + $firstActivationTime = strtotime($activations[count($activations)-1]['used_at']); // 第一次激活时间 + $expireTime = $firstActivationTime + ($totalDays * 24 * 3600); // 总天数转换为秒数 + $daysLeft = ceil(($expireTime - $now) / 86400); + + // 返回成功信息 + $result = [ + 'code' => 200, + 'msg' => '激活成功', + 'data' => [ + 'expire_time' => date('Y-m-d H:i:s', $expireTime), + 'total_days' => $totalDays, + 'added_days' => $activation['days'], + 'days_left' => $daysLeft, + 'machine_id' => $machineId, + 'device_info' => $deviceInfo, + 'activation_time' => date('Y-m-d H:i:s'), + 'activation_records' => array_map(function($record) { + return [ + 'code' => $record['code'], + 'days' => $record['days'], + 'activation_time' => $record['used_at'], + 'device_info' => json_decode($record['device_info'], true) + ]; + }, $activations) + ] + ]; + + return json($result); + + } catch (\Exception $e) { + Db::rollback(); + throw $e; + } + + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => $e->getMessage() + ]); + } + } + + /** + * 获取会员状态 + */ + public function status() + { + try { + // 获取设备ID + $machineId = input('machine_id', ''); + if (empty($machineId)) { + return json(['code' => 400, 'msg' => '设备ID不能为空']); + } + + // 获取该设备所有的激活记录 + $activations = Db::name('cursor_activation_codes') + ->where('used_by', '=', $machineId) + ->where('is_used', '=', 1) + ->order('used_at desc') + ->select() + ->toArray(); + + if (empty($activations)) { + return json([ + 'code' => 401, + 'msg' => '未激活', + 'data' => [ + 'status' => 'inactive', + 'expire_time' => '', + 'total_days' => 0, + 'days_left' => 0, + 'activation_records' => [] + ] + ]); + } + + // 计算总天数 + $totalDays = array_sum(array_column($activations, 'days')); + + // 计算最终到期时间 + $now = time(); + $firstActivationTime = strtotime($activations[count($activations)-1]['used_at']); // 第一次激活时间 + $expireTime = $firstActivationTime + ($totalDays * 24 * 3600); // 总天数转换为秒数 + $daysLeft = ceil(($expireTime - $now) / 86400); + + // 判断是否过期 + if ($daysLeft <= 0) { + return json([ + 'code' => 401, + 'msg' => '已过期', + 'data' => [ + 'status' => 'expired', + 'expire_time' => date('Y-m-d H:i:s', $expireTime), + 'total_days' => $totalDays, + 'days_left' => 0, + 'machine_id' => $machineId, + 'activation_records' => array_map(function($record) { + return [ + 'code' => $record['code'], + 'days' => $record['days'], + 'activation_time' => $record['used_at'], + 'device_info' => json_decode($record['device_info'], true) + ]; + }, $activations) + ] + ]); + } + + // 返回正常状态 + return json([ + 'code' => 200, + 'msg' => '正常', + 'data' => [ + 'status' => 'active', + 'expire_time' => date('Y-m-d H:i:s', $expireTime), + 'total_days' => $totalDays, + 'days_left' => $daysLeft, + 'machine_id' => $machineId, + 'activation_records' => array_map(function($record) { + return [ + 'code' => $record['code'], + 'days' => $record['days'], + 'activation_time' => $record['used_at'], + 'device_info' => json_decode($record['device_info'], true) + ]; + }, $activations) + ] + ]); + + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => $e->getMessage() + ]); + } + } } \ No newline at end of file diff --git a/app/agent/controller/Index.php b/app/agent/controller/Index.php new file mode 100644 index 0000000..8091e65 --- /dev/null +++ b/app/agent/controller/Index.php @@ -0,0 +1,108 @@ +request->isPost()) { + $count = intval(input('count', 1)); + $count = max(1, min($count, 100)); // 限制一次最多生成100个 + + // 获取当前代理商信息 + $agentId = session('agent.id'); + $agent = Db::name('cursor_agents')->where('id', $agentId)->find(); + if (empty($agent)) { + $this->error('代理商信息不存在!'); + } + + // 开启事务 + Db::startTrans(); + try { + $codes = []; + $agentCodes = []; + $now = date('Y-m-d H:i:s'); + + for ($i = 0; $i < $count; $i++) { + // 生成激活码 + $codes[] = [ + 'code' => strtoupper(substr(md5(uniqid() . mt_rand()), 0, 16)), + 'days' => 30, // 固定30天 + 'created_at' => $now, + 'is_used' => 0 + ]; + } + + // 批量插入激活码 + $codeIds = Db::name('cursor_activation_codes')->insertAll($codes); + + // 计算佣金 + $price = 100; // 固定价格100元 + $commission = $price * ($agent['commission_rate'] / 100); + + // 如果是二级代理,计算上级佣金 + $parentCommission = 0; + if ($agent['level'] == 2 && $agent['parent_id'] > 0) { + $parent = Db::name('cursor_agents')->where('id', $agent['parent_id'])->find(); + if ($parent) { + $parentCommission = $price * ($parent['commission_rate'] / 100); + } + } + + // 准备代理商激活码数据 + foreach ($codes as $index => $code) { + $agentCodes[] = [ + 'agent_id' => $agentId, + 'code_id' => $codeIds[$index], + 'price' => $price, + 'commission' => $commission, + 'parent_commission' => $parentCommission, + 'status' => 0, + 'created_at' => $now, + 'updated_at' => $now + ]; + } + + // 批量插入代理商激活码关联 + Db::name('cursor_agent_codes')->insertAll($agentCodes); + + Db::commit(); + $this->success('生成成功!'); + } catch (\Exception $e) { + Db::rollback(); + $this->error('生成失败:' . $e->getMessage()); + } + } else { + $this->fetch(); + } + } + + /** + * 我的激活码列表 + */ + public function codes() + { + $this->title = '我的激活码'; + $agentId = session('agent.id'); + + $query = $this->_query('cursor_agent_codes')->alias('ac') + ->join('cursor_activation_codes c', 'c.id = ac.code_id') + ->where('ac.agent_id', $agentId) + ->field('ac.*, c.code, c.days, c.is_used, c.used_at, c.used_by'); + + // 数据列表处理 + $query->equal('c.is_used')->equal('ac.status'); + // 列表排序并显示 + $query->order('ac.id desc')->page(); + } +} \ No newline at end of file diff --git a/app/agent/view/index/codes.html b/app/agent/view/index/codes.html new file mode 100644 index 0000000..d8ce2b3 --- /dev/null +++ b/app/agent/view/index/codes.html @@ -0,0 +1,83 @@ +{extend name="../../admin/view/main"} + +{block name="button"} + +{/block} + +{block name="content"} +
+ + + + {notempty name='list'} + + + + + + + + + + + + + + {/notempty} + + {foreach $list as $key=>$vo} + + + + + + + + + + + + {/foreach} + +
激活码有效天数销售价格佣金使用状态使用时间使用者结算状态生成时间
{$vo.code}{$vo.days} 天{$vo.price}{$vo.commission} + {if $vo.is_used eq 1} + 已使用 + {else} + 未使用 + {/if} + {$vo.used_at|format_datetime|default='--'}{$vo.used_by|default='--'} + {if $vo.status eq 1} + 已结算 + {else} + 未结算 + {/if} + {$vo.created_at|format_datetime}
+ {empty name='list'}没有记录哦{else}{$pagehtml|raw|default=''}{/empty} +
+{/block} \ No newline at end of file diff --git a/app/agent/view/index/generate.html b/app/agent/view/index/generate.html new file mode 100644 index 0000000..c38eeec --- /dev/null +++ b/app/agent/view/index/generate.html @@ -0,0 +1,20 @@ +{extend name="../../admin/view/main"} + +{block name="content"} +
+
+
+ +
+ + 一次最多可生成100个激活码,每个激活码售价100元 +
+
+
+
+
+ + +
+
+{/block} \ No newline at end of file diff --git a/app/manager/controller/Account.php b/app/manager/controller/Account.php new file mode 100644 index 0000000..3ae0c3b --- /dev/null +++ b/app/manager/controller/Account.php @@ -0,0 +1,304 @@ +title = 'Cursor账号管理'; + // 创建查询对象 + $query = $this->_query('cursor_accounts'); + // 数据列表处理 + $query->equal('is_used')->like('email'); + // 列表排序并显示 + $query->order('id desc')->page(); + } + + /** + * 生成激活码 + * @auth true + */ + public function generate() + { + if ($this->request->isPost()) { + // 获取参数 + $count = intval(input('count', 1)); + $days = intval(input('days', 30)); + $agentId = input('agent_id', ''); + $price = floatval(input('price', 0)); + + // 参数验证 + if ($count < 1 || $count > 100) { + $this->error('生成数量必须在1-100之间'); + } + if ($days < 1) { + $this->error('有效天数必须大于0'); + } + + // 开启事务 + \think\facade\Db::startTrans(); + + $codes = []; + $now = date('Y-m-d H:i:s'); + + // 生成激活码 + for ($i = 0; $i < $count; $i++) { + $codes[] = [ + 'code' => strtoupper(substr(md5(uniqid() . mt_rand()), 0, 16)), + 'days' => $days, + 'created_at' => $now, + 'is_used' => 0 + ]; + } + + // 批量插入激活码 + $result = \think\facade\Db::name('cursor_activation_codes')->insertAll($codes); + if (!$result) { + \think\facade\Db::rollback(); + $this->error('激活码生成失败'); + } + + // 获取插入的ID范围 + $firstId = \think\facade\Db::name('cursor_activation_codes')->getLastInsID(); + $lastId = $firstId + count($codes) - 1; + + // 如果指定了代理商,则自动分配 + if (!empty($agentId) && $price > 0) { + $agent = \think\facade\Db::name('cursor_agents') + ->where('id', $agentId) + ->find(); + + if ($agent) { + $agentCodes = []; + $commission = $price * ($agent['commission_rate'] / 100); + + // 如果是二级代理,计算上级佣金 + $parentCommission = 0; + if ($agent['level'] == 2 && $agent['parent_id'] > 0) { + $parent = \think\facade\Db::name('cursor_agents') + ->where('id', $agent['parent_id']) + ->find(); + if ($parent) { + $parentCommission = $price * ($parent['commission_rate'] / 100); + } + } + + // 准备代理商激活码数据 + for ($id = $firstId; $id <= $lastId; $id++) { + $agentCodes[] = [ + 'agent_id' => $agent['id'], + 'code_id' => $id, + 'price' => $price, + 'commission' => $commission, + 'parent_commission' => $parentCommission, + 'status' => 0, + 'created_at' => $now, + 'updated_at' => $now + ]; + } + + // 批量插入代理商激活码关联 + if (!empty($agentCodes)) { + $result = \think\facade\Db::name('cursor_agent_codes')->insertAll($agentCodes); + if (!$result) { + \think\facade\Db::rollback(); + $this->error('代理商激活码分配失败'); + } + } + } + } + + \think\facade\Db::commit(); + $this->success('生成成功!'); + } else { + // 获取代理商列表 + $this->agents = \think\facade\Db::name('cursor_agents') + ->where('status', 1) + ->select() + ->toArray(); + $this->fetch(); + } + } + + /** + * 激活码管理 + * @auth true + * @menu true + */ + public function codes() + { + $this->title = '激活码管理'; + + // 获取代理商列表用于筛选 + $this->agents = \think\facade\Db::name('cursor_agents') + ->where('status', 1) + ->select() + ->toArray(); + + // 创建查询对象 + $query = $this->_query('cursor_activation_codes')->alias('c') + ->leftJoin('cursor_agent_codes ac', 'c.id = ac.code_id') + ->leftJoin('cursor_agents a', 'ac.agent_id = a.id') + ->field([ + 'c.*', + 'a.id as agent_id', + 'a.username as agent_name', + 'a.level as agent_level', + 'ac.price', + 'ac.commission', + 'ac.parent_commission', + 'ac.status as settle_status' + ]); + + // 数据列表处理 + $query->where(function($query) { + $agentId = input('agent_id', ''); + if ($agentId !== '') { + $query->where('a.id', '=', $agentId); + } + + $isUsed = input('is_used', ''); + if ($isUsed !== '') { + $query->where('c.is_used', '=', $isUsed); + } + + $code = input('code', ''); + if ($code) { + $query->whereLike('c.code', "%{$code}%"); + } + + $usedBy = input('used_by', ''); + if ($usedBy) { + $query->whereLike('c.used_by', "%{$usedBy}%"); + } + }); + + // 列表排序并显示 + $query->order('c.id desc')->page(); + } + + /** + * 修改账号状态 + * @auth true + */ + public function state() + { + $this->_applyFormToken(); + $this->_save('cursor_accounts', ['is_used' => input('state')]); + } + + /** + * 删除账号 + * @auth true + */ + public function remove() + { + $this->_applyFormToken(); + $this->_delete('cursor_accounts'); + } + + /** + * 删除激活码 + * @auth true + */ + public function removeCode() + { + $this->_applyFormToken(); + $this->_delete('cursor_activation_codes'); + } + + /** + * 设备账号管理 + * @auth true + * @menu true + */ + public function devices() + { + $this->title = '设备账号管理'; + // 创建查询对象 + $query = $this->_query('cursor_accounts') + ->where('used_by', '<>', '') + ->where('is_used', 1); + + // 数据列表处理 + $query->like('email,used_by'); + // 列表排序并显示 + $query->order('used_at desc')->page(); + + // 获取列表数据 + $list = $this->app->db->getList(); + + // 检查每个设备的缓存状态 + foreach ($list as &$item) { + if (!empty($item['used_by'])) { + // 检查账号缓存 + $cacheKey = "device_account_{$item['used_by']}"; + $cachedAccount = \think\facade\Cache::get($cacheKey); + $item['has_cache'] = !empty($cachedAccount); + + // 检查冷却期 + $cooldownKey = "device_cooldown_{$item['used_by']}"; + $cooldownTime = \think\facade\Cache::get($cooldownKey); + $item['in_cooldown'] = $cooldownTime > time(); + $item['cooldown_time'] = $cooldownTime ? date('Y-m-d H:i:s', $cooldownTime) : ''; + + // 检查缓存账号是否与数据库一致 + $item['cache_match'] = false; + if ($cachedAccount) { + $item['cache_match'] = ($cachedAccount['id'] == $item['id']); + } + } + } + + $this->list = $list; + } + + /** + * 重置设备账号 + * @auth true + */ + public function resetDevice() + { + $this->_applyFormToken(); + try { + $machineId = input('machine_id', ''); + if (empty($machineId)) { + $this->error('设备ID不能为空'); + } + + // 清除设备的账号缓存 + $cacheKey = "device_account_{$machineId}"; + \think\facade\Cache::delete($cacheKey); + + // 清除冷却期 + $cooldownKey = "device_cooldown_{$machineId}"; + \think\facade\Cache::delete($cooldownKey); + + // 将该设备使用的账号标记为未使用 + \think\facade\Db::name('cursor_accounts') + ->where('used_by', $machineId) + ->update([ + 'is_used' => 0, + 'used_at' => null, + 'used_by' => '' + ]); + + $this->success('重置成功!'); + } catch (\Exception $e) { + $this->error("重置失败:{$e->getMessage()}"); + } + } +} \ No newline at end of file diff --git a/app/manager/controller/Agent.php b/app/manager/controller/Agent.php new file mode 100644 index 0000000..574fd32 --- /dev/null +++ b/app/manager/controller/Agent.php @@ -0,0 +1,275 @@ +title = '代理商管理'; + // 创建查询对象 + $query = $this->_query('cursor_agents'); + // 数据列表处理 + $query->equal('level,status')->like('username,nickname'); + // 列表排序并显示 + $query->order('id desc')->page(); + } + + /** + * 添加代理商 + * @auth true + */ + public function add() + { + if ($this->request->isPost()) { + $data = $this->_vali([ + 'username.require' => '用户名不能为空', + 'password.require' => '密码不能为空', + 'nickname.default' => '', + 'level.require' => '代理级别不能为空', + 'parent_id.default' => 0, + 'commission_rate.require' => '佣金比例不能为空', + 'status.default' => 1, + 'remark.default' => '' + ]); + + // 检查用户名是否存在 + if (Db::name('cursor_agents')->where('username', $data['username'])->find()) { + $this->error('用户名已存在!'); + } + + // 如果是二级代理,检查上级代理 + if ($data['level'] == 2) { + if (empty($data['parent_id'])) { + $this->error('请选择上级代理!'); + } + $parent = Db::name('cursor_agents')->where('id', $data['parent_id'])->find(); + if (empty($parent) || $parent['level'] != 1) { + $this->error('上级代理不存在或不是一级代理!'); + } + } + + // 处理密码 + $data['password'] = password_hash($data['password'], PASSWORD_DEFAULT); + $data['created_at'] = $data['updated_at'] = date('Y-m-d H:i:s'); + + if (Db::name('cursor_agents')->insert($data) !== false) { + $this->success('添加成功!'); + } else { + $this->error('添加失败!'); + } + } else { + // 获取一级代理列表(用于选择上级) + $this->agents = Db::name('cursor_agents') + ->where('level', 1) + ->where('status', 1) + ->select(); + $this->fetch(); + } + } + + /** + * 编辑代理商 + * @auth true + */ + public function edit() + { + $this->_applyFormToken(); + $this->_form('cursor_agents', 'form'); + } + + /** + * 修改代理商状态 + * @auth true + */ + public function state() + { + $this->_applyFormToken(); + $this->_save('cursor_agents', ['status' => input('status')]); + } + + /** + * 删除代理商 + * @auth true + */ + public function remove() + { + $this->_applyFormToken(); + $this->_delete('cursor_agents'); + } + + /** + * 分配激活码 + * @auth true + */ + public function assignCodes() + { + if ($this->request->isPost()) { + $data = $this->_vali([ + 'agent_id.require' => '代理商不能为空', + 'code_ids.require' => '请选择要分配的激活码', + 'price.require' => '请设置销售价格' + ]); + + // 开启事务 + Db::startTrans(); + try { + $agent = Db::name('cursor_agents')->where('id', $data['agent_id'])->find(); + if (empty($agent)) { + $this->error('代理商不存在!'); + } + + $codeIds = explode(',', $data['code_ids']); + $insertData = []; + $now = date('Y-m-d H:i:s'); + + foreach ($codeIds as $codeId) { + // 检查激活码是否已分配 + $exists = Db::name('cursor_agent_codes')->where('code_id', $codeId)->find(); + if ($exists) { + continue; + } + + // 计算佣金 + $commission = $data['price'] * ($agent['commission_rate'] / 100); + + // 如果是二级代理,计算上级佣金 + $parentCommission = 0; + if ($agent['level'] == 2 && $agent['parent_id'] > 0) { + $parent = Db::name('cursor_agents')->where('id', $agent['parent_id'])->find(); + if ($parent) { + $parentCommission = $data['price'] * ($parent['commission_rate'] / 100); + } + } + + $insertData[] = [ + 'agent_id' => $data['agent_id'], + 'code_id' => $codeId, + 'price' => $data['price'], + 'commission' => $commission, + 'parent_commission' => $parentCommission, + 'status' => 0, + 'created_at' => $now, + 'updated_at' => $now + ]; + } + + // 批量插入数据 + if (!empty($insertData)) { + Db::name('cursor_agent_codes')->insertAll($insertData); + } + + Db::commit(); + $this->success('分配成功!'); + } catch (\Exception $e) { + Db::rollback(); + $this->error('分配失败:' . $e->getMessage()); + } + } else { + // 获取未分配的激活码 + $this->codes = Db::name('cursor_activation_codes') + ->where('is_used', 0) + ->whereNotExists(function($query) { + $query->table('cursor_agent_codes')->whereRaw('cursor_agent_codes.code_id = cursor_activation_codes.id'); + }) + ->select(); + + // 获取代理商列表 + $this->agents = Db::name('cursor_agents') + ->where('status', 1) + ->select(); + + $this->fetch(); + } + } + + /** + * 查看代理商激活码 + * @auth true + */ + public function codes() + { + $this->title = '代理商激活码'; + $query = $this->_query('cursor_agent_codes')->alias('ac') + ->join('cursor_agents a', 'a.id = ac.agent_id') + ->join('cursor_activation_codes c', 'c.id = ac.code_id') + ->field('ac.*, a.username as agent_name, c.code, c.days, c.is_used, c.used_at, c.used_by'); + + // 数据列表处理 + $query->equal('ac.agent_id')->equal('c.is_used')->equal('ac.status'); + // 列表排序并显示 + $query->order('ac.id desc')->page(); + } + + /** + * 结算佣金 + * @auth true + */ + public function settle() + { + $id = input('id'); + if (empty($id)) { + $this->error('参数错误!'); + } + + $agentCode = Db::name('cursor_agent_codes')->where('id', $id)->find(); + if (empty($agentCode)) { + $this->error('记录不存在!'); + } + + if ($agentCode['status'] == 1) { + $this->error('该记录已结算!'); + } + + // 开启事务 + Db::startTrans(); + try { + // 更新代理商余额 + Db::name('cursor_agents') + ->where('id', $agentCode['agent_id']) + ->inc('balance', $agentCode['commission']) + ->inc('total_income', $agentCode['commission']) + ->update(); + + // 如果有上级佣金,更新上级代理商余额 + if ($agentCode['parent_commission'] > 0) { + $agent = Db::name('cursor_agents')->where('id', $agentCode['agent_id'])->find(); + if ($agent && $agent['parent_id'] > 0) { + Db::name('cursor_agents') + ->where('id', $agent['parent_id']) + ->inc('balance', $agentCode['parent_commission']) + ->inc('total_income', $agentCode['parent_commission']) + ->update(); + } + } + + // 更新结算状态 + Db::name('cursor_agent_codes') + ->where('id', $id) + ->update([ + 'status' => 1, + 'settle_time' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s') + ]); + + Db::commit(); + $this->success('结算成功!'); + } catch (\Exception $e) { + Db::rollback(); + $this->error('结算失败:' . $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/app/manager/model/ActivationCode.php b/app/manager/model/ActivationCode.php new file mode 100644 index 0000000..d71b3c3 --- /dev/null +++ b/app/manager/model/ActivationCode.php @@ -0,0 +1,39 @@ + 'datetime', + 'created_at' => 'datetime', + ]; + + // 获取代理商分配信息 + public function agentCode() + { + return $this->hasOne('app\manager\model\AgentCode', 'code_id'); + } + + // 检查是否已分配给代理商 + public function isAssigned() + { + return (bool)$this->agentCode; + } + + // 检查是否可用 + public function isAvailable() + { + return !$this->is_used && !$this->isAssigned(); + } +} \ No newline at end of file diff --git a/app/manager/model/Agent.php b/app/manager/model/Agent.php new file mode 100644 index 0000000..bd68a22 --- /dev/null +++ b/app/manager/model/Agent.php @@ -0,0 +1,51 @@ + 'float', + 'total_income' => 'float', + 'commission_rate' => 'float', + 'last_login_time' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + // 获取父级代理 + public function parent() + { + return $this->belongsTo(Agent::class, 'parent_id'); + } + + // 获取子级代理 + public function children() + { + return $this->hasMany(Agent::class, 'parent_id'); + } + + // 获取代理的激活码 + public function codes() + { + return $this->hasMany('app\manager\model\AgentCode', 'agent_id'); + } + + // 更新余额 + public function updateBalance($amount) + { + $this->balance += $amount; + $this->total_income += max(0, $amount); + return $this->save(); + } +} \ No newline at end of file diff --git a/app/manager/model/AgentCode.php b/app/manager/model/AgentCode.php new file mode 100644 index 0000000..cc27bce --- /dev/null +++ b/app/manager/model/AgentCode.php @@ -0,0 +1,68 @@ + 'float', + 'commission' => 'float', + 'parent_commission' => 'float', + 'settle_time' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + // 获取所属代理商 + public function agent() + { + return $this->belongsTo('app\manager\model\Agent', 'agent_id'); + } + + // 获取激活码信息 + public function code() + { + return $this->belongsTo('app\manager\model\ActivationCode', 'code_id'); + } + + // 结算佣金 + public function settle() + { + if ($this->status == 1) { + return false; + } + + // 开启事务 + $this->startTrans(); + try { + // 更新代理商余额 + $this->agent->updateBalance($this->commission); + + // 如果有上级佣金,更新上级代理商余额 + if ($this->parent_commission > 0 && $this->agent->parent) { + $this->agent->parent->updateBalance($this->parent_commission); + } + + // 更新结算状态 + $this->status = 1; + $this->settle_time = date('Y-m-d H:i:s'); + $this->save(); + + $this->commit(); + return true; + } catch (\Exception $e) { + $this->rollback(); + return false; + } + } +} \ No newline at end of file diff --git a/app/manager/view/account/codes.html b/app/manager/view/account/codes.html new file mode 100644 index 0000000..0f1f5b9 --- /dev/null +++ b/app/manager/view/account/codes.html @@ -0,0 +1,123 @@ +{extend name="../../admin/view/main"} + +{block name="button"} + +{/block} + +{block name="content"} +
+ + + + {notempty name='list'} + + + + + + + + + + + + + + + + + + + {/notempty} + + {foreach $list as $key=>$vo} + + + + + + + + + + + + + + + + + {/foreach} + +
+ + 激活码有效天数代理商代理级别销售价格佣金上级佣金结算状态使用状态使用时间使用者生成时间
+ + {$vo.code}{$vo.days} 天{$vo.agent_name|default='--'} + {if $vo.agent_level eq 1} + 一级代理 + {elseif $vo.agent_level eq 2} + 二级代理 + {else} + -- + {/if} + {$vo.price|default='--'}{$vo.commission|default='--'}{$vo.parent_commission|default='--'} + {if isset($vo.settle_status)} + {if $vo.settle_status eq 1} + 已结算 + {else} + 未结算 + {/if} + {else} + -- + {/if} + + {if $vo.is_used eq 1} + 已使用 + {else} + 未使用 + {/if} + {$vo.used_at|format_datetime|default='--'}{$vo.used_by|default='--'}{$vo.created_at|format_datetime} + {if auth("removeCode")} + 删 除 + {/if} +
+ {empty name='list'}没有记录哦{else}{$pagehtml|raw|default=''}{/empty} +
+{/block} \ No newline at end of file diff --git a/app/manager/view/account/devices.html b/app/manager/view/account/devices.html new file mode 100644 index 0000000..425dba4 --- /dev/null +++ b/app/manager/view/account/devices.html @@ -0,0 +1,75 @@ +{extend name="../../admin/view/main"} + +{block name="content"} +
+ + + + {notempty name='list'} + + + + + + + + + + + + {/notempty} + + {foreach $list as $key=>$vo} + + + + + + + + + + {/foreach} + +
账号邮箱设备ID使用时间注册时间缓存状态冷却状态操作
{$vo.email}{$vo.used_by}{$vo.used_at|format_datetime}{$vo.registration_time|format_datetime} + {if $vo.has_cache} + {if $vo.cache_match} + 正常 + {else} + 异常(缓存不匹配) + {/if} + {else} + 无缓存 + {/if} + + {if $vo.in_cooldown} + 冷却中 (至 {$vo.cooldown_time}) + {else} + 可用 + {/if} + + {if auth("resetDevice")} + 重置账号 + {/if} +
+ {empty name='list'}没有记录哦{else}{$pagehtml|raw|default=''}{/empty} +
+{/block} \ No newline at end of file diff --git a/app/manager/view/account/generate.html b/app/manager/view/account/generate.html new file mode 100644 index 0000000..c5e18f0 --- /dev/null +++ b/app/manager/view/account/generate.html @@ -0,0 +1,57 @@ +{extend name="../../admin/view/main"} + +{block name="content"} +
+
+
+ +
+ + 一次最多可生成100个激活码 +
+
+
+ +
+ +
+
+
+ +
+ + 如果选择代理商,需要设置销售价格 +
+
+ +
+
+
+ + +
+
+ + +{/block} \ No newline at end of file diff --git a/app/manager/view/account/index.html b/app/manager/view/account/index.html new file mode 100644 index 0000000..29cdaff --- /dev/null +++ b/app/manager/view/account/index.html @@ -0,0 +1,86 @@ +{extend name="../../admin/view/main"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
+ + + + {notempty name='list'} + + + + + + + + + + + + + + {/notempty} + + {foreach $list as $key=>$vo} + + + + + + + + + + + + {/foreach} + +
+ + 邮箱账号姓名机器ID注册时间使用状态使用时间使用者
+ + {$vo.email|default=''}{$vo.first_name|default=''} {$vo.last_name|default=''}{$vo.machine_id|default=''}{$vo.registration_time|format_datetime} + {if $vo.is_used eq 1} + 已使用 + {else} + 未使用 + {/if} + {$vo.used_at|format_datetime|default='--'}{$vo.used_by|default='--'} + {if auth("state") and $vo.is_used eq 1} + 标记未使用 + {/if} + {if auth("remove")} + 删 除 + {/if} +
+ {empty name='list'}没有记录哦{else}{$pagehtml|raw|default=''}{/empty} +
+{/block} \ No newline at end of file diff --git a/app/manager/view/agent/add.html b/app/manager/view/agent/add.html new file mode 100644 index 0000000..470d744 --- /dev/null +++ b/app/manager/view/agent/add.html @@ -0,0 +1,93 @@ +{extend name="../../admin/view/main"} + +{block name="content"} +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ + + +
+ +
+ + 请输入0-100之间的数字,表示佣金百分比 +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ + +
+
+ + +{/block} \ No newline at end of file diff --git a/app/manager/view/agent/assign_codes.html b/app/manager/view/agent/assign_codes.html new file mode 100644 index 0000000..e45e74d --- /dev/null +++ b/app/manager/view/agent/assign_codes.html @@ -0,0 +1,63 @@ +{extend name="../../admin/view/main"} + +{block name="content"} +
+
+
+ +
+ +
+
+ +
+ +
+ + 设置激活码的销售价格,用于计算佣金 +
+
+ +
+ +
+ + + + + + + + + + + {foreach $codes as $code} + + + + + + + {/foreach} + +
+ + 激活码有效天数生成时间
+ + {$code.code}{$code.days} 天{$code.created_at}
+
+
+ +
+
+
+ + +
+
+{/block} \ No newline at end of file diff --git a/app/manager/view/agent/codes.html b/app/manager/view/agent/codes.html new file mode 100644 index 0000000..7551c0b --- /dev/null +++ b/app/manager/view/agent/codes.html @@ -0,0 +1,103 @@ +{extend name="../../admin/view/main"} + +{block name="content"} +
+ + + + {notempty name='list'} + + + + + + + + + + + + + + + + + {/notempty} + + {foreach $list as $key=>$vo} + + + + + + + + + + + + + + + {/foreach} + +
激活码代理商有效天数销售价格佣金上级佣金使用状态使用时间使用者结算状态分配时间
{$vo.code}{$vo.agent_name}{$vo.days} 天{$vo.price}{$vo.commission}{$vo.parent_commission|default='--'} + {if $vo.is_used eq 1} + 已使用 + {else} + 未使用 + {/if} + {$vo.used_at|format_datetime|default='--'}{$vo.used_by|default='--'} + {if $vo.status eq 1} + 已结算 + {else} + 未结算 + {/if} + {$vo.created_at|format_datetime} + {if auth("settle") and $vo.status eq 0 and $vo.is_used eq 1} + 结 算 + {/if} +
+ {empty name='list'}没有记录哦{else}{$pagehtml|raw|default=''}{/empty} +
+{/block} \ No newline at end of file diff --git a/app/manager/view/agent/form.html b/app/manager/view/agent/form.html new file mode 100644 index 0000000..ee94aec --- /dev/null +++ b/app/manager/view/agent/form.html @@ -0,0 +1,102 @@ +{extend name="../../admin/view/main"} + +{block name="content"} +
+
+
+ +
+ +
+
+ +
+ +
+ + 编辑时不填写表示不修改密码 +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ + + +
+ +
+ + 请输入0-100之间的数字,表示佣金百分比 +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ {notempty name='vo.id'}{/notempty} + + +
+
+ + +{/block} \ No newline at end of file diff --git a/app/manager/view/agent/index.html b/app/manager/view/agent/index.html new file mode 100644 index 0000000..fe8088d --- /dev/null +++ b/app/manager/view/agent/index.html @@ -0,0 +1,123 @@ +{extend name="../../admin/view/main"} + +{block name="button"} + + + + + + +{/block} + +{block name="content"} +
+ + + + {notempty name='list'} + + + + + + + + + + + + + + + + {/notempty} + + {foreach $list as $key=>$vo} + + + + + + + + + + + + + + {/foreach} + +
+ + 用户名昵称代理级别上级代理佣金比例余额总收入状态最后登录
+ + {$vo.username}{$vo.nickname|default='--'} + {if $vo.level eq 1} + 一级代理 + {else} + 二级代理 + {/if} + + {if $vo.parent_id} + {$vo.parent_name|default='--'} + {else} + -- + {/if} + {$vo.commission_rate}%{$vo.balance}{$vo.total_income} + {if $vo.status eq 1} + 正常 + {else} + 禁用 + {/if} + {$vo.last_login_time|format_datetime|default='--'} + {if auth("edit")} + 编 辑 + {/if} + + {if auth("state") and $vo.status eq 1} + 禁 用 + {elseif auth("state") and $vo.status eq 0} + 启 用 + {/if} + + {if auth("remove")} + 删 除 + {/if} +
+ {empty name='list'}没有记录哦{else}{$pagehtml|raw|default=''}{/empty} +
+{/block} \ No newline at end of file diff --git a/database/migrations/20240212001_agents.php b/database/migrations/20240212001_agents.php new file mode 100644 index 0000000..4820f34 --- /dev/null +++ b/database/migrations/20240212001_agents.php @@ -0,0 +1,31 @@ +table('cursor_agents', ['engine' => 'InnoDB']); + $table->addColumn('username', 'string', ['limit' => 50, 'null' => false, 'comment' => '代理商用户名']) + ->addColumn('password', 'string', ['limit' => 255, 'null' => false, 'comment' => '密码']) + ->addColumn('nickname', 'string', ['limit' => 50, 'null' => true, 'comment' => '昵称']) + ->addColumn('parent_id', 'integer', ['null' => true, 'default' => 0, 'comment' => '上级代理ID,0表示一级代理']) + ->addColumn('level', 'integer', ['limit' => 1, 'null' => false, 'default' => 1, 'comment' => '代理级别:1=一级代理,2=二级代理']) + ->addColumn('balance', 'decimal', ['precision' => 10, 'scale' => 2, 'default' => '0.00', 'comment' => '余额']) + ->addColumn('total_income', 'decimal', ['precision' => 10, 'scale' => 2, 'default' => '0.00', 'comment' => '总收入']) + ->addColumn('commission_rate', 'decimal', ['precision' => 5, 'scale' => 2, 'default' => '0.00', 'comment' => '佣金比例']) + ->addColumn('status', 'integer', ['limit' => 1, 'null' => false, 'default' => 1, 'comment' => '状态:0=禁用,1=启用']) + ->addColumn('remark', 'string', ['limit' => 255, 'null' => true, 'comment' => '备注']) + ->addColumn('last_login_time', 'datetime', ['null' => true, 'comment' => '最后登录时间']) + ->addColumn('last_login_ip', 'string', ['limit' => 50, 'null' => true, 'comment' => '最后登录IP']) + ->addColumn('created_at', 'datetime', ['null' => false, 'comment' => '创建时间']) + ->addColumn('updated_at', 'datetime', ['null' => false, 'comment' => '更新时间']) + ->addIndex(['username'], ['unique' => true]) + ->addIndex(['parent_id']) + ->addIndex(['level']) + ->addIndex(['status']) + ->create(); + } +} \ No newline at end of file diff --git a/database/migrations/20240212002_agent_codes.php b/database/migrations/20240212002_agent_codes.php new file mode 100644 index 0000000..1217f99 --- /dev/null +++ b/database/migrations/20240212002_agent_codes.php @@ -0,0 +1,25 @@ +table('cursor_agent_codes', ['engine' => 'InnoDB']); + $table->addColumn('agent_id', 'integer', ['null' => false, 'comment' => '代理商ID']) + ->addColumn('code_id', 'integer', ['null' => false, 'comment' => '激活码ID']) + ->addColumn('price', 'decimal', ['precision' => 10, 'scale' => 2, 'default' => '0.00', 'comment' => '销售价格']) + ->addColumn('commission', 'decimal', ['precision' => 10, 'scale' => 2, 'default' => '0.00', 'comment' => '佣金']) + ->addColumn('parent_commission', 'decimal', ['precision' => 10, 'scale' => 2, 'default' => '0.00', 'comment' => '上级佣金']) + ->addColumn('status', 'integer', ['limit' => 1, 'null' => false, 'default' => 0, 'comment' => '状态:0=未结算,1=已结算']) + ->addColumn('settle_time', 'datetime', ['null' => true, 'comment' => '结算时间']) + ->addColumn('created_at', 'datetime', ['null' => false, 'comment' => '创建时间']) + ->addColumn('updated_at', 'datetime', ['null' => false, 'comment' => '更新时间']) + ->addIndex(['agent_id']) + ->addIndex(['code_id']) + ->addIndex(['status']) + ->create(); + } +} \ No newline at end of file