diff --git a/app/admin/controller/Config.php b/app/admin/controller/Config.php index 6e16967..b5c4a7b 100644 --- a/app/admin/controller/Config.php +++ b/app/admin/controller/Config.php @@ -56,22 +56,51 @@ class Config extends Controller */ public function index() { - $this->title = '系统参数配置'; - $this->files = Storage::types(); - $this->plugins = Plugin::get(null, true); - $this->issuper = AdminService::isSuper(); - $this->systemid = ModuleService::getRunVar('uni'); - $this->framework = ModuleService::getLibrarys('topthink/framework'); - $this->thinkadmin = ModuleService::getLibrarys('zoujingli/think-library'); - if (AdminService::isSuper() && $this->app->session->get('user.password') === md5('admin')) { - $url = url('admin/index/pass', ['id' => AdminService::getUserId()]); - $this->showErrorMessage = lang("超级管理员账号的密码未修改,建议立即修改密码!", [$url]); + if ($this->request->isGet()) { + $this->title = '系统参数配置'; + $this->files = Storage::types(); + $this->plugins = Plugin::get(null, true); + $this->issuper = AdminService::isSuper(); + $this->systemid = ModuleService::getRunVar('uni'); + $this->framework = ModuleService::getLibrarys('topthink/framework'); + $this->thinkadmin = ModuleService::getLibrarys('zoujingli/think-library'); + if (AdminService::isSuper() && $this->app->session->get('user.password') === md5('admin')) { + $url = url('admin/index/pass', ['id' => AdminService::getUserId()]); + $this->showErrorMessage = lang("超级管理员账号的密码未修改,建议立即修改密码!", [$url]); + } + uasort($this->plugins, static function ($a, $b) { + if ($a['space'] === $b['space']) return 0; + return $a['space'] > $b['space'] ? 1 : -1; + }); + $this->fetch(); + } else { + $data = $this->request->post(); + foreach ($data as $k => $v) { + sysconf($k, $v); + } + $this->success('参数修改成功!'); } - uasort($this->plugins, static function ($a, $b) { - if ($a['space'] === $b['space']) return 0; - return $a['space'] > $b['space'] ? 1 : -1; - }); - $this->fetch(); + } + + /** + * 获取系统配置参数 + * @param string $name 配置名称 + * @return mixed + */ + public function get($name) + { + return sysconf($name); + } + + /** + * 修改系统配置参数 + * @param string $name 配置名称 + * @param mixed $value 配置值 + * @return mixed + */ + public function set($name, $value) + { + return sysconf($name, $value); } /** diff --git a/app/admin/controller/api/Account.php b/app/admin/controller/api/Account.php index d2ac1c5..f96bd77 100644 --- a/app/admin/controller/api/Account.php +++ b/app/admin/controller/api/Account.php @@ -82,18 +82,48 @@ class Account extends Controller ]); } - // 检查设备是否在冷却期 - $cooldownKey = "device_cooldown_{$machineId}"; - $isInCooldown = \think\facade\Cache::get($cooldownKey); + // 优先检查缓存中是否有该设备最近使用的账号 + $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分钟缓存 + + // 查询设备激活状态用于计算过期时间 + $activations = Db::name('cursor_activation_codes') + ->where('used_by', '=', $machineId) + ->where('is_used', '=', 1) + ->order('used_at desc') + ->select() + ->toArray(); - if ($isInCooldown) { - return json([ - 'code' => 429, - 'msg' => '请求过于频繁,请稍后再试', - 'data' => [ - 'cooldown_expires' => date('Y-m-d H:i:s', $isInCooldown) - ] - ]); + if (!empty($activations)) { + $totalDays = array_sum(array_column($activations, 'days')); + $firstActivationTime = strtotime($activations[count($activations)-1]['used_at']); + $expireTime = $firstActivationTime + ($totalDays * 24 * 3600); + + // 返回账号信息 + 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) + ] + ]); + } + } } // 查询设备激活状态 @@ -124,39 +154,18 @@ class Account extends Controller ]); } - // 检查缓存中是否有该设备最近使用的账号 - $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) - ] - ]); - } + // 在分配新账号前检查冷却期 + $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', intval($isInCooldown)) + ] + ]); } - - // 记录冷却期 - $cooldownExpires = time() + 1800; // 30分钟冷却期 - \think\facade\Cache::set($cooldownKey, $cooldownExpires, 1800); // 开启事务 Db::startTrans(); @@ -195,6 +204,10 @@ class Account extends Controller 'used_by' => $machineId ]); + // 设置冷却期(仅在成功分配新账号时) + $cooldownExpires = time() + 1800; // 30分钟冷却期 + \think\facade\Cache::set($cooldownKey, $cooldownExpires, 1800); + Db::commit(); // 缓存新账号信息 diff --git a/app/admin/controller/api/Mail.php b/app/admin/controller/api/Mail.php index a6e75db..6de2d8e 100644 --- a/app/admin/controller/api/Mail.php +++ b/app/admin/controller/api/Mail.php @@ -45,6 +45,17 @@ 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' => '备用配置3', + 'config' => [ + 'DOMAIN' => 'jxyweb.site', + 'TEMP_MAIL' => 'exvet', + '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', + 'TEMP_MAIL_EPIN'=>'889944' + ] ] ]; diff --git a/app/admin/controller/api/Member.php b/app/admin/controller/api/Member.php index 3489061..6d9004e 100644 --- a/app/admin/controller/api/Member.php +++ b/app/admin/controller/api/Member.php @@ -205,16 +205,31 @@ class Member extends Controller return json(['code' => 400, 'msg' => '设备ID不能为空']); } + try { + // 获取Redis实例 + $redis = cache('redis'); + $cacheKey = "device_status:{$machineId}"; + + // 尝试获取缓存 + if ($redis && $statusInfo = $redis->get($cacheKey)) { + return json(json_decode($statusInfo, true)); + } + } catch (\Exception $e) { + // Redis异常时记录日志但继续执行 + trace("Redis异常: " . $e->getMessage(), 'error'); + } + // 获取该设备所有的激活记录 $activations = Db::name('cursor_activation_codes') ->where('used_by', '=', $machineId) ->where('is_used', '=', 1) + ->field(['days', 'used_at', 'code', 'device_info']) ->order('used_at desc') ->select() ->toArray(); if (empty($activations)) { - return json([ + $result = [ 'code' => 401, 'msg' => '未激活', 'data' => [ @@ -224,7 +239,16 @@ class Member extends Controller 'days_left' => 0, 'activation_records' => [] ] - ]); + ]; + // 未激活状态缓存1分钟 + try { + if ($redis) { + $redis->set($cacheKey, json_encode($result), 60); + } + } catch (\Exception $e) { + trace("Redis缓存写入异常: " . $e->getMessage(), 'error'); + } + return json($result); } // 计算总天数 @@ -232,13 +256,13 @@ class Member extends Controller // 计算最终到期时间 $now = time(); - $firstActivationTime = strtotime($activations[count($activations)-1]['used_at']); // 第一次激活时间 - $expireTime = $firstActivationTime + ($totalDays * 24 * 3600); // 总天数转换为秒数 + $firstActivationTime = strtotime($activations[count($activations)-1]['used_at']); + $expireTime = $firstActivationTime + ($totalDays * 24 * 3600); $daysLeft = ceil(($expireTime - $now) / 86400); // 判断是否过期 if ($daysLeft <= 0) { - return json([ + $result = [ 'code' => 401, 'msg' => '已过期', 'data' => [ @@ -256,11 +280,20 @@ class Member extends Controller ]; }, $activations) ] - ]); + ]; + // 过期状态缓存1分钟 + try { + if ($redis) { + $redis->set($cacheKey, json_encode($result), 60); + } + } catch (\Exception $e) { + trace("Redis缓存写入异常: " . $e->getMessage(), 'error'); + } + return json($result); } - // 返回正常状态 - return json([ + // 正常状态返回 + $result = [ 'code' => 200, 'msg' => '正常', 'data' => [ @@ -278,12 +311,21 @@ class Member extends Controller ]; }, $activations) ] - ]); - + ]; + + // 正常状态缓存5分钟 + try { + if ($redis) { + $redis->set($cacheKey, json_encode($result), 300); + } + } catch (\Exception $e) { + trace("Redis缓存写入异常: " . $e->getMessage(), 'error'); + } + return json($result); } catch (\Exception $e) { return json([ 'code' => 500, - 'msg' => $e->getMessage() + 'msg' => '系统异常,请稍后重试' ]); } } diff --git a/app/admin/controller/api/Version.php b/app/admin/controller/api/Version.php new file mode 100644 index 0000000..f296f6f --- /dev/null +++ b/app/admin/controller/api/Version.php @@ -0,0 +1,137 @@ +request->param('platform', 'all'); + $version = VersionModel::mk()->getLatestVersion($platform); + if (!$version) { + $this->error('暂无版本信息'); + } + + // 处理下载地址 + $version['download_url'] = $this->getFullUrl($version['download_url']); + $this->success('获取成功', $version); + } + + /** + * 检查更新 + * @return void + */ + public function check() + { + $currentVersion = $this->request->param('version'); + $platform = $this->request->param('platform', 'all'); + + if (empty($currentVersion)) { + $this->error('请提供当前版本号'); + } + + $result = VersionModel::mk()->checkUpdate($currentVersion, $platform); + if (isset($result['error'])) { + $this->error($result['error']); + } + + // 如果有更新,处理下载地址 + if ($result['has_update'] && isset($result['version_info'])) { + $result['version_info']['download_url'] = $this->getFullUrl($result['version_info']['download_url']); + } + + $this->success('检查完成', $result); + } + + /** + * 获取完整的下载地址 + * @param string $path 相对路径 + * @return string + */ + protected function getFullUrl($path) + { + return sysconf('site_domain') . $path; + } +} \ No newline at end of file diff --git a/app/admin/model/Version.php b/app/admin/model/Version.php new file mode 100644 index 0000000..73dce96 --- /dev/null +++ b/app/admin/model/Version.php @@ -0,0 +1,167 @@ +value('version_no'); + $currentVersion = $latest ?: '1.0.0'; + } + + // 拆分版本号 + $parts = explode('.', $currentVersion); + $major = intval($parts[0] ?? 1); + $minor = intval($parts[1] ?? 0); + $patch = intval($parts[2] ?? 0); + + // 增加修订号 + $patch++; + // 如果修订号超过99,增加次版本号 + if ($patch > 99) { + $minor++; + $patch = 0; + } + // 如果次版本号超过99,增加主版本号 + if ($minor > 99) { + $major++; + $minor = 0; + } + + return sprintf('%d.%d.%d', $major, $minor, $patch); + } + + /** + * 生成版本名称 + * @param string $version_no 版本号 + * @param string $filename 文件名 + * @return string + */ + public static function generateVersionName($version_no, $filename = '') + { + // 如果有文件名,尝试从文件名中提取有意义的部分 + if ($filename) { + // 移除扩展名 + $name = pathinfo($filename, PATHINFO_FILENAME); + // 移除版本号部分 (匹配v后面的所有数字和点) + $name = preg_replace('/v\d+[\d\.]+/', '', $name); + $name = trim($name); + if (!empty($name)) { + return $name; + } + } + + // 默认使用版本号作为名称 + return sprintf('版本 %s', $version_no); + } + + /** + * 从文件名解析版本号 + * @param string $filename + * @return string + */ + public static function parseVersionFromFilename($filename) + { + // 移除扩展名 + $name = pathinfo($filename, PATHINFO_FILENAME); + + // 匹配版本号 (匹配v后面的所有数字和点) + if (preg_match('/v(\d+[\d\.]+)/', $name, $matches)) { + return $matches[1]; + } + + // 如果没有找到版本号,使用当前最新版本号加1 + return self::getNextVersion(); + } + + /** + * 获取最新版本信息 + * @param string $platform 平台 + * @return array|null + */ + public function getLatestVersion($platform = 'all') + { + return $this->where([ + ['status', '=', self::STATUS_ENABLED], + ['platform', 'in', [$platform, 'all']] + ])->order('version_no DESC')->find(); + } + + /** + * 检查版本更新 + * @param string $currentVersion 当前版本 + * @param string $platform 平台 + * @return array + */ + public function checkUpdate($currentVersion, $platform = 'all') + { + $latest = $this->getLatestVersion($platform); + if (!$latest) { + return ['has_update' => false]; + } + + // 版本号比较 + if (version_compare($latest['version_no'], $currentVersion) > 0) { + // 检查是否满足最低版本要求 + if ($latest['min_version'] && version_compare($currentVersion, $latest['min_version']) < 0) { + return [ + 'has_update' => false, + 'error' => '当前版本过低,请先更新到' . $latest['min_version'] . '版本' + ]; + } + + return [ + 'has_update' => true, + 'is_force' => $latest['is_force'], + 'version_info' => $latest + ]; + } + + return ['has_update' => false]; + } + + /** + * 检查版本权限 + * @param int $versionId 版本ID + * @param int $targetId 目标ID + * @param string $targetType 目标类型 + * @return bool + */ + public function checkVersionPermission($versionId, $targetId, $targetType) + { + $control = \think\facade\Db::name('version_control') + ->where([ + ['version_id', '=', $versionId], + ['target_id', '=', $targetId], + ['target_type', '=', $targetType], + ['expire_time', '>', date('Y-m-d H:i:s')] + ]) + ->find(); + + if (!$control) { + return true; + } + + return $control['control_type'] === 'white_list'; + } +} \ No newline at end of file diff --git a/app/admin/view/config/index.html b/app/admin/view/config/index.html index aaa6f72..b896fc0 100644 --- a/app/admin/view/config/index.html +++ b/app/admin/view/config/index.html @@ -11,224 +11,234 @@ {/block} {block name="content"} - -
CKEditor4:{:lang('旧版本编辑器,对浏览器兼容较好,但内容编辑体验稍有不足。')}
-CKEditor5:{:lang('新版本编辑器,只支持新特性浏览器,对内容编辑体验较好,推荐使用。')}
-wangEditor:{:lang('国产优质富文本编辑器,对于小程序及App内容支持会更友好,推荐使用。')}
-{:lang('自适应模式')}:{:lang('优先使用新版本编辑器,若浏览器不支持新版本时自动降级为旧版本编辑器。')}
-{:lang('本地服务器存储')}:{:lang('文件上传到本地服务器的 `static/upload` 目录,不支持大文件上传,占用服务器磁盘空间,访问时消耗服务器带宽流量。')}
-{:lang('自建Alist存储')}:{:lang('文件上传到 Alist 存储的服务器或云存储空间,根据服务配置可支持大文件上传,不占用本身服务器空间及服务器带宽流量。')}
-{:lang('七牛云对象存储')}:{:lang('文件上传到七牛云存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}
-{:lang('又拍云USS存储')}:{:lang('文件上传到又拍云 USS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}
-{:lang('阿里云OSS存储')}:{:lang('文件上传到阿里云 OSS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}
-{:lang('腾讯云COS存储')}:{:lang('文件上传到腾讯云 COS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}
-- {:lang('公安备案号,可以在 %s 查询获取,将在登录页面下面显示。',['www.beian.gov.cn'])} -
-{:lang('开发模式')}:{:lang('开发人员或在功能调试时使用,系统异常时会显示详细的错误信息,同时还会记录操作日志及数据库 SQL 语句信息。')}
+{:lang('生产模式')}:{:lang('项目正式部署上线后使用,系统异常时统一显示 "%s",只记录重要的异常日志信息,强烈推荐上线后使用此模式。',[config('app.error_message')])}
+CKEditor4:{:lang('旧版本编辑器,对浏览器兼容较好,但内容编辑体验稍有不足。')}
+CKEditor5:{:lang('新版本编辑器,只支持新特性浏览器,对内容编辑体验较好,推荐使用。')}
+wangEditor:{:lang('国产优质富文本编辑器,对于小程序及App内容支持会更友好,推荐使用。')}
+{:lang('自适应模式')}:{:lang('优先使用新版本编辑器,若浏览器不支持新版本时自动降级为旧版本编辑器。')}
+{:lang('本地服务器存储')}:{:lang('文件上传到本地服务器的 `static/upload` 目录,不支持大文件上传,占用服务器磁盘空间,访问时消耗服务器带宽流量。')}
+{:lang('自建Alist存储')}:{:lang('文件上传到 Alist 存储的服务器或云存储空间,根据服务配置可支持大文件上传,不占用本身服务器空间及服务器带宽流量。')}
+{:lang('七牛云对象存储')}:{:lang('文件上传到七牛云存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}
+{:lang('又拍云USS存储')}:{:lang('文件上传到又拍云 USS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}
+{:lang('阿里云OSS存储')}:{:lang('文件上传到阿里云 OSS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}
+{:lang('腾讯云COS存储')}:{:lang('文件上传到腾讯云 COS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}
++ {:lang('公安备案号,可以在 %s 查询获取,将在登录页面下面显示。',['www.beian.gov.cn'])} +
+| {:lang('核心框架')} | +ThinkPHP Version {$framework.version|default='None'} | +
|---|---|
| {:lang('平台框架')} | +ThinkAdmin Version {$thinkadmin.version|default='6.0.0'} | +
| {:lang('操作系统')} | +{:php_uname()} | +
| {:lang('运行环境')} | +{:ucfirst($request->server('SERVER_SOFTWARE',php_sapi_name()))} & PHP {$Think.const.PHP_VERSION} & {:ucfirst(app()->db->connect()->getConfig('type'))} | +
| {:lang('系统序号')} | +{$systemid|default=''} | +
| {:lang('应用名称')} | +{:lang('插件名称')} | +{:lang('插件包名')} | +{:lang('插件版本')} | +{:lang('授权协议')} | +
|---|---|---|---|---|
| {$key} | +{$plugin.name|lang} | ++ {if empty($plugin.install.document)}{$plugin.package} + {else}{$plugin.package}{/if} + | +{$plugin.install.version|default='unknow'} | ++ {if empty($plugin.install.license)} - + {elseif is_array($plugin.install.license)}{$plugin.install.license|join='、',###} + {else}{$plugin.install.license|default='-'}{/if} + | +
| {:lang('核心框架')} | -ThinkPHP Version {$framework.version|default='None'} | -
|---|---|
| {:lang('平台框架')} | -ThinkAdmin Version {$thinkadmin.version|default='6.0.0'} | -
| {:lang('操作系统')} | -{:php_uname()} | -
| {:lang('运行环境')} | -{:ucfirst($request->server('SERVER_SOFTWARE',php_sapi_name()))} & PHP {$Think.const.PHP_VERSION} & {:ucfirst(app()->db->connect()->getConfig('type'))} | -
| {:lang('系统序号')} | -{$systemid|default=''} | -
| {:lang('应用名称')} | -{:lang('插件名称')} | -{:lang('插件包名')} | -{:lang('插件版本')} | -{:lang('授权协议')} | -
|---|---|---|---|---|
| {$key} | -{$plugin.name|lang} | -- {if empty($plugin.install.document)}{$plugin.package} - {else}{$plugin.package}{/if} - | -{$plugin.install.version|default='unknow'} | -- {if empty($plugin.install.license)} - - {elseif is_array($plugin.install.license)}{$plugin.install.license|join='、',###} - {else}{$plugin.install.license|default='-'}{/if} - | -
| 激活码 | -有效天数 | -销售价格 | -佣金 | -使用状态 | -使用时间 | -使用者 | -结算状态 | -生成时间 | -
|---|---|---|---|---|---|---|---|---|
| {$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} | -
| + + | +激活码 | +有效天数 | + {if $Think.admin.is_super} +代理商 | +代理级别 | + {/if} +销售价格 | +佣金 | +使用状态 | +使用时间 | +使用者 | +生成时间 | +状态 | ++ |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| + + | +{$vo.code} | +{$vo.days} 天 | + {if $Think.admin.is_super} +{$vo.agent_name|default='--'} | ++ {if $vo.agent_level eq 1} + 一级代理 + {elseif $vo.agent_level eq 2} + 二级代理 + {else} + -- + {/if} + | + {/if} +{$vo.price|default='--'} | +{$vo.commission|default='--'} | ++ {if $vo.is_used eq 1} + 已使用 + {else} + 未使用 + {/if} + | +{$vo.used_at|format_datetime|default='--'} | +{$vo.used_by|default='--'} | +{$vo.created_at|format_datetime} | ++ {if $vo.status eq 1} + 正常 + {else} + 已禁用 + {/if} + | ++ {if auth("disableCode") && $vo.status eq 1} + 禁用 + {/if} + | +