2026年2月开发记录-文档类实现方案草案

文档创建者:蜗牛
浏览次数:164
最后更新:2026-02-28
新建文档详情
排列序号: 10
配置选择: 普通文档
配置贴展示: 展示在目录

分类信息排序方案实施计划(方案 1.0 完整版)

一、项目概述

1.1 方案目标

基于 Discuz 分类信息系统实现目录树和版块列表页的统一排序,解决以下问题:

  1. 排序一致性:目录树排序和列表页排序保持一致
  2. 配置简化:发帖时直接设置排序值,无需在配置帖中查 tid
  3. 教育成本降低:使用下拉单选 + 置顶判断,无需学习特殊格式
  4. 顶级帖子展示:支持置顶说明帖自动展示在 0 级目录
  5. 配置帖可控:配置帖可通过选项控制是否展示在目录树中

1.2 核心设计

项目 说明
sortid 5 分类名称:文档管理
optionid=21 sort_order 类型:字串 (text),用于帖子排序值
optionid=22 is_config 类型:单选 (radio),0=普通帖子,1=配置帖子
optionid=23 show_in_tree 类型:单选 (radio),0=不展示,1=展示(仅对配置帖有效)

1.3 判断逻辑

0 级目录(顶级):displayorder > 0 AND is_config = 0
    → 自动展示所有置顶说明帖

配置帖展示:displayorder > 0 AND is_config = 1 AND show_in_tree = 1
    → 配置帖可选择是否展示在目录树中

普通帖子:displayorder = 0
    → 按 sort_order 排序归入对应目录

二、数据库设计

2.1 分类信息项目创建

2.1.1 创建分类信息(sortid=5)

在 Discuz 后台 → 分类信息 → 添加分类信息

字段
分类名称 文档管理
类型标识 document
显示顺序 5
可用

2.1.2 创建选项项目

optionid=21 - sort_order(排序值)

字段
所属分类 sortid=5
选项类型 字串 (text)
选项标题 排序权重
变量名 sort_order
输入提示 数字越小越靠前,如 1、2、3...
必填 否(默认 99)

optionid=22 - is_config(配置帖标识)

字段
所属分类 sortid=5
选项类型 单选 (radio)
选项标题 是否为配置帖
变量名 is_config
选项值 0=否\n1=是
默认值 0
必填

optionid=23 - show_in_tree(目录树展示控制)

字段
所属分类 sortid=5
选项类型 单选 (radio)
选项标题 是否展示在目录树
变量名 show_in_tree
选项值 0=不展示\n1=展示
默认值 1
权限设置 仅管理员可设置
说明 仅对配置帖有效,普通帖子始终展示

2.2 涉及的数据表

-- 分类信息选项定义表
pre_forum_typeoption

-- 分类信息选项数据表
pre_forum_typeoptionvar

-- 分类信息单选值表
pre_forum_typeoptionvalue

-- 帖子表(查询用)
pre_forum_thread

-- 标签表(现有功能)
pre_common_tag
pre_common_tagitem

2.3 索引优化建议

-- 为分类信息表添加组合索引
ALTER TABLE `pre_forum_typeoptionvar`
ADD INDEX `idx_sortid_optionid` (`sortid`, `optionid`);

ALTER TABLE `pre_forum_typeoptionvar`
ADD INDEX `idx_tid_optionid` (`tid`, `optionid`);

-- 为帖子表添加索引(如果尚未存在)
ALTER TABLE `pre_forum_thread`
ADD INDEX `idx_fid_displayorder` (`fid`, `displayorder`);

三、代码实施步骤

3.1 文件修改清单

文件 修改类型 优先级
template/xmyc_doc/php/directory_tree.php 核心重写 P0
template/xmyc_touch_doc/touch/php/directory_tree.php 核心重写 P0
template/xmyc_doc/forum/forumdisplay.htm 修改 P1
template/xmyc_touch_doc/touch/forum/forumdisplay.htm 修改 P1

3.2 directory_tree.php 核心修改

3.2.1 新增辅助函数

/**
 * 批量获取帖子的分类信息值
 * @param array $tids 帖子 ID 数组
 * @param int $optionid 选项 ID
 * @return array [tid => value]
 */
