#!/usr/bin/env python3 # -*- coding: utf-8 -*- import psycopg2 import os import sys from dotenv import load_dotenv load_dotenv("../../.env", override=True) DB_CONFIG = { "host": os.getenv("ROOT_DB_HOST", "localhost"), "port": os.getenv("ROOT_DB_PORT", "5432"), "dbname": os.getenv("ROOT_DB_NAME", "mydb"), "user": os.getenv("ROOT_DB_USER", "postgres"), "password": os.getenv("ROOT_DB_PASSWD", "postgres"), } def fetch_nodes(): conn = psycopg2.connect(**DB_CONFIG) cur = conn.cursor() cur.execute("SELECT id, name, parent_id FROM fst") rows = cur.fetchall() cur.close() conn.close() return rows def build_tree(rows): nodes = {} for id_, name, parent_id in rows: nodes[id_] = {"id": id_, "name": name, "parent_id": parent_id, "children": []} roots = [] for n in nodes.values(): if n["parent_id"] is None or n["parent_id"] not in nodes: roots.append(n) else: nodes[n["parent_id"]]["children"].append(n) def sort_rec(n): n["children"].sort(key=lambda x: x["name"]) for c in n["children"]: sort_rec(c) for r in roots: sort_rec(r) return roots def print_subtree(node, prefix="", is_last=True, depth=0): # depth==0: 根节点;depth>=1: 各层子节点 if depth == 0: # 根直接打印名字 print(node["name"]) else: # 非根:打印前置 prefix + 连接符 + 名字 connector = "└── " if is_last else "├── " print(prefix + connector + node["name"]) # 计算传给下一层孩子的前缀 if depth == 0: # 第一层孩子不缩进,prefix 仍然空 child_prefix = "" else: # depth>=1:如果我是本层最后一个,用空格填充,否则保留竖线 child_prefix = prefix + (" " if is_last else "│ ") # 递归打印子节点 cnt = len(node["children"]) for idx, child in enumerate(node["children"]): last = idx == cnt - 1 print_subtree(child, child_prefix, last, depth + 1) def main(): rows = fetch_nodes() if not rows: print("fst 表中没有数据。", file=sys.stderr) sys.exit(1) roots = build_tree(rows) for idx, r in enumerate(roots): last_root = idx == len(roots) - 1 print_subtree(r, prefix="", is_last=last_root, depth=0) if __name__ == "__main__": main()