多软件归属:软件→分类两级,配置全部入库 (#1)
原先分类写死在 config.example.php,后台改不了;也没有「软件」概念。 现在改为数据库驱动的两级结构,后台可配(管理界面待做)。 ## 数据层(lib/db.php) - 新增 software / categories / statuses 三张表 - categories.software_id 外键:每个软件有自己的分类集合,可同名互不干扰 - threads 加 software_id / category_id;老 category 文本列保留不删(可回退) - fb_db_migrate_taxonomy() 幂等迁移:建表 → 空表灌初始数据 → 回填老帖子 ## 迁移实测 - 老 5 个分类(使用问题/Bug 反馈/功能建议/同步问题/其他)原样落进默认软件 - 老帖子按 category slug 回填 category_id,匹配不到的留 NULL ## 状态(5 个) 待处理 / 处理中 / 已解决 / 已关闭 / 不予考虑(wontfix) 每个状态带 user_note「给用户看的话」,后台可改,将进邮件正文。 待处理 = 刚提交:您提交的反馈已收到,我们正在处理中。 ## 前台 - index.php:软件切换条 + 分类 chip(一级软件、二级分类) - new.php:选软件、分类下拉随软件变、服务端校验分类归属 - URL:?sw=<slug>&cat=<slug>;老链接 ?cat=xxx 解析不到时退回全部,200 不 404 ## 界面一致性 按用户要求去掉「单软件时隐藏软件选择器」的逻辑: 所有界面一致,配一个就列一个,配多个就列多个。 ## 兼容 fb_categories() 现在必须传软件 id;fb_statuses() 返回行数组, 另有 fb_status_keys() 取 key=>name 映射。 验证:WSL 内临时实例实测——单软件显示 1 个软件条;两软件各显示 自己的分类集合与帖子,互不串;无 PHP 警告/致命错误。
This commit is contained in:
+113
-8
@@ -58,16 +58,121 @@ function fb_redirect($url) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/** 分类 / 状态 */
|
||||
function fb_categories() { return (array)fb_cfg('categories', []); }
|
||||
function fb_category_label($k) {
|
||||
$c = fb_categories();
|
||||
return isset($c[$k]) ? $c[$k] : '其他';
|
||||
/** ================= 软件 / 分类 / 状态 =================
|
||||
* 三者全部存数据库、后台可配,不再读配置文件。
|
||||
* 结构:软件 → 分类(两级)。分类属于某个软件,不同软件可有同名分类。 */
|
||||
|
||||
/** 全部软件(按排序),$onlyEnabled=true 时只取启用的 */
|
||||
function fb_softwares($onlyEnabled = true) {
|
||||
$sql = 'SELECT * FROM software';
|
||||
if ($onlyEnabled) { $sql .= ' WHERE enabled = 1'; }
|
||||
$sql .= ' ORDER BY sort_order, id';
|
||||
$rows = fb_db()->query($sql)->fetchAll();
|
||||
$out = [];
|
||||
foreach ($rows as $r) { $out[] = $r; }
|
||||
return $out;
|
||||
}
|
||||
function fb_statuses() { return (array)fb_cfg('statuses', []); }
|
||||
|
||||
/** 按 slug 或 id 取一个软件;取不到返回 null */
|
||||
function fb_software($key) {
|
||||
if ($key === null || $key === '') { return null; }
|
||||
$pdo = fb_db();
|
||||
if (ctype_digit((string)$key)) {
|
||||
$st = $pdo->prepare('SELECT * FROM software WHERE id = ? LIMIT 1');
|
||||
$st->execute([(int)$key]);
|
||||
} else {
|
||||
$st = $pdo->prepare('SELECT * FROM software WHERE slug = ? LIMIT 1');
|
||||
$st->execute([(string)$key]);
|
||||
}
|
||||
$r = $st->fetch();
|
||||
return $r ? $r : null;
|
||||
}
|
||||
|
||||
/** 默认软件:排序最前的启用软件;一个都没有时返回 null */
|
||||
function fb_default_software() {
|
||||
$list = fb_softwares(true);
|
||||
return $list ? $list[0] : null;
|
||||
}
|
||||
|
||||
/** 某软件下的分类;不传软件则返回空数组 */
|
||||
function fb_categories($softwareId = null, $onlyEnabled = true) {
|
||||
if ($softwareId === null) { return []; }
|
||||
$sql = 'SELECT * FROM categories WHERE software_id = ?';
|
||||
$args = [(int)$softwareId];
|
||||
if ($onlyEnabled) { $sql .= ' AND enabled = 1'; }
|
||||
$sql .= ' ORDER BY sort_order, id';
|
||||
$st = fb_db()->prepare($sql);
|
||||
$st->execute($args);
|
||||
return $st->fetchAll();
|
||||
}
|
||||
|
||||
/** 按 id 取分类(含禁用的,后台编辑用) */
|
||||
function fb_category($id) {
|
||||
if (!$id) { return null; }
|
||||
$st = fb_db()->prepare('SELECT * FROM categories WHERE id = ? LIMIT 1');
|
||||
$st->execute([(int)$id]);
|
||||
$r = $st->fetch();
|
||||
return $r ? $r : null;
|
||||
}
|
||||
|
||||
/** 在某软件内按 slug 取分类 */
|
||||
function fb_category_by_slug($softwareId, $slug) {
|
||||
if (!$softwareId || $slug === '') { return null; }
|
||||
$st = fb_db()->prepare('SELECT * FROM categories WHERE software_id = ? AND slug = ? LIMIT 1');
|
||||
$st->execute([(int)$softwareId, (string)$slug]);
|
||||
$r = $st->fetch();
|
||||
return $r ? $r : null;
|
||||
}
|
||||
|
||||
/** 分类显示名:只给 id 就能拿到名字(列表渲染用) */
|
||||
function fb_category_label($id) {
|
||||
if (!$id) { return ''; }
|
||||
static $cache = [];
|
||||
$id = (int)$id;
|
||||
if (!isset($cache[$id])) {
|
||||
$c = fb_category($id);
|
||||
$cache[$id] = $c ? (string)$c['name'] : '';
|
||||
}
|
||||
return $cache[$id];
|
||||
}
|
||||
|
||||
/** 全部状态(按排序) */
|
||||
function fb_statuses($onlyEnabled = true) {
|
||||
$sql = 'SELECT * FROM statuses';
|
||||
if ($onlyEnabled) { $sql .= ' WHERE enabled = 1'; }
|
||||
$sql .= ' ORDER BY sort_order, id';
|
||||
return fb_db()->query($sql)->fetchAll();
|
||||
}
|
||||
|
||||
/** 状态 key 列表(校验用) */
|
||||
function fb_status_keys() {
|
||||
$out = [];
|
||||
foreach (fb_statuses(true) as $s) { $out[(string)$s['key_name']] = (string)$s['name']; }
|
||||
return $out;
|
||||
}
|
||||
|
||||
/** 状态显示名 */
|
||||
function fb_status_label($k) {
|
||||
$s = fb_statuses();
|
||||
return isset($s[$k]) ? $s[$k] : (string)$k;
|
||||
static $cache = null;
|
||||
if ($cache === null) {
|
||||
$cache = [];
|
||||
foreach (fb_statuses(false) as $s) { $cache[(string)$s['key_name']] = (string)$s['name']; }
|
||||
}
|
||||
$k = (string)$k;
|
||||
return isset($cache[$k]) ? $cache[$k] : $k;
|
||||
}
|
||||
|
||||
/** 状态的「给用户看的话」——进邮件正文,后台可改 */
|
||||
function fb_status_user_note($k) {
|
||||
static $cache = null;
|
||||
if ($cache === null) {
|
||||
$cache = [];
|
||||
foreach (fb_statuses(false) as $s) {
|
||||
$cache[(string)$s['key_name']] = (string)($s['user_note'] === null ? '' : $s['user_note']);
|
||||
}
|
||||
}
|
||||
$k = (string)$k;
|
||||
return isset($cache[$k]) ? $cache[$k] : '';
|
||||
}
|
||||
|
||||
/** CSRF */
|
||||
|
||||
Reference in New Issue
Block a user