function get_typeoption_values($tids, $optionid) {
    $values = array();
    if(empty($tids)) return $values;

    $tids_str = implode(',', $tids);
    $query = DB::query("SELECT tid, value FROM ".DB::table('forum_typeoptionvar')."
                        WHERE tid IN ($tids_str) AND optionid=".intval($optionid));

    while($row = DB::fetch($query)) {
        $values[$row['tid']] = $row['value'];
    }

    return $values;
}

/**
 * 获取版块下所有相关帖子(优化版)
 * @param int $fid 版块 ID
 * @return array ['config'=>[], 'top_sticky'=>[], 'normal'=>[]]
 */
function get_directory_threads_optimized($fid) {
    $result = array(
        'config' => array(),        // 配置帖
        'top_sticky' => array(),    // 置顶说明帖(0 级目录)
        'normal' => array()         // 普通帖子
    );

    $fid = intval($fid);
    if(empty($fid)) {
        global $_G;
        $fid = intval($_G['fid']);
    }

    // 查询所有置顶帖和带目录标签的普通帖
    $query = DB::query("SELECT t.tid, t.subject, t.dateline, t.displayorder
        FROM ".DB::table('forum_thread')." t
        WHERE t.fid=".$fid."
        AND t.subject NOT LIKE '[DIR]%'
        AND t.displayorder>=0
        ORDER BY t.displayorder DESC, t.dateline DESC");

    $threads = array();
    while($row = DB::fetch($query)) {
        $threads[$row['tid']] = $row;
    }

    if(empty($threads)) {
        return $result;
    }

    $tids = array_keys($threads);

    // 批量获取分类信息
    $sort_orders = get_typeoption_values($tids, 21);      // sort_order
    $is_configs = get_typeoption_values($tids, 22);      // is_config
    $show_in_trees = get_typeoption_values($tids, 23);   // show_in_tree

    // 分类帖子
    foreach($threads as $tid => $thread) {
        $is_config = isset($is_configs[$tid]) ? $is_configs[$tid] : '0';
        $show_in_tree = isset($show_in_trees[$tid]) ? $show_in_trees[$tid] : '1';
        $sort_order = isset($sort_orders[$tid]) ? $sort_orders[$tid] : '99';

        $thread['sort_order'] = $sort_order;

        if($thread['displayorder'] > 0 && $is_config == '1') {
            // 配置帖:根据 show_in_tree 决定是否加入 0 级目录
            if($show_in_tree == '1') {
                $result['config'][$tid] = $thread;
            }
        } elseif($thread['displayorder'] > 0 && $is_config == '0') {
            // 置顶说明帖:自动加入 0 级目录
            $result['top_sticky'][$tid] = $thread;
        } else {
            // 普通帖子
            $result['normal'][$tid] = $thread;
        }
    }

    return $result;
}

3.2.2 主逻辑修改

// === 原代码 ===
// $_tagged_threads = get_directory_threads($_G['fid']);

// === 新代码 ===
// 使用优化版函数获取帖子
$all_threads = get_directory_threads_optimized($_G['fid']);

// 初始化目录树
$flquery = array(
    'choices' => array(),
    'curkey' => '',
    'source' => 'tag',
    'sortid' => 0,
    'identifier' => 'dir'
);

// 设置当前选中的目录
$dir_param = isset($_GET['dir']) ? $_GET['dir'] : '';
$flquery['curkey'] = str_replace('.', '-', trim($dir_param));

// 1. 首先添加 0 级目录(置顶帖和配置帖)
$level_0_index = 0;

// 添加置顶说明帖(displayorder > 0 AND is_config = 0)
foreach($all_threads['top_sticky'] as $tid => $thread) {
    $thread_key = 'tid_' . $tid;
    $flquery['choices'][$thread_key] = array(
        'key' => $thread_key,
        'num' => $tid,
        'level' => 0,
        'name' => $thread['subject'],
        'type' => 'thread',
        'href' => generate_thread_link($tid),
        'list' => array(),
        'sort_order' => $level_0_index++
    );
}

// 添加配置帖(displayorder > 0 AND is_config = 1 AND show_in_tree = 1)
foreach($all_threads['config'] as $tid => $thread) {
    $thread_key = 'tid_' . $tid;
    $flquery['choices'][$thread_key] = array(
        'key' => $thread_key,
        'num' => $tid,
        'level' => 0,
        'name' => $thread['subject'],
        'type' => 'thread',
        'href' => generate_thread_link($tid),
        'list' => array(),
        'sort_order' => $level_0_index++
    );
}

// 2. 处理目录配置帖,构建目录树结构
if(!empty($directory_config)) {
    // 检查是否使用新格式
    $is_new_format = false;
    foreach($directory_config['directories'] as $num_path => $config) {
        if(!empty($config['is_new_format'])) {
            $is_new_format = true;
            break;
        }
    }

    if($is_new_format) {
        // 新格式处理:序号 = 标签名 = 目录名
        foreach($directory_config['directories'] as $num_path => $config) {
            create_directory_node_new($flquery['choices'], $num_path, $config['name']);
        }

        // 添加普通帖子到对应目录(通过标签名称匹配)
        foreach($all_threads['normal'] as $tid => $thread) {
            // 获取帖子标签
            $tags_str = get_thread_tags($tid);
            $tag_names = extract_directory_tags($tags_str);

            foreach($tag_names as $tag_name) {
                $num_path = find_num_path_by_tag($directory_config, $tag_name);
                if($num_path) {
                    // 使用分类信息中的 sort_order
                    $sort_order = intval($thread['sort_order']);
                    add_thread_to_directory_new($flquery['choices'], $num_path, $thread, $sort_order);
                }
            }
        }
    } else {
        // 旧格式处理(兼容)
        // ...(保持原有逻辑)
    }
} else {
    // 没有配置帖,按标签自动生成单层目录
    foreach($all_threads['normal'] as $tid => $thread) {
        $tags_str = get_thread_tags($tid);
        $dir_names = extract_directory_tags($tags_str);

        foreach($dir_names as $dir_name) {
            build_directory_tree($flquery['choices'], $dir_name, $thread, intval($thread['sort_order']));
        }
    }
}

// === 新增辅助函数:获取帖子标签 ===
function get_thread_tags($tid) {
    $tags = '';
    $query = DB::query("SELECT tag.tagname FROM ".DB::table('common_tagitem')." ti
                        INNER JOIN ".DB::table('common_tag')." tag ON ti.tagid=tag.tagid
                        WHERE ti.itemid=".intval($tid)." AND ti.idtype='tid'");

    $tag_arr = array();
    while($row = DB::fetch($query)) {
        $tag_arr[] = $row['tagname'];
    }

    if(!empty($tag_arr)) {
        $tags = implode(' ', $tag_arr);
    }

    return $tags;
}

3.3 版块列表页模板修改

3.3.1 forumdisplay.htm 修改要点

在主题列表循环中,添加 sort_order 排序:

// 原代码(按回复时间排序)
// $orderby = 'lastpost';

// 新代码(优先按 sort_order 排序)
// 需要在查询时获取分类信息
$_G['forum']['threads_option_sort'] = 5; // 使用分类信息 sortid=5
$_G['forum']['threads_option_order'] = 21; // 使用 optionid=21 (sort_order)

在模板循环中:

<!-- 在主题列表循环前获取 sort_order -->
<!--{loop $_G['forum_threadlist'] $thread}-->
    <!--{if $thread['sortid'] == 5}-->
        <!--{eval $sort_order = $thread['option']['21'] ? $thread['option']['21'] : 99;}-->
    <!--{/if}-->
<!--{/loop}-->

四、数据迁移计划

4.1 现有帖子数据迁移

4.1.1 迁移脚本(SQL 示例)

-- 1. 为现有配置帖初始化分类信息
-- 查找 [DIR] 开头的置顶帖,设置为配置帖
INSERT INTO `pre_forum_typeoptionvar` (`sortid`, `tid`, `optionid`, `value`)
SELECT 5, tid, 22, '1'
FROM `pre_forum_thread`
WHERE subject LIKE '[DIR]%' AND displayorder > 0
ON DUPLICATE KEY UPDATE value = '1';

-- 2. 为其他置顶帖设置 is_config = 0
INSERT INTO `pre_forum_typeoptionvar` (`sortid`, `tid`, `optionid`, `value`)
SELECT 5, tid, 22, '0'
FROM `pre_forum_thread`
WHERE displayorder > 0 AND subject NOT LIKE '[DIR]%'
ON DUPLICATE KEY UPDATE value = '0';

-- 3. 为带目录标签的普通帖子设置 sort_order = 99(默认)
-- 需要先获取这些帖子的 tid(通过标签关联)
-- 这个步骤建议在 PHP 中完成,因为需要查询标签

-- 4. 为所有普通帖子设置默认值
INSERT INTO `pre_forum_typeoptionvar` (`sortid`, `tid`, `optionid`, `value`)
SELECT 5, tid, 21, '99'
FROM `pre_forum_thread`
WHERE displayorder = 0
ON DUPLICATE KEY UPDATE value = '99';

4.1.2 PHP 迁移脚本

<?php
/**
 * 数据迁移脚本:为现有帖子初始化分类信息
 * 使用方法:在 Discuz 根目录执行
 */

define('IN_DISCUZ', true);
require './source/class/class_core.php';

$discuz = C::app();
$discuz->init();

// 获取所有帖子
$query = DB::query("SELECT tid, displayorder FROM ".DB::table('forum_thread')." WHERE displayorder>=0");

$migrated_count = 0;
while($thread = DB::fetch($query)) {
    $tid = $thread['tid'];
    $displayorder = $thread['displayorder'];

    // 判断是否为配置帖
    $is_config = (strpos($thread['subject'], '[DIR]') === 0 && $displayorder > 0) ? '1' : '0';

    // 设置 is_config
    DB::insert('forum_typeoptionvar', array(
        'sortid' => 5,
        'tid' => $tid,
        'optionid' => 22,
        'value' => $is_config
    ), false, true);

    // 设置 sort_order(普通帖子默认 99)
    if($displayorder == 0) {
        DB::insert('forum_typeoptionvar', array(
            'sortid' => 5,
            'tid' => $tid,
            'optionid' => 21,
            'value' => '99'
        ), false, true);
    }

    // 设置 show_in_tree(默认展示)
    DB::insert('forum_typeoptionvar', array(
        'sortid' => 5,
        'tid' => $tid,
        'optionid' => 23,
        'value' => '1'
    ), false, true);

    $migrated_count++;
}

echo "迁移完成!共迁移 {$migrated_count} 个帖子\n";
?>

4.2 版主操作指引

发帖/编辑帖子时的操作流程:

4.2.1 发布置顶说明帖(0 级目录)

  1. 发帖时选择分类信息:文档管理
  2. 设置 是否为配置帖 = 否
  3. 设置 置顶 = 置顶
  4. 发布后自动展示在 0 级目录

4.2.2 发布配置帖

  1. 发帖时选择分类信息:文档管理
  2. 设置 是否为配置帖 = 是
  3. 设置 是否展示在目录树 = 是/否(根据需要)
  4. 设置 置顶 = 置顶
  5. 发布后可展示在 0 级目录(如果 show_in_tree=1)

4.2.3 发布普通文档帖

  1. 发帖时选择分类信息:文档管理
  2. 设置 排序权重 = 数字(如 1、2、3...)
  3. 设置 是否为配置帖 = 否
  4. 添加标签:#目录_xxx
  5. 发布后按 sort_order 归入对应目录

五、测试验证清单

5.1 单元测试

测试项 操作步骤 预期结果 状态
0 级目录 - 置顶说明帖 发布 is_config=0 的置顶帖 自动展示在 0 级目录
0 级目录 - 配置帖展示 发布 is_config=1, show_in_tree=1 的置顶帖 展示在 0 级目录
0 级目录 - 配置帖隐藏 发布 is_config=1, show_in_tree=0 的置顶帖 不展示在 0 级目录
目录树 - 新格式 配置新格式,帖子打标签 帖子正确归入目录
目录树 - 旧格式 配置旧格式,帖子打标签 帖子正确归入目录(兼容)
排序 - sort_order 设置不同 sort_order 值 按数字从小到大排序
排序 - 默认值 不设置 sort_order 默认 99,排在最后
Ajax 请求 getdirectory=true 返回正确 JSON 数据

5.2 集成测试

测试项 操作步骤 预期结果 状态
目录树与列表页排序一致 设置多个帖子 sort_order 两处排序顺序相同
配置帖编辑 修改配置帖的 show_in_tree 目录树即时更新
帖子移动 移动帖子到其他版块 分类信息保持
帖子删除 删除配置帖 目录树自动移除
权限控制 普通用户编辑帖子 无法修改 show_in_tree

5.3 性能测试

测试项 指标 目标值 状态
目录树生成时间 100 个帖子 < 100ms
分类信息查询 批量查询 < 50ms
Ajax 响应时间 目录数据请求 < 200ms

六、上线部署计划

6.1 部署阶段

阶段一:数据库准备(预计 30 分钟)

  1. 在 Discuz 后台创建分类信息
  2. 创建三个选项项目
  3. 执行索引优化 SQL
  4. 负责人:数据库管理员
  5. 验收标准:后台可看到新分类信息

阶段二:代码部署(预计 1 小时)

  1. 备份原文件
  2. 部署新版 directory_tree.php
  3. 部署新版 forumdisplay.htm
  4. 负责人:开发人员
  5. 验收标准:语法检查通过

阶段三:数据迁移(预计 2 小时)

  1. 执行数据迁移脚本
  2. 验证迁移结果
  3. 抽查帖子分类信息
  4. 负责人:开发人员
  5. 验收标准:所有帖子有默认值

阶段四:测试验证(预计 4 小时)

  1. 执行单元测试
  2. 执行集成测试
  3. 执行性能测试
  4. 负责人:测试人员
  5. 验收标准:所有测试项通过

阶段五:上线切换(预计 30 分钟)

  1. 选择低峰期(如凌晨 2-4 点)
  2. 切换代码到生产环境
  3. 验证生产环境功能
  4. 负责人:运维人员
  5. 验收标准:生产环境功能正常

6.2 回滚计划

如上线后发现问题,按以下步骤回滚:

  1. 立即回滚代码:还原 directory_tree.php 到旧版本
  2. 数据保留:分类信息数据保留,不影响现有功能
  3. 问题排查:在测试环境复现并修复问题
  4. 重新上线:问题修复后重新执行上线流程

七、时间估算和里程碑

7.1 时间估算

阶段 工时 备注
数据库准备 0.5 小时 创建分类和选项
代码开发 4 小时 修改 4 个文件
数据迁移 2 小时 脚本编写 + 执行
测试验证 4 小时 完整测试用例
上线部署 1 小时 含回滚准备
总计 11.5 小时 约 1.5 个工作日

7.2 里程碑

里程碑 交付物 验收标准
M1:数据库就绪 分类信息创建完成 后台可见,选项可编辑
M2:代码完成 directory_tree.php 修改完成 单元测试通过
M3:数据迁移完成 帖子分类信息初始化 抽查验证通过
M4:测试通过 测试报告 所有测试项通过
M5:上线成功 生产环境验证 功能正常,性能达标

八、风险与应对

8.1 技术风险

风险 影响 概率 应对措施
分类信息表数据量大 查询性能下降 添加索引,分页查询
迁移脚本执行失败 数据不一致 事务处理,回滚机制
旧格式兼容问题 部分目录失效 保留旧代码,单独测试

8.2 业务风险

风险 影响 概率 应对措施
用户不会设置分类信息 排序混乱 发布操作指引,培训版主
版主权限设置错误 普通用户修改排序 严格权限控制,审核机制
上线后出现 bug 用户体验受损 快速回滚,及时修复

九、附录

9.1 相关文件路径

template/xmyc_doc/php/directory_tree.php           # PC 版核心
template/xmyc_touch_doc/touch/php/directory_tree.php # 移动版核心
template/xmyc_doc/forum/forumdisplay.htm           # PC 版列表页
template/xmyc_touch_doc/touch/forum/forumdisplay.htm # 移动版列表页

9.2 数据库表结构

pre_forum_typeoption        # 分类信息选项定义
pre_forum_typeoptionvar     # 分类信息选项数据
pre_forum_typeoptionvalue   # 分类信息单选值
pre_forum_thread            # 帖子表
pre_common_tag              # 标签表
pre_common_tagitem          # 标签项目关联表

9.3 参考资料


文档版本:1.0
创建日期:2026-02-16
创建人:蜗牛

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条