初始问题
在讨论如何实现 [posts] 段落配置对帖子进行排序,具体需求是:
- 有 5 个帖子(tid:1,2,3,4,5)都在"技术手册"目录下
- 需要在"技术手册"目录下对这些帖子进行排序
实际问题诊断
通过调试发现实际问题是:
- 配置帖中使用了传统文字路径格式(
开发维护/社创开发)
- 帖子标签只使用了单层名称(
#目录_社创开发)
- 标签名与配置路径无法正确匹配,导致帖子无法归入目录
修改过程时间线
阶段一:问题诊断
1.1 添加 SQL 调试代码
问题:初始调试时 $_G['fid'] 变量为空,导致 SQL 语法错误
错误信息:
1064) You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version
for the right syntax to use near 'AND tag.tagname LIKE...'
修改内容:
- 修复变量访问:使用
$fid 参数代替 $_G['fid']
- 添加
intval() 确保类型正确
- 添加 fallback 逻辑,从全局变量获取 fid
文件:
template/xmyc_doc/php/directory_tree.php
template/xmyc_touch_doc/touch/php/directory_tree.php
1.2 调试输出结果
当前 fid: 23
所有带目录标签的帖子:
tid:118 subject:2025-12-26 开发记录 tags:目录_社创开发
tid:135 subject:社创行动派交流论坛介绍 tags:目录_开发维护
tid:137 subject:2026 年 1 月开发记录 tags:目录_社创开发
tid:140 subject:文档模式前端模板解析目录的开发 tags:目录_社创开发
目录配置:
directories:
[开发维护] => Array([name] => 网站开发维护 [sort_order] => 98)
[开发维护/社创开发] => Array([name] => 社创官网和社区开发 [sort_order] => 1)
posts:
[社创开发] => Array([0] => 118 [1] => 140 [2] => 137)
1.3 问题定位
- 配置路径:
开发维护/社创开发
- 帖子标签:
社创开发(单层)
- 匹配函数
find_dir_path_by_name() 只能匹配路径最后一级
阶段二:修复路径匹配
2.1 修改匹配逻辑
修改内容:
- 修复
[posts] 排序查找逻辑,使用标签名 $dir_name 代替完整路径 $dir_path
- 添加调试输出,显示帖子与目录的匹配过程
代码变更:
// 原代码
if(isset($directory_config['posts'][$dir_path])) {
$tid_index = array_search($_thread['tid'], $directory_config['posts'][$dir_path]);
}
// 修改后
if(isset($directory_config['posts'][$dir_name])) {
$tid_index = array_search($_thread['tid'], $directory_config['posts'][$dir_name]);
}
阶段三:方案优化讨论
3.1 用户需求确认
用户提出新方案需求:
- 置顶帖路径配置格式:
数字序号 = 标签名称 = 目录树名称
- 目录树层级直接解析数字序号来匹配完成
- 帖子只需要单层标签即可
配置格式示例:
1 = 开发维护 = 网站的开发维护
1-1 = 社创开发 = 社创行动派开发
1-1-1 = 文档开发 = 文档类内容展示逻辑开发
3.2 方案设计
新格式特点:
- 序号决定层级关系(
1 → 1-1 → 1-1-1)
- 标签名保持单层(
#目录_社创开发)
- 配置更清晰,维护更简单
阶段四:新格式实现
4.1 配置解析函数重写
函数:parse_config_message()
新增新格式解析:
// 新格式:序号 = 标签名 = 目录名
preg_match_all('/^([\d\-]+)\s*=\s*([^=]+)\s*=\s*(.+)$/m', $content, $new_matches);
if($new_matches[1]) {
foreach($new_matches[1] as $index => $num_path) {
$num_path = trim($new_matches[1][$index]);
$tag_name = trim($new_matches[2][$index]);
$dir_name = trim($new_matches[3][$index]);
$config['directories'][$num_path] = array(
'tag_name' => $tag_name,
'name' => $dir_name,
'sort_order' => $index,
'is_new_format' => true
);
}
}
保留旧格式兼容:
// 旧格式:序号 = 目录名。权重
preg_match_all('/^([^=\n]+)\s*=\s*([^.\n]+)\.(\d+)/m', $content, $dir_matches);
4.2 新增辅助函数
函数:find_num_path_by_tag()
function find_num_path_by_tag($directory_config, $tag_name) {
foreach($directory_config['directories'] as $num_path => $config) {
if($config['tag_name'] == $tag_name) {
return $num_path;
}
}
return '';
}
4.3 新增目录节点创建函数
函数:create_directory_node_new()
function create_directory_node_new(&$choices, $num_path, $name) {
// 将 1-1-1 转换为数组 [1, 1, 1]
$parts = explode('-', $num_path);
$current = &$choices;
$key_path = '';
$num_path_dot = '';
foreach($parts as $index => $part) {
$key_path .= ($key_path ? '-' : '') . $part;
$num_path_dot .= ($num_path_dot ? '.' : '') . $part;
if(!isset($current[$num_path_dot])) {
$current[$num_path_dot] = array(
'key' => $key_path,
'num' => $num_path_dot,
'level' => $index + 1,
'name' => $name,
'type' => 'directory',
'href' => generate_dir_link($num_path_dot),
'list' => array(),
'sort_order' => $index
);
}
$current = &$current[$num_path_dot]['list'];
}
}
4.4 新增帖子添加函数
函数:add_thread_to_directory_new()
function add_thread_to_directory_new(&$choices, $num_path, $thread, $sort_order = 0) {
$parts = explode('-', $num_path);
$current = &$choices;
$num_path_dot = implode('.', $parts);
// 导航到最后的目录
foreach($parts as $index => $part) {
$path = implode('.', array_slice($parts, 0, $index + 1));
if(isset($current[$path])) {
$current = &$current[$path]['list'];
} else {
return; // 目录不存在,跳过
}
}
// 添加帖子
$thread_key = 'tid_' . $thread['tid'];
if(!isset($current[$thread_key])) {
$current[$thread_key] = array(
'key' => $thread_key,
'num' => $thread['tid'],
'name' => $thread['subject'],
'type' => 'thread',
'href' => generate_thread_link($thread['tid']),
'list' => array(),
'sort_order' => $sort_order
);
}
}
4.5 主逻辑流程控制
// 检查是否使用新格式
$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($_tagged_threads as $_thread) {
$tag_names = extract_directory_tags($_thread['tags']);
foreach($tag_names as $tag_name) {
$num_path = find_num_path_by_tag($directory_config, $tag_name);
if($num_path) {
$sort_order = 0;
if(isset($directory_config['posts'][$tag_name])) {
$tid_index = array_search($_thread['tid'], $directory_config['posts'][$tag_name]);
if($tid_index !== false) {
$sort_order = $tid_index;
}
}
add_thread_to_directory_new($flquery['choices'], $num_path, $_thread, $sort_order);
}
}
}
} else {
// 旧格式处理(兼容)
}
修改文件清单
| 文件 |
修改类型 |
说明 |
template/xmyc_doc/php/directory_tree.php |
重写 |
PC 版目录树生成核心 |
template/xmyc_touch_doc/touch/php/directory_tree.php |
重写 |
移动版目录树生成核心 |
函数变更清单
新增函数
| 函数名 |
用途 |
版本 |
find_num_path_by_tag() |
根据标签名查找序号路径 |
新格式专用 |
create_directory_node_new() |
创建序号目录节点 |
新格式专用 |
add_thread_to_directory_new() |
添加帖子到序号目录 |
新格式专用 |
修改函数
| 函数名 |
修改内容 |
parse_config_message() |
新增新格式解析逻辑,保留旧格式兼容 |
extract_directory_tags() |
简化,移除路径转换逻辑 |
get_directory_threads() |
移除调试代码,优化 SQL |
保留函数(旧格式兼容)
| 函数名 |
用途 |
build_directory_tree() |
旧格式目录树构建 |
find_dir_path_by_name() |
旧格式路径匹配 |
配置格式对比
旧格式
[doc_directory]
开发维护 = 网站开发维护.98
开发维护/社创开发 = 社创官网和社区开发.1
开发维护/利智官网 = 北京利智官网.2
[posts]
社创开发 = tid:118,tid:140,tid:137
[/posts]
[/doc_directory]
新格式
[doc_directory]
1 = 开发维护 = 网站开发维护
1-1 = 社创开发 = 社创官网和社区开发
1-2 = 利智官网 = 北京利智官网
[posts]
社创开发 = tid:118,tid:140,tid:137
[/posts]
[/doc_directory]
技术要点
1. 序号路径解析
输入:1-1-1
解析:explode('-', '1-1-1') → [1, 1, 1]
层级:level=3
路径:1.1.1(内部存储格式)
KEY: 1-1-1(用于前端)
2. 标签匹配逻辑
帖子标签:#目录_社创开发
配置查找:遍历 directories 数组,匹配 tag_name
返回序号:1-1
目录路径:1.1(内部)
3. 目录树结构
$flquery['choices'] = array(
'1' => array(
'key' => '1',
'num' => '1',
'level' => 1,
'name' => '开发维护',
'type' => 'directory',
'href' => '...&dir=1',
'list' => array(
'1.1' => array(
'key' => '1-1',
'num' => '1.1',
'level' => 2,
'name' => '社创官网和社区开发',
'type' => 'directory',
'list' => array(
'tid_118' => array(
'key' => 'tid_118',
'num' => 118,
'name' => '2025-12-26 开发记录',
'type' => 'thread',
'href' => '...tid=118',
'sort_order' => 0
)
)
)
)
)
);
测试验证
测试场景
| 场景 |
预期结果 |
状态 |
| 新格式配置解析 |
正确解析序号、标签名、目录名 |
✓ |
| 旧格式配置解析 |
向后兼容,正常解析 |
✓ |
| 标签匹配序号 |
单层标签匹配到正确序号路径 |
✓ |
| 多层目录构建 |
1-1-1 正确构建三级目录 |
✓ |
| 帖子排序 |
[posts] 配置生效 |
✓ |
| Ajax 请求 |
getdirectory=true 返回 JSON |
✓ |
遇到的问题及解决方案
问题 1:fid 变量为空
现象:SQL 报错 WHERE t.fid= AND...
原因:$_G['fid'] 在函数作用域内不可用
解决:
$fid = intval($fid);
if(empty($fid)) {
global $_G;
$fid = intval($_G['fid']);
}
问题 2:标签与路径不匹配
现象:帖子无法归入目录
原因:配置使用完整路径 开发维护/社创开发,标签只有 社创开发
临时解决:修改匹配逻辑,使用标签名查找 [posts] 配置
最终解决:引入新格式,序号配置 + 单层标签
后续建议
功能增强
- 自动序号生成:配置时可省略序号,自动生成
- 目录权限控制:支持按目录设置访问权限
- 目录统计信息:显示每个目录下的文档数量
- 批量标签管理:提供批量修改帖子标签的工具
性能优化
- 缓存机制:目录树结果缓存,减少数据库查询
- Ajax 加载:大目录树分步加载
- 索引优化:为标签表添加组合索引
用户体验
- 配置可视化:提供配置帖生成工具
- 目录预览:编辑配置帖时实时预览目录结构
- 错误提示:配置错误时给出明确提示
总结
本次修改完成了目录树系统从文字路径配置到序号路径配置的升级,主要成果:
- 新格式更简洁:
序号 = 标签名 = 目录名 三要素清晰明了
- 标签更简单:帖子只需单层标签,降低维护成本
- 层级更清晰:序号直接体现目录层级关系
- 向后兼容:保留旧格式支持,平滑迁移
核心改进点:
- 配置解析支持新旧两种格式
- 新增序号路径匹配和目录构建函数
- 简化了帖子标签要求
- 优化了目录树生成逻辑
日志版本:1.0
记录日期:2026-02-28
记录人:蜗牛