查看: 21|回复: 2

便民网的一些维护记录

[复制链接]

1

主题

2

回帖

30

威望

管理员

UID
1

发表于 2026-5-19 01:08:40 | 显示全部楼层 |阅读模式

马上注册,解锁更多功能,轻松玩转便民网 zgbm.com

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖只是更新一些我在维护便民网过程中的一些记录,如果这个网站能一直发展下去,也算是网站历史的一部分。
2026-05-11,拍得zgbm.com。
2026-5-15,搭建便民网。
2026-5-19,各项功能基本设置完毕,可进入测试阶段。

目前只是测试,没有正式公开运营,后期还有很多工作要做,域名也还没有备案,域名目前还在国外,要下个月中旬才能转回国内备案。

如有志中道合者,欢迎联系我们Email:kefu@zgbm.com,谢谢。
信息真伪请自行辨别,发现虚假信息请到[便民诉求]发帖举报

1

主题

2

回帖

30

威望

管理员

UID
1

 楼主| 发表于 2026-5-22 19:35:17 | 显示全部楼层
2026-5-22,更新网站地图的页头页尾及部分细节显示样式

  1. <?php
  2. /**
  3. * Discuz! Sitemap HTML 生成计划任务(静默模式)
  4. * 文件路径: source/include/cron/sitemap_builder.php
  5. *
  6. * 功能:定时生成 Sitemap.html,供用户浏览网站结构
  7. * 特点:无任何输出,仅记录日志,符合 Discuz! Cron 规范
  8. *
  9. * @author Your Name
  10. * @version 2.4
  11. * @license GPL v3
  12. */
  13. // 防止直接访问,只能通过Discuz系统调用
  14. if (!defined('IN_DISCUZ')) {
  15.     exit('Access Denied');
  16. }

  17. // 静默运行,禁止任何输出
  18. error_reporting(0);
  19. ini_set('display_errors', 0);

  20. // ==================== 【关键配置区】====================
  21. // 🔒 版块显示控制配置
  22. //    1. $hidden_groups: 不在网站地图中显示的版块(分区),但其帖子仍会显示
  23. //    2. $excluded_from_posts: 不显示其帖子的版块,但版块本身可能仍显示在地图中
  24. // 版块控制配置
  25. $hidden_groups = [1];        // ←←← 在地图中不显示的版块ID(分区),但其帖子仍会显示
  26. $excluded_from_posts = [0];  // ←←← 不显示其帖子的版块ID,但版块本身可能仍显示在地图中

  27. $config = [
  28.     'html_filename'      => 'Sitemap.html',
  29.     'update_interval'    => 58,     // 最短更新间隔(分钟),建议每天一次
  30.     'batch_size'         => 200,    // 每批次处理的数量
  31.     'max_threads'        => 1000,   // 最大处理帖子数
  32.     'memory_limit'       => '256M', // 内存限制
  33.     'max_execution_time' => 300,    // 最大执行时间(秒)
  34.     'charset'            => 'utf-8'
  35. ];

  36. // ================================================

  37. // 设置资源限制
  38. @ini_set('memory_limit', $config['memory_limit']);
  39. @set_time_limit($config['max_execution_time']);

  40. // 获取站点时区
  41. $site_timezone = $_G['setting']['timeoffset'] ?? 8;// 默认为北京时间(UTC+8)
  42. // 强制设置PHP时区以确保一致性
  43. $timezone_map = [
  44.     -12 => 'Pacific/Kwajalein', -11 => 'Pacific/Midway', -10 => 'Pacific/Honolulu',
  45.     -9 => 'America/Anchorage', -8 => 'America/Los_Angeles', -7 => 'America/Denver',
  46.     -6 => 'America/Chicago', -5 => 'America/New_York', -4 => 'America/Halifax',
  47.     -3 => 'America/Sao_Paulo', -2 => 'Atlantic/South_Georgia', -1 => 'Atlantic/Azores',
  48.     0 => 'UTC', 1 => 'Europe/London', 2 => 'Europe/Paris', 3 => 'Europe/Moscow',
  49.     4 => 'Asia/Baku', 5 => 'Asia/Karachi', 6 => 'Asia/Dhaka', 7 => 'Asia/Jakarta',
  50.     8 => 'Asia/Shanghai', 9 => 'Asia/Tokyo', 10 => 'Australia/Brisbane',
  51.     11 => 'Pacific/Noumea', 12 => 'Pacific/Auckland'
  52. ];
  53. date_default_timezone_set($timezone_map[$site_timezone] ?? 'Asia/Shanghai');

  54. $html_filepath = DISCUZ_ROOT . '/' . $config['html_filename'];
  55. $web_root = rtrim($_G['siteurl'], '/') . '/';
  56. $site_name = htmlspecialchars($_G['setting']['sitename'] ?? 'Discuz Forum', ENT_QUOTES, $config['charset']);

  57. // 检查是否在更新冷却期内,防止频繁生成
  58. if (file_exists($html_filepath)) {
  59.     $last_modified = filemtime($html_filepath);
  60.     if ((time() - $last_modified) < ($config['update_interval'] * 60)) {
  61.         runlog('sitemap_html', 'Skipped: within update interval.');
  62.         return TRUE;
  63.     }
  64. }

  65. // 构建版块排除条件
  66. $exclude_groups_sql = '';
  67. $exclude_posts_sql = '';

  68. if (!empty($hidden_groups)) {
  69.     $group_ids = array_map('intval', array_filter($hidden_groups));
  70.     if (!empty($group_ids)) {
  71.         $exclude_groups_sql = " AND fid NOT IN (" . implode(',', $group_ids) . ") ";
  72.     }
  73. }

  74. if (!empty($excluded_from_posts)) {
  75.     $post_exclude_ids = array_map('intval', array_filter($excluded_from_posts));
  76.     if (!empty($post_exclude_ids)) {
  77.         $exclude_posts_sql = " AND fid NOT IN (" . implode(',', $post_exclude_ids) . ") ";
  78.     }
  79. }

  80. // === 修复:获取所有分区(包括status=2的隐藏分区),但排除在$hidden_groups中的版块 ===
  81. $groups_data = []; // 存储分区信息(即城市版块)
  82. $gids_for_thread = []; // 存储分区ID用于帖子查询

  83. // 获取所有公开的分区(包括隐藏分区),但排除$hidden_groups中的
  84. $query_groups = DB::query("
  85.     SELECT fid as gid, name
  86.     FROM " . DB::table('forum_forum') . "
  87.     WHERE type='group'
  88.     {$exclude_groups_sql}
  89.     ORDER BY displayorder
  90. ");
  91. while ($group = DB::fetch($query_groups)) {
  92.     $groups_data[] = $group;
  93.     $gids_for_thread[] = (int) $group['gid'];
  94. }
  95. $public_group_count = count($groups_data);

  96. // === 修复:帖子查询逻辑 - 分区版块不显示但其帖子仍显示 ===
  97. $thread_fid_condition = '';

  98. // 获取所有版块,排除$excluded_from_posts中的版块,但保留属于隐藏分区的版块的帖子
  99. $query_fids = DB::query("
  100.     SELECT fid
  101.     FROM " . DB::table('forum_forum') . "
  102.     WHERE status='1' AND type='forum'
  103.     AND fid NOT IN (" . implode(',', array_map('intval', $excluded_from_posts)) . ")
  104. ");

  105. $fids_to_include = [];
  106. while ($fid_row = DB::fetch($query_fids)) {
  107.     $fids_to_include[] = (int) $fid_row['fid'];
  108. }

  109. if (!empty($fids_to_include)) {
  110.     $thread_fid_condition = "AND fid IN (" . implode(',', $fids_to_include) . ")";
  111. } else {
  112.     $thread_fid_condition = "AND 1=0"; // 无符合条件的版块,则不查任何帖子
  113. }

  114. // 获取帖子总数(仅排除$excluded_from_posts中的版块,不考虑$hidden_groups对帖子的影响)
  115. $total_result = DB::fetch_first(
  116.     "SELECT COUNT(*) AS total FROM " . DB::table('forum_thread') .
  117.     " WHERE displayorder >= 0 AND lastpost > 0 {$thread_fid_condition}"
  118. );
  119. $total_threads = (int)$total_result['total'];

  120. // 若无有效内容,提前退出
  121. if ($total_threads <= 0 && $public_group_count <= 0) {
  122.     runlog('sitemap_html', 'No valid threads or groups found for Sitemap.html.');
  123.     return TRUE;
  124. }

  125. // 分批读取数据(含 subject)
  126. $threads_data = [];
  127. $offset = 0;
  128. $processed = 0;

  129. do {
  130.     $query = DB::query(
  131.         "SELECT tid, lastpost, subject, replies, fid FROM " . DB::table('forum_thread') .
  132.         " WHERE displayorder >= 0 AND lastpost > 0 {$thread_fid_condition} " .
  133.         "ORDER BY lastpost DESC LIMIT {$offset}, {$config['batch_size']}"
  134.     );
  135.     $batch_count = 0;
  136.     while ($thread = DB::fetch($query)) {
  137.         // 确保 subject 安全
  138.         $thread['subject'] = dhtmlspecialchars($thread['subject']);
  139.         $threads_data[] = $thread;
  140.         $batch_count++;
  141.         $processed++;
  142.         if ($processed >= $config['max_threads']) break;
  143.     }
  144.     $offset += $config['batch_size'];
  145.     if ($batch_count < $config['batch_size']) break;
  146.     if ($processed >= $config['max_threads']) break;
  147. } while ($batch_count == $config['batch_size']);

  148. if (empty($threads_data) && empty($groups_data)) {
  149.     runlog('sitemap_html', 'No thread or group data retrieved for HTML sitemap.');
  150.     return TRUE;
  151. }

  152. // 开始写入 HTML
  153. $html_file = @fopen($html_filepath, 'w');
  154. if (!$html_file) {
  155.     runlog('sitemap_html', 'Failed to open HTML file for writing: ' . $html_filepath);
  156.     return FALSE;
  157. }

  158. $generation_time = date('Y-m-d H:i:s');

  159. // HTML 头部
  160. $html_header = '<!DOCTYPE html>
  161. <html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
  162. <head>
  163.     <meta charset="' . $config['charset'] . '">
  164.     <title>网站地图 - ' . $site_name . '</title>
  165.     <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  166.     <meta name="robots" content="noindex,follow">
  167.     <style>
  168. /* 原有样式 */
  169. body{
  170.     font-family: Arial, sans-serif;
  171.     margin: 0;
  172.     padding: 0;
  173.     background: #f5f5f5;
  174.     line-height: 1.4; /* 调整行高 */
  175. }
  176. .container{
  177.     max-width: 100%;
  178.     width: 100%;
  179.     margin: 0 auto;
  180.     background: #fff;
  181.     padding: 15px;
  182.     box-sizing: border-box;
  183. }
  184. @media (min-width: 768px) {
  185.     .container {
  186.         max-width: 1200px;
  187.         padding: 30px;
  188.         border-radius: 8px;
  189.         box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  190.         margin: 20px auto;
  191.     }
  192. }
  193. h1{
  194.     text-align: center;
  195.     color: #333;
  196.     margin-top: 0;
  197.     font-size: 1.8rem;
  198.     display: none; /* 隐藏主标题 */
  199. }
  200. @media (min-width: 768px) {
  201.     h1 {
  202.         font-size: 2.2rem;
  203.     }
  204. }
  205. h2{
  206.     color: #007cba;
  207.     margin-top: 30px;
  208.     font-size: 1.4rem;
  209.     line-height: 1.4; /* 调整行高 */
  210. }
  211. @media (min-width: 768px) {
  212.     h2 {
  213.         font-size: 1.6rem;
  214.     }
  215. }
  216. h3{
  217.     color: #555;
  218.     margin-top: 20px;
  219.     margin-bottom: 10px;
  220.     font-size: 1.2rem;
  221.     line-height: 1.4; /* 调整行高 */
  222. }
  223. ul{
  224.     list-style: none;
  225.     padding: 0;
  226.     margin: 0;
  227. }
  228. li{
  229.     margin: 5px 0; /* 减小间距 */
  230.     padding: 8px 12px; /* 减小内边距 */
  231.     background: #f9f9f9;
  232.     border-left: 4px solid #007cba;
  233.     border-radius: 4px;
  234.     word-wrap: break-word;
  235.     overflow-wrap: break-word;
  236.     line-height: 1.4; /* 调整行高 */
  237. }
  238. a{
  239.     color: #007cba;
  240.     text-decoration: none;
  241.     font-weight: bold;
  242.     word-break: break-all;
  243.     display: inline; /* 改为inline以便时间和链接在同一行 */
  244. }
  245. a:hover{
  246.     text-decoration: underline;
  247. }
  248. .timestamp{
  249.     color: #666;
  250.     font-size: 0.85em;
  251.     margin-left: 8px; /* 减小间距 */
  252.     display: inline; /* 与链接同行 */
  253. }
  254. @media (min-width: 768px) {
  255.     .timestamp {
  256.         margin-left: 10px;
  257.     }
  258. }
  259. .hot{
  260.     border-left-color: #ff6b6b;
  261.     background: #fff5f5;
  262. }
  263. .recent{
  264.     border-left-color: #4ecdc4;
  265. }
  266. .stats{
  267.     background: #e8f4fd;
  268.     padding: 12px;
  269.     border-radius: 5px;
  270.     margin: 20px 0;
  271.     font-size: 0.9rem;
  272.     line-height: 1.4; /* 调整行高 */
  273. }
  274. @media (min-width: 768px) {
  275.     .stats {
  276.         font-size: 1rem;
  277.     }
  278. }
  279. /* 修复换行问题:确保统计信息在一行显示 */
  280. .stats p {
  281.     margin: 6px 0; /* 减小间距 */
  282.     white-space: nowrap; /* 防止内容换行 */
  283.     overflow: hidden;    /* 隐藏溢出内容 */
  284.     text-overflow: ellipsis; /* 超出部分显示省略号 */
  285. }
  286. /* 针对链接的特殊处理 */
  287. .stats a {
  288.     display: inline !important; /* 确保链接为行内元素 */
  289.     white-space: nowrap; /* 防止链接文字换行 */
  290. }
  291. .group-section{
  292.     margin-bottom: 30px;
  293. }
  294. .footer-text{
  295.     margin-top: 40px;
  296.     text-align: center;
  297.     color: #666;
  298.     font-size: 0.85rem;
  299.     padding: 20px 0 10px;
  300.     line-height: 1.4; /* 调整行高 */
  301. }
  302. @media (min-width: 768px) {
  303.     .footer-text {
  304.         font-size: 0.9rem;
  305.     }
  306. }
  307. /* 提示信息样式 */
  308. .city-hint {
  309.     color: #666;
  310.     font-style: italic;
  311.     margin-top: -10px;
  312.     margin-bottom: 15px;
  313.     font-size: 0.9em;
  314.     display: inline-block; /* 改为行内块元素 */
  315.     line-height: 1.4; /* 调整行高 */
  316. }
  317. /* 便民诉求链接样式 */
  318. .city-hint a {
  319.     display: inline; /* 确保链接为行内元素 */
  320.     white-space: nowrap; /* 防止链接换行 */
  321. }
  322. /* 修复帖子列表布局 - 调整为紧凑样式 */
  323. .thread-item {
  324.     display: inline; /* 与时间在同一行 */
  325. }
  326. .thread-title {
  327.     display: inline; /* 与时间在同一行 */
  328. }
  329. .thread-timestamp {
  330.     white-space: nowrap; /* 时间不换行 */
  331.     color: #666;
  332.     font-size: 0.85em;
  333. }

  334. /* 页头样式 */
  335. .header {
  336.     background: #0033FF; /* 修改为指定颜色 */
  337.     border-bottom: 1px solid #e5e5e5;
  338.     padding: 15px 0;
  339.     box-shadow: 0 2px 4px rgba(0,0,0,0.05);
  340. }
  341. .header-content {
  342.     max-width: 1200px;
  343.     margin: 0 auto;
  344.     padding: 0 20px;
  345.     display: flex;
  346.     justify-content: space-between;
  347.     align-items: center;
  348.     flex-wrap: wrap;
  349. }
  350. .logo-area {
  351.     display: flex;
  352.     align-items: center;
  353.     gap: 15px;
  354. }
  355. .logo {
  356.     font-size: 1.8rem;
  357.     font-weight: bold;
  358.     text-decoration: none;
  359.     color: #ffffff; /* 白色文字 */
  360. }
  361. .site-name {
  362.     font-size: 1.2rem;
  363.     color: #ffffff; /* 白色文字 */
  364. }
  365. .nav-main {
  366.     display: flex;
  367.     gap: 25px;
  368.     list-style: none;
  369.     margin: 0;
  370.     padding: 0;
  371.     align-items: center;
  372.     justify-content: flex-start; /* 导航居左显示 */
  373. }
  374. .nav-main a {
  375.     color: #ffffff; /* 白色导航文字 */
  376.     text-decoration: none;
  377.     font-weight: 500;
  378.     padding: 8px 12px;
  379.     border-radius: 4px;
  380.     transition: all 0.3s ease;
  381. }
  382. .nav-main a:hover {
  383.     background-color: #0022cc; /* 深蓝色悬停效果 */
  384.     color: #ffffff;
  385. }

  386. /* 页脚样式 */
  387. .footer {
  388.     background: #2c3e50;
  389.     color: #ecf0f1;
  390.     padding: 10px 0 5px; /* 进一步减少上下内边距 */
  391.     margin-top: 15px; /* 减少页脚与内容的距离 */
  392. }
  393. .footer-content {
  394.     max-width: 1200px;
  395.     margin: 0 auto;
  396.     padding: 0 20px;
  397. }
  398. .footer-top {
  399.     display: grid;
  400.     grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  401.     gap: 20px; /* 减少间距 */
  402.     margin-bottom: 10px; /* 减少底部外边距 */
  403. }
  404. .footer-column h4 {
  405.     color: #ecf0f1;
  406.     margin-bottom: 10px; /* 减少底部外边距 */
  407.     font-size: 1.1rem;
  408. }
  409. .footer-column ul {
  410.     list-style: none;
  411.     padding: 0;
  412.     margin: 0;
  413. }
  414. .footer-column li {
  415.     margin-bottom: 5px; /* 减少底部外边距 */
  416. }
  417. .footer-column a {
  418.     color: #bdc3c7;
  419.     text-decoration: none;
  420.     transition: color 0.3s ease;
  421. }
  422. .footer-column a:hover {
  423.     color: #3498db;
  424. }
  425. .footer-bottom {
  426.     padding-top: 5px; /* 最小化上内边距,只保留一行文字的空间 */
  427.     text-align: center;
  428.     border-top: none; /* 确保没有顶部边框 */
  429. }
  430. .copyright {
  431.     color: #95a5a6;
  432.     font-size: 0.9rem;
  433.     line-height: 1.4; /* 进一步调整行高 */
  434.     margin: 0; /* 移除默认外边距 */
  435. }
  436. .copyright p {
  437.     margin: 3px 0; /* 最小化段落间距 */
  438. }
  439. .copyright a {
  440.     color: #3498db;
  441.     text-decoration: none;
  442. }
  443. .copyright a:hover {
  444.     text-decoration: underline;
  445. }

  446. /* 响应式设计 */
  447. @media (max-width: 768px) {
  448.     .header-content {
  449.         flex-direction: column;
  450.         gap: 15px;
  451.         text-align: center;
  452.     }
  453.     .nav-main {
  454.         flex-wrap: wrap;
  455.         justify-content: flex-start; /* 移动端也保持居左 */
  456.         gap: 10px;
  457.     }
  458.     .footer-top {
  459.         grid-template-columns: 1fr;
  460.         gap: 15px; /* 移动端进一步减少间距 */
  461.     }
  462.     .logo-area {
  463.                 display: flex;
  464.         flex-direction: row;
  465.     }
  466.     .footer-bottom {
  467.         padding-top: 4px; /* 移动端进一步减少上内边距 */
  468.     }
  469. }
  470.     </style>
  471. </head>
  472. <body>
  473. <!-- 页头 -->
  474. <header class="header">
  475.     <div class="header-content">
  476.         <div class="logo-area">
  477.             <a href="/" class="logo">' . $site_name . '</a>
  478.             <span class="site-name">网站地图</span>
  479.         </div>
  480.     </div>
  481. </header>

  482. <div class="container">';

  483. fwrite($html_file, $html_header);

  484. // 首页
  485. fwrite($html_file, "<div style="display: flex; align-items: center; gap: 20px;">\n");
  486. fwrite($html_file, "  <h2 style="margin:0;">首页</h2>\n");
  487. fwrite($html_file, "  <ul style="display: flex; gap: 15px; list-style: none; padding: 0; margin: 0;">\n");

  488. // 导航链接
  489. fwrite($html_file, "    <li><a href="" . htmlspecialchars($web_root, ENT_QUOTES, $config['charset']) . "">{$site_name}</a></li>\n");
  490. fwrite($html_file, "    <li><a href="2-1.html">便民诉求</a></li>\n");

  491. fwrite($html_file, "  </ul>\n");
  492. fwrite($html_file, "</div>\n");



  493. // === 修复:显示分区列表(即城市版块),包括隐藏分区,使用静态链接格式 gid-1.html ===
  494. if (!empty($groups_data)) {
  495.     fwrite($html_file, "<h2>城市版块</h2>\n");
  496.     fwrite($html_file, "<div class="city-hint">如果没有您的城市,请到<a href="2-1.html" target="_blank">便民诉求</a>发帖申请!</div>\n<ul>\n");
  497.     foreach ($groups_data as $group) {
  498.         // 使用静态链接格式 gid-1.html
  499.         $group_url = $web_root . $group['gid'] . '-1.html';
  500.         
  501.         $entry = "\t<li>\n" .
  502.                  "\t\t<a href="" . htmlspecialchars($group_url, ENT_QUOTES, $config['charset']) . "" target="_blank">" . htmlspecialchars($group['name'], ENT_QUOTES, $config['charset']) . "</a>\n" .
  503.                  "\t</li>\n";
  504.         fwrite($html_file, $entry);
  505.     }
  506.     fwrite($html_file, "</ul>\n");
  507. }
  508. // === 修复结束 ===

  509. // 帖子列表
  510. fwrite($html_file, "<h2>最新帖子</h2>\n<ul>\n");
  511. foreach ($threads_data as $thread) {
  512.     $url = $web_root . $thread['tid'] . '-1-1.html';
  513.     $update_time = date('Y-m-d H:i', $thread['lastpost']);
  514.     $hours = (time() - $thread['lastpost']) / 3600;

  515.     $classes = [];
  516.     if ($thread['replies'] > 50) $classes[] = 'hot';
  517.     if ($hours < 24) $classes[] = 'recent';
  518.     $class_attr = $classes ? ' class="' . implode(' ', $classes) . '"' : '';

  519.     $entry = "\t<li{$class_attr}>\n" .
  520.              "\t\t<div class="thread-item">\n" .
  521.              "\t\t\t<a href="" . htmlspecialchars($url, ENT_QUOTES, $config['charset']) . "" target="_blank" class="thread-title">" . $thread['subject'] . "</a>\n" .
  522.              "\t\t\t<span class="timestamp">({$update_time})</span>\n" .
  523.              "\t\t</div>\n" .
  524.              "\t</li>\n";
  525.     fwrite($html_file, $entry);
  526. }
  527. fwrite($html_file, "</ul>\n");

  528. // === 移动:统计信息移到帖子下方 ===
  529. fwrite($html_file, "<div class="stats">\n");

  530. fwrite($html_file, "\t<p>本地图由系统自动生成,仅用于帮助用户导航</p>\n");
  531. fwrite($html_file, "\t<p>生成时间:" . $generation_time . "</p>\n");
  532. fwrite($html_file, "\t<p>收录城市版块(分区):" . $public_group_count . " 个</p>\n");
  533. fwrite($html_file, "\t<p>收录帖子(排除特定版块帖子):" . $processed . " / " . $total_threads . " 条</p>\n");
  534. fwrite($html_file, "\t<p><strong>XML格式:</strong><a href="Sitemap.xml" target="_blank">点击打开</a></p>\n");
  535. fwrite($html_file, "</div>\n");
  536. // === 移动结束 ===

  537. // 尾部
  538. $html_footer = '
  539.     </div>
  540.     <!-- 页脚 -->
  541.     <footer class="footer">
  542.         <div class="footer-content">
  543.             <div class="footer-bottom">
  544.                 <div class="copyright">
  545.                     <p>客服:<a href="mailto:kefu@zgbm.com">kefu@zgbm.com</a></p>
  546.                     <p>&copy; ' . date('Y') . ' ' . $site_name . '</p>
  547.                 </div>
  548.             </div>
  549.         </div>
  550.     </footer>
  551.    
  552.     <script>
  553. // 页面加载完成后的初始化
  554. document.addEventListener("DOMContentLoaded", function() {
  555.     console.log("网站地图页面加载完成");
  556. });
  557.     </script>
  558. </body>
  559. </html>';
  560. fwrite($html_file, $html_footer);
  561. fclose($html_file);
  562. @chmod($html_filepath, 0644);

  563. // 更新日志内容
  564. runlog('sitemap_html', "Success: Generated Sitemap.html with {$public_group_count} city-groups and {$processed}/{$total_threads} threads. Hidden groups: [" . implode(',', $hidden_groups) . "], Excluded post fids: [" . implode(',', $excluded_from_posts) . "]");

  565. return TRUE;
  566. ?>
复制代码


信息真伪请自行辨别,发现虚假信息请到[便民诉求]发帖举报

1

主题

2

回帖

30

威望

管理员

UID
1

 楼主| 发表于 2026-5-19 03:26:49 | 显示全部楼层
2026-5-19,更新网站地图的一些功能,主要更新部分:
  1. // ==================== 【关键配置区】====================
  2. // 🔒 版块显示控制配置
  3. //    1. $hidden_groups: 不在网站地图中显示的版块(分区),但其帖子仍会显示
  4. //    2. $excluded_from_posts: 不显示其帖子的版块,但版块本身可能仍显示在地图中
  5. // 版块控制配置
  6. $hidden_groups = [1];        // ←←← 在地图中不显示的版块ID(分区),但其帖子仍会显示
  7. $excluded_from_posts = [0];  // ←←← 不显示其帖子的版块ID,但版块本身可能仍显示在地图中
复制代码

完整代码:

  1. <?php
  2. /**
  3. * Discuz! Sitemap HTML 生成计划任务(静默模式)
  4. * 文件路径: source/include/cron/sitemap_builder.php
  5. *
  6. * 功能:定时生成 Sitemap.html,供用户浏览网站结构
  7. * 特点:无任何输出,仅记录日志,符合 Discuz! Cron 规范
  8. *
  9. * @author Your Name
  10. * @version 2.2
  11. * @license GPL v3
  12. */
  13. // 防止直接访问,只能通过Discuz系统调用
  14. if (!defined('IN_DISCUZ')) {
  15.     exit('Access Denied');
  16. }

  17. // 静默运行,禁止任何输出
  18. error_reporting(0);
  19. ini_set('display_errors', 0);

  20. // ==================== 【关键配置区】====================
  21. // 🔒 版块显示控制配置
  22. //    1. $hidden_groups: 不在网站地图中显示的版块(分区),但其帖子仍会显示
  23. //    2. $excluded_from_posts: 不显示其帖子的版块,但版块本身可能仍显示在地图中
  24. // 版块控制配置
  25. $hidden_groups = [1];        // ←←← 在地图中不显示的版块ID(分区),但其帖子仍会显示
  26. $excluded_from_posts = [0];  // ←←← 不显示其帖子的版块ID,但版块本身可能仍显示在地图中

  27. $config = [
  28.     'html_filename'      => 'Sitemap.html',
  29.     'update_interval'    => 58,     // 最短更新间隔(分钟),建议每天一次
  30.     'batch_size'         => 200,    // 每批次处理的数量
  31.     'max_threads'        => 1000,   // 最大处理帖子数
  32.     'memory_limit'       => '256M', // 内存限制
  33.     'max_execution_time' => 300,    // 最大执行时间(秒)
  34.     'charset'            => 'utf-8'
  35. ];

  36. // ================================================

  37. // 设置资源限制
  38. @ini_set('memory_limit', $config['memory_limit']);
  39. @set_time_limit($config['max_execution_time']);

  40. // 获取站点时区
  41. $site_timezone = $_G['setting']['timeoffset'] ?? 8;// 默认为北京时间(UTC+8)
  42. // 强制设置PHP时区以确保一致性
  43. $timezone_map = [
  44.     -12 => 'Pacific/Kwajalein', -11 => 'Pacific/Midway', -10 => 'Pacific/Honolulu',
  45.     -9 => 'America/Anchorage', -8 => 'America/Los_Angeles', -7 => 'America/Denver',
  46.     -6 => 'America/Chicago', -5 => 'America/New_York', -4 => 'America/Halifax',
  47.     -3 => 'America/Sao_Paulo', -2 => 'Atlantic/South_Georgia', -1 => 'Atlantic/Azores',
  48.     0 => 'UTC', 1 => 'Europe/London', 2 => 'Europe/Paris', 3 => 'Europe/Moscow',
  49.     4 => 'Asia/Baku', 5 => 'Asia/Karachi', 6 => 'Asia/Dhaka', 7 => 'Asia/Jakarta',
  50.     8 => 'Asia/Shanghai', 9 => 'Asia/Tokyo', 10 => 'Australia/Brisbane',
  51.     11 => 'Pacific/Noumea', 12 => 'Pacific/Auckland'
  52. ];
  53. date_default_timezone_set($timezone_map[$site_timezone] ?? 'Asia/Shanghai');

  54. $html_filepath = DISCUZ_ROOT . '/' . $config['html_filename'];
  55. $web_root = rtrim($_G['siteurl'], '/') . '/';
  56. $site_name = htmlspecialchars($_G['setting']['sitename'] ?? 'Discuz Forum', ENT_QUOTES, $config['charset']);

  57. // 检查是否在更新冷却期内,防止频繁生成
  58. if (file_exists($html_filepath)) {
  59.     $last_modified = filemtime($html_filepath);
  60.     if ((time() - $last_modified) < ($config['update_interval'] * 60)) {
  61.         runlog('sitemap_html', 'Skipped: within update interval.');
  62.         return TRUE;
  63.     }
  64. }

  65. // 构建版块排除条件
  66. $exclude_groups_sql = '';
  67. $exclude_posts_sql = '';

  68. if (!empty($hidden_groups)) {
  69.     $group_ids = array_map('intval', array_filter($hidden_groups));
  70.     if (!empty($group_ids)) {
  71.         $exclude_groups_sql = " AND fid NOT IN (" . implode(',', $group_ids) . ") ";
  72.     }
  73. }

  74. if (!empty($excluded_from_posts)) {
  75.     $post_exclude_ids = array_map('intval', array_filter($excluded_from_posts));
  76.     if (!empty($post_exclude_ids)) {
  77.         $exclude_posts_sql = " AND fid NOT IN (" . implode(',', $post_exclude_ids) . ") ";
  78.     }
  79. }

  80. // === 修复:获取所有分区(包括status=2的隐藏分区),但排除在$hidden_groups中的版块 ===
  81. $groups_data = []; // 存储分区信息(即城市版块)
  82. $gids_for_thread = []; // 存储分区ID用于帖子查询

  83. // 获取所有公开的分区(包括隐藏分区),但排除$hidden_groups中的
  84. $query_groups = DB::query("
  85.     SELECT fid as gid, name
  86.     FROM " . DB::table('forum_forum') . "
  87.     WHERE type='group'
  88.     {$exclude_groups_sql}
  89.     ORDER BY displayorder
  90. ");
  91. while ($group = DB::fetch($query_groups)) {
  92.     $groups_data[] = $group;
  93.     $gids_for_thread[] = (int) $group['gid'];
  94. }
  95. $public_group_count = count($groups_data);

  96. // === 修复:帖子查询逻辑 - 分区版块不显示但其帖子仍显示 ===
  97. $thread_fid_condition = '';

  98. // 获取所有版块,排除$excluded_from_posts中的版块,但保留属于隐藏分区的版块的帖子
  99. $query_fids = DB::query("
  100.     SELECT fid
  101.     FROM " . DB::table('forum_forum') . "
  102.     WHERE status='1' AND type='forum'
  103.     AND fid NOT IN (" . implode(',', array_map('intval', $excluded_from_posts)) . ")
  104. ");

  105. $fids_to_include = [];
  106. while ($fid_row = DB::fetch($query_fids)) {
  107.     $fids_to_include[] = (int) $fid_row['fid'];
  108. }

  109. if (!empty($fids_to_include)) {
  110.     $thread_fid_condition = "AND fid IN (" . implode(',', $fids_to_include) . ")";
  111. } else {
  112.     $thread_fid_condition = "AND 1=0"; // 无符合条件的版块,则不查任何帖子
  113. }

  114. // 获取帖子总数(仅排除$excluded_from_posts中的版块,不考虑$hidden_groups对帖子的影响)
  115. $total_result = DB::fetch_first(
  116.     "SELECT COUNT(*) AS total FROM " . DB::table('forum_thread') .
  117.     " WHERE displayorder >= 0 AND lastpost > 0 {$thread_fid_condition}"
  118. );
  119. $total_threads = (int)$total_result['total'];

  120. // 若无有效内容,提前退出
  121. if ($total_threads <= 0 && $public_group_count <= 0) {
  122.     runlog('sitemap_html', 'No valid threads or groups found for Sitemap.html.');
  123.     return TRUE;
  124. }

  125. // 分批读取数据(含 subject)
  126. $threads_data = [];
  127. $offset = 0;
  128. $processed = 0;

  129. do {
  130.     $query = DB::query(
  131.         "SELECT tid, lastpost, subject, replies, fid FROM " . DB::table('forum_thread') .
  132.         " WHERE displayorder >= 0 AND lastpost > 0 {$thread_fid_condition} " .
  133.         "ORDER BY lastpost DESC LIMIT {$offset}, {$config['batch_size']}"
  134.     );
  135.     $batch_count = 0;
  136.     while ($thread = DB::fetch($query)) {
  137.         // 确保 subject 安全
  138.         $thread['subject'] = dhtmlspecialchars($thread['subject']);
  139.         $threads_data[] = $thread;
  140.         $batch_count++;
  141.         $processed++;
  142.         if ($processed >= $config['max_threads']) break;
  143.     }
  144.     $offset += $config['batch_size'];
  145.     if ($batch_count < $config['batch_size']) break;
  146.     if ($processed >= $config['max_threads']) break;
  147. } while ($batch_count == $config['batch_size']);

  148. if (empty($threads_data) && empty($groups_data)) {
  149.     runlog('sitemap_html', 'No thread or group data retrieved for HTML sitemap.');
  150.     return TRUE;
  151. }

  152. // 开始写入 HTML
  153. $html_file = @fopen($html_filepath, 'w');
  154. if (!$html_file) {
  155.     runlog('sitemap_html', 'Failed to open HTML file for writing: ' . $html_filepath);
  156.     return FALSE;
  157. }

  158. $generation_time = date('Y-m-d H:i:s');

  159. // HTML 头部
  160. $html_header = '<!DOCTYPE html>
  161. <html lang="zh-CN">
  162. <head>
  163.     <meta charset="' . $config['charset'] . '">
  164.     <title>网站地图 - ' . $site_name . '</title>
  165.     <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  166.     <meta name="robots" content="noindex,follow">
  167.     <style>
  168. body{
  169.     font-family: Arial, sans-serif;
  170.     margin: 0;
  171.     padding: 0;
  172.     background: #f5f5f5;
  173.     line-height: 1.4; /* 调整行高 */
  174. }
  175. .container{
  176.     max-width: 100%;
  177.     width: 100%;
  178.     margin: 0 auto;
  179.     background: #fff;
  180.     padding: 15px;
  181.     box-sizing: border-box;
  182. }
  183. @media (min-width: 768px) {
  184.     .container {
  185.         max-width: 1200px;
  186.         padding: 30px;
  187.         border-radius: 8px;
  188.         box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  189.         margin: 20px auto;
  190.     }
  191. }
  192. h1{
  193.     text-align: center;
  194.     color: #333;
  195.     border-bottom: 2px solid #007cba;
  196.     padding-bottom: 15px;
  197.     margin-top: 0;
  198.     font-size: 1.8rem;
  199. }
  200. @media (min-width: 768px) {
  201.     h1 {
  202.         font-size: 2.2rem;
  203.     }
  204. }
  205. h2{
  206.     color: #007cba;
  207.     margin-top: 30px;
  208.     font-size: 1.4rem;
  209.     line-height: 1.4; /* 调整行高 */
  210. }
  211. @media (min-width: 768px) {
  212.     h2 {
  213.         font-size: 1.6rem;
  214.     }
  215. }
  216. h3{
  217.     color: #555;
  218.     margin-top: 20px;
  219.     margin-bottom: 10px;
  220.     font-size: 1.2rem;
  221.     line-height: 1.4; /* 调整行高 */
  222. }
  223. ul{
  224.     list-style: none;
  225.     padding: 0;
  226.     margin: 0;
  227. }
  228. li{
  229.     margin: 5px 0; /* 减小间距 */
  230.     padding: 8px 12px; /* 减小内边距 */
  231.     background: #f9f9f9;
  232.     border-left: 4px solid #007cba;
  233.     border-radius: 4px;
  234.     word-wrap: break-word;
  235.     overflow-wrap: break-word;
  236.     line-height: 1.4; /* 调整行高 */
  237. }
  238. a{
  239.     color: #007cba;
  240.     text-decoration: none;
  241.     font-weight: bold;
  242.     word-break: break-all;
  243.     display: inline; /* 改为inline以便时间和链接在同一行 */
  244. }
  245. a:hover{
  246.     text-decoration: underline;
  247. }
  248. .timestamp{
  249.     color: #666;
  250.     font-size: 0.85em;
  251.     margin-left: 8px; /* 减小间距 */
  252.     display: inline; /* 与链接同行 */
  253. }
  254. @media (min-width: 768px) {
  255.     .timestamp {
  256.         margin-left: 10px;
  257.     }
  258. }
  259. .hot{
  260.     border-left-color: #ff6b6b;
  261.     background: #fff5f5;
  262. }
  263. .recent{
  264.     border-left-color: #4ecdc4;
  265. }
  266. .stats{
  267.     background: #e8f4fd;
  268.     padding: 12px;
  269.     border-radius: 5px;
  270.     margin: 20px 0;
  271.     font-size: 0.9rem;
  272.     line-height: 1.4; /* 调整行高 */
  273. }
  274. @media (min-width: 768px) {
  275.     .stats {
  276.         font-size: 1rem;
  277.     }
  278. }
  279. /* 修复换行问题:确保统计信息在一行显示 */
  280. .stats p {
  281.     margin: 6px 0; /* 减小间距 */
  282.     white-space: nowrap; /* 防止内容换行 */
  283.     overflow: hidden;    /* 隐藏溢出内容 */
  284.     text-overflow: ellipsis; /* 超出部分显示省略号 */
  285. }
  286. /* 针对链接的特殊处理 */
  287. .stats a {
  288.     display: inline !important; /* 确保链接为行内元素 */
  289.     white-space: nowrap; /* 防止链接文字换行 */
  290. }
  291. .group-section{
  292.     margin-bottom: 30px;
  293. }
  294. .footer-text{
  295.     margin-top: 40px;
  296.     text-align: center;
  297.     color: #666;
  298.     font-size: 0.85rem;
  299.     padding: 20px 0 10px;
  300.     line-height: 1.4; /* 调整行高 */
  301. }
  302. @media (min-width: 768px) {
  303.     .footer-text {
  304.         font-size: 0.9rem;
  305.     }
  306. }
  307. /* 提示信息样式 */
  308. .city-hint {
  309.     color: #666;
  310.     font-style: italic;
  311.     margin-top: -10px;
  312.     margin-bottom: 15px;
  313.     font-size: 0.9em;
  314.     display: inline-block; /* 改为行内块元素 */
  315.     line-height: 1.4; /* 调整行高 */
  316. }
  317. /* 便民诉求链接样式 */
  318. .city-hint a {
  319.     display: inline; /* 确保链接为行内元素 */
  320.     white-space: nowrap; /* 防止链接换行 */
  321. }
  322. /* 修复帖子列表布局 - 调整为紧凑样式 */
  323. .thread-item {
  324.     display: inline; /* 与时间在同一行 */
  325. }
  326. .thread-title {
  327.     display: inline; /* 与时间在同一行 */
  328. }
  329. .thread-timestamp {
  330.     white-space: nowrap; /* 时间不换行 */
  331.     color: #666;
  332.     font-size: 0.85em;
  333. }
  334.     </style>
  335. </head>
  336. <body>
  337. <div class="container">
  338.     <h1>网站地图 - ' . $site_name . '</h1>';

  339. fwrite($html_file, $html_header);

  340. // 首页
  341. fwrite($html_file, "<h2>首页</h2>\n<ul>\n");
  342. fwrite($html_file, "\t<li><a href="" . htmlspecialchars($web_root, ENT_QUOTES, $config['charset']) . "">{$site_name}</a></li>\n");
  343. fwrite($html_file, "</ul>\n");

  344. // === 修复:显示分区列表(即城市版块),包括隐藏分区,使用静态链接格式 gid-1.html ===
  345. if (!empty($groups_data)) {
  346.     fwrite($html_file, "<h2>城市版块</h2>\n");
  347.     fwrite($html_file, "<div class="city-hint">如果没有您的城市,请到<a href="2-1.html" target="_blank">便民诉求</a>发帖申请!</div>\n<ul>\n");
  348.     foreach ($groups_data as $group) {
  349.         // 使用静态链接格式 gid-1.html
  350.         $group_url = $web_root . $group['gid'] . '-1.html';
  351.         
  352.         $entry = "\t<li>\n" .
  353.                  "\t\t<a href="" . htmlspecialchars($group_url, ENT_QUOTES, $config['charset']) . "" target="_blank">" . htmlspecialchars($group['name'], ENT_QUOTES, $config['charset']) . "</a>\n" .
  354.                  "\t</li>\n";
  355.         fwrite($html_file, $entry);
  356.     }
  357.     fwrite($html_file, "</ul>\n");
  358. }
  359. // === 修复结束 ===

  360. // 帖子列表
  361. fwrite($html_file, "<h2>最新帖子</h2>\n<ul>\n");
  362. foreach ($threads_data as $thread) {
  363.     $url = $web_root . $thread['tid'] . '-1-1.html';
  364.     $update_time = date('Y-m-d H:i', $thread['lastpost']);
  365.     $hours = (time() - $thread['lastpost']) / 3600;

  366.     $classes = [];
  367.     if ($thread['replies'] > 50) $classes[] = 'hot';
  368.     if ($hours < 24) $classes[] = 'recent';
  369.     $class_attr = $classes ? ' class="' . implode(' ', $classes) . '"' : '';

  370.     $entry = "\t<li{$class_attr}>\n" .
  371.              "\t\t<div class="thread-item">\n" .
  372.              "\t\t\t<a href="" . htmlspecialchars($url, ENT_QUOTES, $config['charset']) . "" target="_blank" class="thread-title">" . $thread['subject'] . "</a>\n" .
  373.              "\t\t\t<span class="timestamp">({$update_time})</span>\n" .
  374.              "\t\t</div>\n" .
  375.              "\t</li>\n";
  376.     fwrite($html_file, $entry);
  377. }
  378. fwrite($html_file, "</ul>\n");

  379. // === 移动:统计信息移到帖子下方 ===
  380. fwrite($html_file, "<div class="stats">\n");
  381. fwrite($html_file, "\t<p><strong>生成时间:</strong>" . $generation_time . "</p>\n");
  382. fwrite($html_file, "\t<p><strong>收录城市版块(分区):</strong>" . $public_group_count . " 个</p>\n");
  383. fwrite($html_file, "\t<p><strong>收录帖子(排除特定版块帖子):</strong>" . $processed . " / " . $total_threads . " 条</p>\n");
  384. fwrite($html_file, "\t<p><strong>XML格式:</strong><a href="Sitemap.xml" target="_blank">点击打开</a></p>\n");
  385. fwrite($html_file, "</div>\n");
  386. // === 移动结束 ===

  387. // 尾部
  388. $html_footer = '
  389.     <div class="footer-text">
  390.         <p>本地图由系统自动生成,仅用于帮助用户导航。</p>
  391.         <p>© ' . date('Y') . ' ' . $site_name . '</p>
  392.     </div>
  393. </div>
  394. </body>
  395. </html>';
  396. fwrite($html_file, $html_footer);
  397. fclose($html_file);
  398. @chmod($html_filepath, 0644);

  399. // 更新日志内容
  400. runlog('sitemap_html', "Success: Generated Sitemap.html with {$public_group_count} city-groups and {$processed}/{$total_threads} threads. Hidden groups: [" . implode(',', $hidden_groups) . "], Excluded post fids: [" . implode(',', $excluded_from_posts) . "]");

  401. return TRUE;
  402. ?>
复制代码


信息真伪请自行辨别,发现虚假信息请到[便民诉求]发帖举报
手机版|小黑屋|便民网

相关侵权、举报、投诉及建议等,请发联系客服:kefu@zgbm.com

© 2026 . 便民网已运行: 000 小时 00 Powered by Discuz! X5.0 |苏ICP备20025604号|苏公网安备32021302003166号

在本版发帖返回顶部