162 lines
5.9 KiB
Python
162 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
薪人薪事(xinrenxinshi) 功能调研 —— SQLite 数据存取工具(通用,无站点耦合)
|
|
|
|
用法:
|
|
python dbtool.py init 初始化数据库表结构
|
|
python dbtool.py add <json> 新增/更新一个功能节点(传入JSON对象)
|
|
python dbtool.py addmany <json_array> 批量新增/更新(传入JSON数组)
|
|
python dbtool.py setshot <node_key> <path> 设置某节点的截图路径
|
|
python dbtool.py get <node_key> 查询单个节点
|
|
python dbtool.py tree 以树形结构打印(JSON)
|
|
python dbtool.py stats 统计各层级节点数
|
|
|
|
节点字段:
|
|
node_key 唯一键(建议用 URL 或 path 组合, 用于去重更新)
|
|
parent_key 父节点的 node_key (顶层为空)
|
|
level 0=顶层模块 1=分组 2=菜单页 3=子页/Tab
|
|
name 功能名称
|
|
full_path 全路径名称(面包屑, 如 "人事 / 员工管理 / 员工花名册")
|
|
url 页面URL
|
|
description 释义(功能是做什么的)
|
|
understanding 我理解的信息(交互/字段/价值等)
|
|
screenshot_path 截图相对路径
|
|
sort_order 排序
|
|
keep 是否保留(供管理员勾选, 默认1=保留)
|
|
note 审核备注
|
|
"""
|
|
import sqlite3, sys, json, os
|
|
|
|
DB_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "db", "research.db")
|
|
|
|
SCHEMA = """
|
|
CREATE TABLE IF NOT EXISTS features (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
node_key TEXT UNIQUE NOT NULL,
|
|
parent_key TEXT,
|
|
level INTEGER NOT NULL DEFAULT 0,
|
|
name TEXT NOT NULL,
|
|
full_path TEXT,
|
|
url TEXT,
|
|
description TEXT,
|
|
understanding TEXT,
|
|
screenshot_path TEXT,
|
|
sort_order INTEGER DEFAULT 0,
|
|
keep INTEGER DEFAULT 1,
|
|
note TEXT,
|
|
created_at TEXT DEFAULT (datetime('now','localtime')),
|
|
updated_at TEXT DEFAULT (datetime('now','localtime'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_parent ON features(parent_key);
|
|
CREATE INDEX IF NOT EXISTS idx_level ON features(level);
|
|
|
|
-- 深层界面截图(弹窗/筛选/自定义列/详情)
|
|
CREATE TABLE IF NOT EXISTS page_shots (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
node_key TEXT NOT NULL,
|
|
kind TEXT NOT NULL, -- main/add/filter/columns/detail
|
|
label TEXT,
|
|
path TEXT NOT NULL,
|
|
created_at TEXT DEFAULT (datetime('now','localtime'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_ps_node ON page_shots(node_key);
|
|
|
|
-- 真实导出附件
|
|
CREATE TABLE IF NOT EXISTS downloads (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
node_key TEXT,
|
|
file_path TEXT NOT NULL,
|
|
size INTEGER,
|
|
kind TEXT,
|
|
created_at TEXT DEFAULT (datetime('now','localtime'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_dl_node ON downloads(node_key);
|
|
"""
|
|
|
|
FIELDS = ["node_key","parent_key","level","name","full_path","url",
|
|
"description","understanding","screenshot_path","sort_order","keep","note"]
|
|
|
|
def conn():
|
|
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
|
|
c = sqlite3.connect(DB_PATH)
|
|
c.row_factory = sqlite3.Row
|
|
return c
|
|
|
|
def init():
|
|
c = conn()
|
|
c.executescript(SCHEMA)
|
|
c.commit(); c.close()
|
|
print("DB initialized at", DB_PATH)
|
|
|
|
def upsert(c, obj):
|
|
keys = [k for k in FIELDS if k in obj]
|
|
placeholders = ",".join(["?"]*len(keys))
|
|
cols = ",".join(keys)
|
|
updates = ",".join([f"{k}=excluded.{k}" for k in keys if k != "node_key"])
|
|
sql = f"INSERT INTO features ({cols}) VALUES ({placeholders}) " \
|
|
f"ON CONFLICT(node_key) DO UPDATE SET {updates}, updated_at=datetime('now','localtime')"
|
|
c.execute(sql, [obj[k] for k in keys])
|
|
|
|
def add(obj):
|
|
c = conn(); upsert(c, obj); c.commit(); c.close()
|
|
print("ok:", obj.get("node_key"))
|
|
|
|
def addmany(arr):
|
|
c = conn()
|
|
for obj in arr:
|
|
upsert(c, obj)
|
|
c.commit(); c.close()
|
|
print("ok addmany:", len(arr))
|
|
|
|
def setshot(node_key, path):
|
|
c = conn()
|
|
c.execute("UPDATE features SET screenshot_path=?, updated_at=datetime('now','localtime') WHERE node_key=?", (path, node_key))
|
|
c.commit(); n = c.total_changes; c.close()
|
|
print("updated" if n else "not found", node_key)
|
|
|
|
def get(node_key):
|
|
c = conn()
|
|
r = c.execute("SELECT * FROM features WHERE node_key=?", (node_key,)).fetchone()
|
|
c.close()
|
|
print(json.dumps(dict(r), ensure_ascii=False) if r else "null")
|
|
|
|
def stats():
|
|
c = conn()
|
|
rows = c.execute("SELECT level, COUNT(*) n FROM features GROUP BY level ORDER BY level").fetchall()
|
|
total = c.execute("SELECT COUNT(*) n FROM features").fetchone()["n"]
|
|
shots = c.execute("SELECT COUNT(*) n FROM features WHERE screenshot_path IS NOT NULL AND screenshot_path<>''").fetchone()["n"]
|
|
c.close()
|
|
for r in rows:
|
|
print(f"level {r['level']}: {r['n']}")
|
|
print(f"total: {total}, with_screenshot: {shots}")
|
|
|
|
def tree():
|
|
c = conn()
|
|
rows = [dict(r) for r in c.execute("SELECT * FROM features ORDER BY level, sort_order, id").fetchall()]
|
|
c.close()
|
|
by_parent = {}
|
|
for r in rows:
|
|
by_parent.setdefault(r["parent_key"] or "", []).append(r)
|
|
def build(pk):
|
|
out = []
|
|
for r in by_parent.get(pk, []):
|
|
node = {k: r[k] for k in ("node_key","name","level","url","description","understanding","screenshot_path","keep")}
|
|
node["children"] = build(r["node_key"])
|
|
out.append(node)
|
|
return out
|
|
print(json.dumps(build(""), ensure_ascii=False))
|
|
|
|
if __name__ == "__main__":
|
|
cmd = sys.argv[1] if len(sys.argv) > 1 else ""
|
|
if cmd == "init": init()
|
|
elif cmd == "add": add(json.loads(sys.argv[2]))
|
|
elif cmd == "addmany": addmany(json.loads(sys.argv[2]))
|
|
elif cmd == "setshot": setshot(sys.argv[2], sys.argv[3])
|
|
elif cmd == "get": get(sys.argv[2])
|
|
elif cmd == "stats": stats()
|
|
elif cmd == "tree": tree()
|
|
else:
|
|
print(__doc__)
|
|
sys.exit(1)
|