Files

115 lines
5.0 KiB
Python
Raw Permalink Normal View History

2026-09-03 15:35:23 +08:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
读取 db/menu_map.json, 构建功能树写入 SQLite(features 表)。
层级: level0=顶层模块, level1=分组, level2=菜单页(叶子)
并为每个叶子分配期望截图路径 screenshots/<序号_模块>/<分组>/<序号_名称>.jpg
menu_map.json 结构(由实时爬取生成):
{
"<模块名>": {
"loc": "落地页URL",
"desc": "模块释义(可选)",
"understanding": "开发理解(可选)",
"groups": [ {"title": "分组名", "items": [ {"name":"页名","href":"/路径","desc":"","understanding":""}, ...] } ],
"single": {"name":"页名","href":"/路径"} # 无侧边栏的单页模块(可选)
},
...
}
"""
import json, os, re, sqlite3
BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
MAP = os.path.join(BASE, "db", "menu_map.json")
DB = os.path.join(BASE, "db", "research.db")
ORIGIN = "https://s-demob.xinrenxinshi.show"
def sanitize(s):
s = re.sub(r'[\\/:*?"<>|]', "_", s or "").strip()
return s[:40] or "page"
def full_url(href):
if not href:
return ""
if href.startswith("http"):
return href
return ORIGIN + (href if href.startswith("/") else "/" + href)
def main():
data = json.load(open(MAP, encoding="utf-8"))
conn = sqlite3.connect(DB)
conn.row_factory = sqlite3.Row
cur = conn.cursor()
cur.execute("DELETE FROM features")
def up(obj):
keys = list(obj.keys()); cols = ",".join(keys); ph = ",".join(["?"] * len(keys))
cur.execute(f"INSERT OR REPLACE INTO features ({cols}) VALUES ({ph})", [obj[k] for k in keys])
for mi, module in enumerate(data.keys(), 1):
v = data[module]
mod_key = f"mod::{module}"
seq_mod = f"{mi:02d}_{sanitize(module)}"
up(dict(node_key=mod_key, parent_key="", level=0, name=module,
full_path=module, url=v.get("loc", ""),
description=v.get("desc", f"{module}模块。"),
understanding=v.get("understanding", ""), sort_order=mi, keep=1))
groups = v.get("groups", [])
single = v.get("single")
flat_items = v.get("items")
# 扁平模式: 模块直接挂功能页(level2), 无中间分组层
if not groups and flat_items:
for ii, it in enumerate(flat_items, 1):
nm = it["name"]; href = it.get("href", "")
leaf_key = f"{module}::{href or nm}"
rel = f"screenshots/{seq_mod}/{ii:02d}_{sanitize(nm)}.jpg"
up(dict(node_key=leaf_key, parent_key=mod_key, level=2, name=nm,
full_path=f"{module} / {nm}", url=full_url(href),
description=it.get("desc", f"{module}模块下「{nm}」相关功能页。"),
understanding=it.get("understanding", ""),
screenshot_path=rel, sort_order=ii, keep=1))
continue
if not groups and single:
nm = single["name"]; href = single.get("href", "")
leaf_key = f"{module}::{href or nm}"
rel = f"screenshots/{seq_mod}/{sanitize(nm)}.jpg"
up(dict(node_key=leaf_key, parent_key=mod_key, level=2, name=nm,
full_path=f"{module} / {nm}", url=full_url(href),
description=single.get("desc", f"{module}模块下「{nm}」相关功能页。"),
understanding=single.get("understanding", ""),
screenshot_path=rel, sort_order=1, keep=1))
continue
gi = 0
for g in groups:
gtitle = g.get("title") or "常规"
gi += 1
grp_key = f"{mod_key}::grp::{gi}_{gtitle}"
up(dict(node_key=grp_key, parent_key=mod_key, level=1, name=gtitle,
full_path=f"{module} / {gtitle}", url="",
description=f"{module}模块下「{gtitle}」功能分组。",
understanding="", sort_order=gi, keep=1))
for ii, it in enumerate(g.get("items", []), 1):
nm = it["name"]; href = it.get("href", "")
leaf_key = f"{module}::{href or (gtitle + '/' + nm)}"
rel = f"screenshots/{seq_mod}/{sanitize(gtitle)}/{ii:02d}_{sanitize(nm)}.jpg"
up(dict(node_key=leaf_key, parent_key=grp_key, level=2, name=nm,
full_path=f"{module} / {gtitle} / {nm}", url=full_url(href),
description=it.get("desc", f"{module}模块下「{nm}」相关功能页。"),
understanding=it.get("understanding", ""),
screenshot_path=rel, sort_order=ii, keep=1))
conn.commit()
for lv in (0, 1, 2):
n = cur.execute("SELECT COUNT(*) FROM features WHERE level=?", (lv,)).fetchone()[0]
print(f"level{lv}: {n}")
print("total:", cur.execute("SELECT COUNT(*) FROM features").fetchone()[0])
conn.close()
if __name__ == "__main__":
main()