216 lines
6.9 KiB
Python
216 lines
6.9 KiB
Python
"""
|
||
验证模板层级结构是否正确
|
||
"""
|
||
import os
|
||
import pymysql
|
||
from pathlib import Path
|
||
from typing import Dict, List, Optional
|
||
from dotenv import load_dotenv
|
||
|
||
# 加载环境变量
|
||
load_dotenv()
|
||
|
||
# 数据库配置
|
||
DB_CONFIG = {
|
||
'host': os.getenv('DB_HOST', '152.136.177.240'),
|
||
'port': int(os.getenv('DB_PORT', 5012)),
|
||
'user': os.getenv('DB_USER', 'finyx'),
|
||
'password': os.getenv('DB_PASSWORD', '6QsGK6MpePZDE57Z'),
|
||
'database': os.getenv('DB_NAME', 'finyx'),
|
||
'charset': 'utf8mb4'
|
||
}
|
||
|
||
|
||
def print_section(title):
|
||
"""打印章节标题"""
|
||
print("\n" + "="*70)
|
||
print(f" {title}")
|
||
print("="*70)
|
||
|
||
|
||
def get_actual_tenant_id(conn) -> int:
|
||
"""获取数据库中的实际tenant_id"""
|
||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||
try:
|
||
cursor.execute("SELECT DISTINCT tenant_id FROM f_polic_file_config LIMIT 1")
|
||
result = cursor.fetchone()
|
||
if result:
|
||
return result['tenant_id']
|
||
return 1
|
||
finally:
|
||
cursor.close()
|
||
|
||
|
||
def print_hierarchy(conn, tenant_id: int, parent_id: Optional[int] = None, level: int = 0, prefix: str = ""):
|
||
"""递归打印层级结构"""
|
||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||
|
||
try:
|
||
if parent_id is None:
|
||
sql = """
|
||
SELECT id, name, file_path, parent_id
|
||
FROM f_polic_file_config
|
||
WHERE tenant_id = %s AND parent_id IS NULL
|
||
ORDER BY name
|
||
"""
|
||
cursor.execute(sql, (tenant_id,))
|
||
else:
|
||
sql = """
|
||
SELECT id, name, file_path, parent_id
|
||
FROM f_polic_file_config
|
||
WHERE tenant_id = %s AND parent_id = %s
|
||
ORDER BY name
|
||
"""
|
||
cursor.execute(sql, (tenant_id, parent_id))
|
||
|
||
items = cursor.fetchall()
|
||
|
||
for i, item in enumerate(items):
|
||
is_last = (i == len(items) - 1)
|
||
current_prefix = "└── " if is_last else "├── "
|
||
next_prefix = prefix + (" " if is_last else "│ ")
|
||
|
||
if item['file_path']:
|
||
# 文件节点
|
||
print(f"{prefix}{current_prefix}{item['name']} (文件, ID: {item['id']})")
|
||
else:
|
||
# 目录节点
|
||
print(f"{prefix}{current_prefix}{item['name']} (目录, ID: {item['id']})")
|
||
# 递归打印子节点
|
||
print_hierarchy(conn, tenant_id, item['id'], level + 1, next_prefix)
|
||
finally:
|
||
cursor.close()
|
||
|
||
|
||
def verify_hierarchy(conn, tenant_id: int):
|
||
"""验证层级结构"""
|
||
print_section("验证模板层级结构")
|
||
|
||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||
|
||
try:
|
||
# 1. 统计信息
|
||
print("1. 统计信息:")
|
||
|
||
# 根节点(parent_id为NULL)
|
||
cursor.execute("""
|
||
SELECT COUNT(*) as count
|
||
FROM f_polic_file_config
|
||
WHERE tenant_id = %s AND parent_id IS NULL
|
||
""", (tenant_id,))
|
||
root_count = cursor.fetchone()['count']
|
||
print(f" 根节点: {root_count} 个")
|
||
|
||
# 目录节点(file_path为NULL)
|
||
cursor.execute("""
|
||
SELECT COUNT(*) as count
|
||
FROM f_polic_file_config
|
||
WHERE tenant_id = %s AND file_path IS NULL
|
||
""", (tenant_id,))
|
||
dir_count = cursor.fetchone()['count']
|
||
print(f" 目录节点: {dir_count} 个")
|
||
|
||
# 文件节点(file_path不为NULL)
|
||
cursor.execute("""
|
||
SELECT COUNT(*) as count
|
||
FROM f_polic_file_config
|
||
WHERE tenant_id = %s AND file_path IS NOT NULL
|
||
""", (tenant_id,))
|
||
file_count = cursor.fetchone()['count']
|
||
print(f" 文件节点: {file_count} 个")
|
||
|
||
# 有父级的文件
|
||
cursor.execute("""
|
||
SELECT COUNT(*) as count
|
||
FROM f_polic_file_config
|
||
WHERE tenant_id = %s
|
||
AND file_path IS NOT NULL
|
||
AND parent_id IS NOT NULL
|
||
""", (tenant_id,))
|
||
file_with_parent = cursor.fetchone()['count']
|
||
print(f" 有父级的文件: {file_with_parent} 个")
|
||
|
||
# 没有父级的文件
|
||
cursor.execute("""
|
||
SELECT COUNT(*) as count
|
||
FROM f_polic_file_config
|
||
WHERE tenant_id = %s
|
||
AND file_path IS NOT NULL
|
||
AND parent_id IS NULL
|
||
""", (tenant_id,))
|
||
file_without_parent = cursor.fetchone()['count']
|
||
print(f" 没有父级的文件: {file_without_parent} 个")
|
||
|
||
# 2. 显示层级结构
|
||
print("\n2. 层级结构:")
|
||
print_hierarchy(conn, tenant_id)
|
||
|
||
# 3. 检查问题
|
||
print("\n3. 检查潜在问题:")
|
||
|
||
# 检查是否有孤立的文件(没有父级,但应该在某个目录下)
|
||
cursor.execute("""
|
||
SELECT id, name, file_path
|
||
FROM f_polic_file_config
|
||
WHERE tenant_id = %s
|
||
AND file_path IS NOT NULL
|
||
AND parent_id IS NULL
|
||
AND file_path LIKE 'template_finish/%%/%%'
|
||
""", (tenant_id,))
|
||
isolated_files = cursor.fetchall()
|
||
|
||
if isolated_files:
|
||
print(f" [警告] 发现 {len(isolated_files)} 个可能孤立的文件(有路径但无父级):")
|
||
for file in isolated_files[:5]:
|
||
print(f" - {file['name']} (ID: {file['id']})")
|
||
print(f" 路径: {file['file_path']}")
|
||
else:
|
||
print(" [OK] 没有发现孤立的文件")
|
||
|
||
# 检查是否有循环引用
|
||
cursor.execute("""
|
||
SELECT t1.id, t1.name, t1.parent_id, t2.id as parent_exists
|
||
FROM f_polic_file_config t1
|
||
LEFT JOIN f_polic_file_config t2 ON t1.parent_id = t2.id AND t1.tenant_id = t2.tenant_id
|
||
WHERE t1.tenant_id = %s
|
||
AND t1.parent_id IS NOT NULL
|
||
AND t2.id IS NULL
|
||
""", (tenant_id,))
|
||
broken_links = cursor.fetchall()
|
||
|
||
if broken_links:
|
||
print(f" [警告] 发现 {len(broken_links)} 个断开的父级链接:")
|
||
for link in broken_links[:5]:
|
||
print(f" - {link['name']} (ID: {link['id']}, parent_id: {link['parent_id']})")
|
||
else:
|
||
print(" [OK] 没有发现断开的父级链接")
|
||
|
||
finally:
|
||
cursor.close()
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
print_section("验证模板层级结构")
|
||
|
||
try:
|
||
conn = pymysql.connect(**DB_CONFIG)
|
||
print("[OK] 数据库连接成功")
|
||
except Exception as e:
|
||
print(f"[FAIL] 数据库连接失败: {str(e)}")
|
||
return
|
||
|
||
try:
|
||
tenant_id = get_actual_tenant_id(conn)
|
||
print(f"实际tenant_id: {tenant_id}")
|
||
|
||
verify_hierarchy(conn, tenant_id)
|
||
|
||
finally:
|
||
conn.close()
|
||
print("\n[OK] 数据库连接已关闭")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|