#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 薪人薪事(xinrenxinshi) 功能调研 —— SQLite 数据存取工具(通用,无站点耦合) 用法: python dbtool.py init 初始化数据库表结构 python dbtool.py add 新增/更新一个功能节点(传入JSON对象) python dbtool.py addmany 批量新增/更新(传入JSON数组) python dbtool.py setshot 设置某节点的截图路径 python dbtool.py get 查询单个节点 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)