ConfigureModel::getVal('default_engine', 'baidu'), 'enable_submit' => (int)ConfigureModel::getVal('enable_submit', '1'), 'enable_hot_api' => (int)ConfigureModel::getVal('enable_hot_api', '0'), ]; } /** * 外观背景配置注入(背景图 / 遮罩 / 随机变换色), * 供前端 nav-theme.js 读取:window.NavAppearance。 */ protected function assignAppearance(): void { $this->view->assign('appearanceJson', json_encode([ 'image' => (string)ConfigureModel::getVal('bg_image', ''), 'mask' => (int)ConfigureModel::getVal('bg_mask', 35), 'random' => (int)ConfigureModel::getVal('bg_random', 0), 'festival' => (int)ConfigureModel::getVal('festival_theme', 1), ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } protected function seoConfig(): array { return [ 'title' => (string)ConfigureModel::getVal('seo_title', '网址导航 - 精选实用网站大全'), 'keywords' => (string)ConfigureModel::getVal('seo_keywords', '网址导航,常用网址,网站大全,上网导航'), 'description' => (string)ConfigureModel::getVal('seo_description', '简洁实用的网址导航,收录精选优质网站,支持分类浏览、站内搜索、热门排行与网址投稿。'), ]; } protected function assignSeo(): array { $seo = $this->seoConfig(); $this->view->assign('seo', $seo); $this->view->assign('siteUrl', $this->request->domain()); return $seo; } /* ===================== 用户 / 权限 ===================== */ protected function uid(): int { if ($this->auth && $this->auth->isLogin) { $info = $this->auth->info; if (is_array($info)) { return (int)($info['id'] ?? 0); } return (int)($info->id ?? 0); } return 0; } /* ===================== 广告位 / 点击统计 ===================== */ protected function assignAds() { $this->view->assign('ads', AdModel::allActiveBySlot()); } /** * 点击时段聚合(供后台「点击热力图」)。表结构首次访问自愈, * 写入失败静默不影响跳转。仅按 (link_id, date, hour) 聚合, * 全站每天最多 N链接×24 行,规模可控。 */ protected function recordClickStat(int $linkId, int $cid): void { $table = 'wxapp_haonav_click_stats'; try { $ymd = date('Y-m-d'); $hr = (int)date('G'); Db::execute( "INSERT INTO `$table` (`link_id`,`cid`,`date`,`hour`,`clicks`) VALUES (?,?,?,?,1) " . "ON DUPLICATE KEY UPDATE `clicks` = `clicks` + 1", [$linkId, $cid, $ymd, $hr] ); } catch (\Throwable $e) { // 统计写入失败不影响跳转 } } /* ===================== 静态资源输出(Favicon / Snapshot 共享) ===================== */ protected function serveFile(string $path) { $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION)); $map = [ 'png' => 'image/png', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'gif' => 'image/gif', 'ico' => 'image/x-icon', 'svg' => 'image/svg+xml', 'webp' => 'image/webp', ]; $ct = $map[$ext] ?? 'image/png'; $data = file_get_contents($path); return Response::create($data)->header([ 'Content-Type' => $ct, 'Cache-Control' => 'public, max-age=86400', 'Expires' => gmdate('D, d M Y H:i:s', time() + 86400) . ' GMT', ]); } protected function serveDefault() { $path = app()->getRootPath() . 'public/static/haonav/img/default.png'; if (is_file($path)) { return $this->serveFile($path); } $gif = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); return Response::create($gif)->header([ 'Content-Type' => 'image/gif', 'Cache-Control' => 'public, max-age=3600', ]); } }