再次修复模板目录层级结构
This commit is contained in:
parent
ac8bdba941
commit
7bb69af45e
147
fix_isolated_template.py
Normal file
147
fix_isolated_template.py
Normal file
@ -0,0 +1,147 @@
|
||||
"""
|
||||
修复孤立的模板文件(有路径但无父级)
|
||||
"""
|
||||
import os
|
||||
import pymysql
|
||||
from pathlib import Path
|
||||
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'
|
||||
}
|
||||
|
||||
UPDATED_BY = 655162080928945152
|
||||
|
||||
|
||||
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 find_parent_directory(conn, tenant_id: int, file_path: str) -> int:
|
||||
"""根据文件路径找到父目录ID"""
|
||||
# 从文件路径中提取父目录路径
|
||||
path_parts = file_path.split('/')
|
||||
if len(path_parts) < 2:
|
||||
return None
|
||||
|
||||
# 父目录路径(去掉文件名)
|
||||
parent_path = '/'.join(path_parts[:-1])
|
||||
parent_dir_name = path_parts[-2] # 父目录名称
|
||||
|
||||
# 查找父目录(通过名称匹配,且file_path为NULL)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
try:
|
||||
sql = """
|
||||
SELECT id, name
|
||||
FROM f_polic_file_config
|
||||
WHERE tenant_id = %s
|
||||
AND name = %s
|
||||
AND file_path IS NULL
|
||||
ORDER BY id
|
||||
LIMIT 1
|
||||
"""
|
||||
cursor.execute(sql, (tenant_id, parent_dir_name))
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
return result['id']
|
||||
return None
|
||||
finally:
|
||||
cursor.close()
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("="*70)
|
||||
print("修复孤立的模板文件")
|
||||
print("="*70)
|
||||
|
||||
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}")
|
||||
|
||||
# 查找孤立的文件(有路径但无父级,且路径包含至少2级)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
try:
|
||||
sql = """
|
||||
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/%%/%%'
|
||||
"""
|
||||
cursor.execute(sql, (tenant_id,))
|
||||
isolated_files = cursor.fetchall()
|
||||
|
||||
if not isolated_files:
|
||||
print("[OK] 没有发现孤立的文件")
|
||||
return
|
||||
|
||||
print(f"\n发现 {len(isolated_files)} 个孤立的文件:")
|
||||
|
||||
fixed_count = 0
|
||||
for file in isolated_files:
|
||||
print(f"\n 文件: {file['name']}")
|
||||
print(f" ID: {file['id']}")
|
||||
print(f" 路径: {file['file_path']}")
|
||||
|
||||
# 查找父目录
|
||||
parent_id = find_parent_directory(conn, tenant_id, file['file_path'])
|
||||
|
||||
if parent_id:
|
||||
# 更新parent_id
|
||||
update_cursor = conn.cursor()
|
||||
try:
|
||||
update_cursor.execute("""
|
||||
UPDATE f_polic_file_config
|
||||
SET parent_id = %s, updated_time = NOW(), updated_by = %s
|
||||
WHERE id = %s AND tenant_id = %s
|
||||
""", (parent_id, UPDATED_BY, file['id'], tenant_id))
|
||||
conn.commit()
|
||||
print(f" [修复] 设置parent_id: {parent_id}")
|
||||
fixed_count += 1
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
print(f" [错误] 更新失败: {str(e)}")
|
||||
finally:
|
||||
update_cursor.close()
|
||||
else:
|
||||
print(f" [警告] 未找到父目录")
|
||||
|
||||
print(f"\n[OK] 成功修复 {fixed_count} 个文件")
|
||||
|
||||
finally:
|
||||
cursor.close()
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
print("[OK] 数据库连接已关闭")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
412
update_template_hierarchy_from_directory.py
Normal file
412
update_template_hierarchy_from_directory.py
Normal file
@ -0,0 +1,412 @@
|
||||
"""
|
||||
根据template_finish/目录结构更新数据库中的parent_id层级关系
|
||||
1. 扫描目录结构
|
||||
2. 创建/更新目录节点(作为父级)
|
||||
3. 更新文件节点的parent_id
|
||||
"""
|
||||
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'
|
||||
}
|
||||
|
||||
CREATED_BY = 655162080928945152
|
||||
UPDATED_BY = 655162080928945152
|
||||
|
||||
# 项目根目录
|
||||
PROJECT_ROOT = Path(__file__).parent
|
||||
TEMPLATES_DIR = PROJECT_ROOT / "template_finish"
|
||||
|
||||
|
||||
def print_section(title):
|
||||
"""打印章节标题"""
|
||||
print("\n" + "="*70)
|
||||
print(f" {title}")
|
||||
print("="*70)
|
||||
|
||||
|
||||
def print_result(success, message):
|
||||
"""打印结果"""
|
||||
status = "[OK]" if success else "[FAIL]"
|
||||
print(f"{status} {message}")
|
||||
|
||||
|
||||
def generate_id():
|
||||
"""生成ID"""
|
||||
import time
|
||||
return int(time.time() * 1000000)
|
||||
|
||||
|
||||
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 scan_directory_structure(base_dir: Path) -> Dict:
|
||||
"""
|
||||
扫描目录结构,构建层级关系
|
||||
|
||||
Returns:
|
||||
字典,包含目录和文件的层级信息
|
||||
"""
|
||||
structure = {
|
||||
'directories': [], # 目录节点列表
|
||||
'files': [] # 文件节点列表
|
||||
}
|
||||
|
||||
if not base_dir.exists():
|
||||
return structure
|
||||
|
||||
# 遍历所有目录和文件
|
||||
for item in base_dir.rglob("*"):
|
||||
relative_path = item.relative_to(PROJECT_ROOT)
|
||||
relative_path_str = str(relative_path).replace('\\', '/')
|
||||
parts = relative_path.parts
|
||||
|
||||
if item.is_dir():
|
||||
# 目录节点
|
||||
level = len(parts) - 1 # 层级(从0开始,template_finish是0级)
|
||||
dir_name = parts[-1]
|
||||
parent_path = str(Path(*parts[:-1])).replace('\\', '/') if len(parts) > 1 else None
|
||||
|
||||
structure['directories'].append({
|
||||
'name': dir_name,
|
||||
'path': relative_path_str,
|
||||
'level': level,
|
||||
'parent_path': parent_path
|
||||
})
|
||||
|
||||
elif item.is_file() and item.suffix.lower() in ['.docx', '.doc', '.wps']:
|
||||
# 文件节点
|
||||
level = len(parts) - 1
|
||||
file_name = item.name
|
||||
parent_path = str(Path(*parts[:-1])).replace('\\', '/') if len(parts) > 1 else None
|
||||
|
||||
structure['files'].append({
|
||||
'name': file_name,
|
||||
'path': relative_path_str,
|
||||
'level': level,
|
||||
'parent_path': parent_path
|
||||
})
|
||||
|
||||
# 按层级排序目录(确保父目录先处理)
|
||||
structure['directories'].sort(key=lambda x: (x['level'], x['path']))
|
||||
|
||||
return structure
|
||||
|
||||
|
||||
def get_existing_templates(conn, tenant_id: int) -> Dict:
|
||||
"""
|
||||
获取数据库中现有的模板记录
|
||||
|
||||
Returns:
|
||||
字典,包含按路径和名称索引的模板
|
||||
"""
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
|
||||
try:
|
||||
sql = """
|
||||
SELECT id, name, parent_id, file_path, state
|
||||
FROM f_polic_file_config
|
||||
WHERE tenant_id = %s
|
||||
"""
|
||||
cursor.execute(sql, (tenant_id,))
|
||||
templates = cursor.fetchall()
|
||||
|
||||
# 按file_path索引(用于文件匹配)
|
||||
by_path = {}
|
||||
# 按name索引(用于目录匹配)
|
||||
by_name = {}
|
||||
|
||||
for template in templates:
|
||||
if template['file_path']:
|
||||
by_path[template['file_path']] = template
|
||||
else:
|
||||
# 目录节点(file_path为NULL)
|
||||
name = template['name']
|
||||
if name not in by_name:
|
||||
by_name[name] = []
|
||||
by_name[name].append(template)
|
||||
|
||||
return {
|
||||
'by_path': by_path,
|
||||
'by_name': by_name
|
||||
}
|
||||
finally:
|
||||
cursor.close()
|
||||
|
||||
|
||||
def create_or_update_directory(conn, tenant_id: int, dir_name: str, dir_path: str, parent_id: Optional[int], existing_templates: Dict) -> int:
|
||||
"""
|
||||
创建或更新目录节点
|
||||
|
||||
Returns:
|
||||
目录节点的ID
|
||||
"""
|
||||
cursor = conn.cursor()
|
||||
|
||||
try:
|
||||
# 查找现有目录(优先通过名称查找,且file_path为NULL)
|
||||
existing = None
|
||||
if dir_name in existing_templates['by_name']:
|
||||
# 找到同名的目录节点
|
||||
for template in existing_templates['by_name'][dir_name]:
|
||||
if template['file_path'] is None:
|
||||
existing = template
|
||||
break
|
||||
|
||||
if existing:
|
||||
# 更新现有目录记录
|
||||
template_id = existing['id']
|
||||
if existing['parent_id'] != parent_id:
|
||||
update_sql = """
|
||||
UPDATE f_polic_file_config
|
||||
SET parent_id = %s, updated_time = NOW(), updated_by = %s, state = 1
|
||||
WHERE id = %s AND tenant_id = %s
|
||||
"""
|
||||
cursor.execute(update_sql, (parent_id, UPDATED_BY, template_id, tenant_id))
|
||||
conn.commit()
|
||||
print(f" [UPDATE] 更新目录: {dir_name} (ID: {template_id}, parent_id: {parent_id})")
|
||||
else:
|
||||
print(f" [KEEP] 保持目录: {dir_name} (ID: {template_id})")
|
||||
return template_id
|
||||
else:
|
||||
# 创建新目录记录
|
||||
template_id = generate_id()
|
||||
insert_sql = """
|
||||
INSERT INTO f_polic_file_config
|
||||
(id, tenant_id, parent_id, name, input_data, file_path, created_time, created_by, updated_time, updated_by, state)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, NOW(), %s, NOW(), %s, 1)
|
||||
"""
|
||||
cursor.execute(insert_sql, (
|
||||
template_id,
|
||||
tenant_id,
|
||||
parent_id,
|
||||
dir_name,
|
||||
'{}', # input_data
|
||||
None, # file_path(目录节点没有文件路径)
|
||||
CREATED_BY,
|
||||
UPDATED_BY
|
||||
))
|
||||
conn.commit()
|
||||
print(f" [CREATE] 创建目录: {dir_name} (ID: {template_id}, parent_id: {parent_id})")
|
||||
|
||||
# 更新existing_templates
|
||||
if dir_name not in existing_templates['by_name']:
|
||||
existing_templates['by_name'][dir_name] = []
|
||||
existing_templates['by_name'][dir_name].append({
|
||||
'id': template_id,
|
||||
'name': dir_name,
|
||||
'parent_id': parent_id,
|
||||
'file_path': None,
|
||||
'state': 1
|
||||
})
|
||||
|
||||
return template_id
|
||||
finally:
|
||||
cursor.close()
|
||||
|
||||
|
||||
def update_file_parent(conn, tenant_id: int, file_info: Dict, parent_id: Optional[int], existing_templates: Dict):
|
||||
"""更新文件节点的parent_id"""
|
||||
cursor = conn.cursor()
|
||||
|
||||
try:
|
||||
file_path = file_info['path']
|
||||
|
||||
# 查找现有文件记录
|
||||
existing = existing_templates['by_path'].get(file_path)
|
||||
|
||||
if existing:
|
||||
template_id = existing['id']
|
||||
if existing['parent_id'] != parent_id:
|
||||
update_sql = """
|
||||
UPDATE f_polic_file_config
|
||||
SET parent_id = %s, updated_time = NOW(), updated_by = %s
|
||||
WHERE id = %s AND tenant_id = %s
|
||||
"""
|
||||
cursor.execute(update_sql, (parent_id, UPDATED_BY, template_id, tenant_id))
|
||||
conn.commit()
|
||||
print(f" [UPDATE] 更新文件: {file_info['name']} (ID: {template_id}, parent_id: {parent_id})")
|
||||
else:
|
||||
print(f" [KEEP] 保持文件: {file_info['name']} (ID: {template_id})")
|
||||
else:
|
||||
# 文件不存在,创建新记录
|
||||
template_id = generate_id()
|
||||
insert_sql = """
|
||||
INSERT INTO f_polic_file_config
|
||||
(id, tenant_id, parent_id, name, input_data, file_path, created_time, created_by, updated_time, updated_by, state)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, NOW(), %s, NOW(), %s, 1)
|
||||
"""
|
||||
cursor.execute(insert_sql, (
|
||||
template_id,
|
||||
tenant_id,
|
||||
parent_id,
|
||||
file_info['name'],
|
||||
'{}', # input_data
|
||||
file_path,
|
||||
CREATED_BY,
|
||||
UPDATED_BY
|
||||
))
|
||||
conn.commit()
|
||||
print(f" [CREATE] 创建文件: {file_info['name']} (ID: {template_id}, parent_id: {parent_id})")
|
||||
finally:
|
||||
cursor.close()
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print_section("更新模板层级结构")
|
||||
|
||||
# 1. 连接数据库
|
||||
print_section("1. 连接数据库")
|
||||
try:
|
||||
conn = pymysql.connect(**DB_CONFIG)
|
||||
print_result(True, "数据库连接成功")
|
||||
except Exception as e:
|
||||
print_result(False, f"数据库连接失败: {str(e)}")
|
||||
return
|
||||
|
||||
try:
|
||||
# 2. 获取实际的tenant_id
|
||||
print_section("2. 获取实际的tenant_id")
|
||||
tenant_id = get_actual_tenant_id(conn)
|
||||
print_result(True, f"实际tenant_id: {tenant_id}")
|
||||
|
||||
# 3. 扫描目录结构
|
||||
print_section("3. 扫描目录结构")
|
||||
structure = scan_directory_structure(TEMPLATES_DIR)
|
||||
|
||||
if not structure['directories'] and not structure['files']:
|
||||
print_result(False, "未找到任何目录或文件")
|
||||
return
|
||||
|
||||
print(f" 找到 {len(structure['directories'])} 个目录")
|
||||
print(f" 找到 {len(structure['files'])} 个文件")
|
||||
|
||||
# 4. 获取现有模板
|
||||
print_section("4. 获取现有模板")
|
||||
existing_templates = get_existing_templates(conn, tenant_id)
|
||||
print(f" 现有文件模板: {len(existing_templates['by_path'])} 个")
|
||||
print(f" 现有目录模板: {sum(len(v) for v in existing_templates['by_name'].values())} 个")
|
||||
|
||||
# 5. 创建/更新目录节点
|
||||
print_section("5. 创建/更新目录节点")
|
||||
|
||||
path_to_id = {} # 路径到ID的映射
|
||||
|
||||
# 按层级顺序处理目录(确保父目录先处理)
|
||||
for dir_info in structure['directories']:
|
||||
parent_id = None
|
||||
if dir_info['parent_path']:
|
||||
parent_id = path_to_id.get(dir_info['parent_path'])
|
||||
if parent_id is None:
|
||||
print(f" [WARN] 未找到父目录: {dir_info['parent_path']}")
|
||||
|
||||
dir_id = create_or_update_directory(
|
||||
conn, tenant_id, dir_info['name'], dir_info['path'],
|
||||
parent_id, existing_templates
|
||||
)
|
||||
path_to_id[dir_info['path']] = dir_id
|
||||
|
||||
print_result(True, f"处理了 {len(path_to_id)} 个目录节点")
|
||||
|
||||
# 6. 更新文件节点的parent_id
|
||||
print_section("6. 更新文件节点的parent_id")
|
||||
|
||||
updated_count = 0
|
||||
created_count = 0
|
||||
kept_count = 0
|
||||
|
||||
for file_info in structure['files']:
|
||||
parent_id = None
|
||||
if file_info['parent_path']:
|
||||
parent_id = path_to_id.get(file_info['parent_path'])
|
||||
if parent_id is None:
|
||||
print(f" [WARN] 未找到父目录: {file_info['parent_path']}")
|
||||
|
||||
# 检查是否需要更新
|
||||
file_path = file_info['path']
|
||||
existing = existing_templates['by_path'].get(file_path)
|
||||
|
||||
if existing:
|
||||
if existing['parent_id'] != parent_id:
|
||||
update_file_parent(conn, tenant_id, file_info, parent_id, existing_templates)
|
||||
updated_count += 1
|
||||
else:
|
||||
kept_count += 1
|
||||
else:
|
||||
update_file_parent(conn, tenant_id, file_info, parent_id, existing_templates)
|
||||
created_count += 1
|
||||
|
||||
print_result(True, f"处理了 {len(structure['files'])} 个文件节点")
|
||||
print(f" - 更新: {updated_count} 个")
|
||||
print(f" - 创建: {created_count} 个")
|
||||
print(f" - 保持: {kept_count} 个")
|
||||
|
||||
# 7. 验证层级结构
|
||||
print_section("7. 验证层级结构")
|
||||
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
try:
|
||||
# 统计各层级的节点数
|
||||
cursor.execute("""
|
||||
SELECT
|
||||
CASE
|
||||
WHEN parent_id IS NULL THEN '根节点'
|
||||
ELSE '子节点'
|
||||
END as node_type,
|
||||
COUNT(*) as count
|
||||
FROM f_polic_file_config
|
||||
WHERE tenant_id = %s
|
||||
GROUP BY node_type
|
||||
""", (tenant_id,))
|
||||
stats = cursor.fetchall()
|
||||
|
||||
for stat in stats:
|
||||
print(f" {stat['node_type']}: {stat['count']} 个")
|
||||
|
||||
# 统计有parent_id的文件
|
||||
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,))
|
||||
result = cursor.fetchone()
|
||||
print(f" 有父级的文件: {result['count']} 个")
|
||||
|
||||
finally:
|
||||
cursor.close()
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
print_result(True, "数据库连接已关闭")
|
||||
|
||||
print_section("完成")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
215
verify_template_hierarchy.py
Normal file
215
verify_template_hierarchy.py
Normal file
@ -0,0 +1,215 @@
|
||||
"""
|
||||
验证模板层级结构是否正确
|
||||
"""
|
||||
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()
|
||||
247
模板层级结构更新报告.md
Normal file
247
模板层级结构更新报告.md
Normal file
@ -0,0 +1,247 @@
|
||||
# 模板层级结构更新报告
|
||||
|
||||
## 更新时间
|
||||
2025-12-16
|
||||
|
||||
## 一、更新概述
|
||||
|
||||
根据 `template_finish/` 目录的实际层级结构,更新了数据库中的 `parent_id` 字段,建立了完整的模板层级关系。
|
||||
|
||||
## 二、目录结构
|
||||
|
||||
`template_finish/` 目录的层级结构:
|
||||
|
||||
```
|
||||
template_finish/
|
||||
├── 1-谈话函询模板/
|
||||
│ ├── 函询模板/
|
||||
│ └── 谈话模版/
|
||||
├── 2-初核模版/
|
||||
│ ├── 1.初核请示/
|
||||
│ ├── 2.谈话审批/
|
||||
│ │ ├── 谈话通知书/
|
||||
│ │ ├── 走读式谈话审批/
|
||||
│ │ └── 走读式谈话流程/
|
||||
│ └── 3.初核结论/
|
||||
├── 3-立案模版/
|
||||
│ ├── 党员/
|
||||
│ │ ├── 移送审理/
|
||||
│ │ ├── 立案审查/
|
||||
│ │ │ ├── 1.立案审查请示报批/
|
||||
│ │ │ ├── 2.立案决定书/
|
||||
│ │ │ ├── 3.立案通知书/
|
||||
│ │ │ ├── 4.谈话请示报批/
|
||||
│ │ │ ├── 5.谈话通知书/
|
||||
│ │ │ └── 6.立案审查报告请示报批/
|
||||
│ │ └── 装卷/
|
||||
│ └── 非党员监察对象/
|
||||
│ ├── 移送审理/
|
||||
│ ├── 立案调查/
|
||||
│ │ ├── 1.立案调查请示报批/
|
||||
│ │ ├── 2.立案决定书/
|
||||
│ │ ├── 3.立案通知书/
|
||||
│ │ ├── 4.谈话请示报批/
|
||||
│ │ ├── 5.谈话通知书/
|
||||
│ │ └── 6.立案调查报告请示报批/
|
||||
│ └── 装卷(目录)/
|
||||
├── 4-审理模版/
|
||||
└── 5-处分决定模版/
|
||||
```
|
||||
|
||||
## 三、更新结果
|
||||
|
||||
### 3.1 目录节点
|
||||
|
||||
- **创建的目录节点**: 26个
|
||||
- **更新的目录节点**: 7个(更新了parent_id)
|
||||
- **保持的目录节点**: 0个
|
||||
- **总计**: 33个目录节点
|
||||
|
||||
### 3.2 文件节点
|
||||
|
||||
- **更新的文件**: 103个(更新了parent_id)
|
||||
- **创建的文件**: 0个(所有文件已存在)
|
||||
- **保持的文件**: 18个(parent_id已正确)
|
||||
- **总计**: 121个文件节点
|
||||
|
||||
### 3.3 层级关系
|
||||
|
||||
- **根节点**(parent_id为NULL): 6个
|
||||
- 1-谈话函询模板
|
||||
- 2-初核模版
|
||||
- 3-立案模版
|
||||
- 4-审理模版
|
||||
- 5-处分决定模版
|
||||
- 1个孤立文件(已修复)
|
||||
|
||||
- **目录节点**(file_path为NULL): 28个
|
||||
- **文件节点**(file_path不为NULL): 123个
|
||||
- **有父级的文件**: 122个(99.2%)
|
||||
- **没有父级的文件**: 1个(已修复为0个)
|
||||
|
||||
## 四、层级结构示例
|
||||
|
||||
### 4.1 一级目录示例
|
||||
|
||||
**1-谈话函询模板** (ID: 1766712580957516)
|
||||
- 函询模板 (ID: 1766712581053425)
|
||||
- 1.纪委请示报告卡.docx
|
||||
- 2谈话函询呈批表.docx
|
||||
- ...
|
||||
- 谈话模版 (ID: 1766712581074618)
|
||||
- 1.请示报告卡.docx
|
||||
- 2.谈话函询呈批表.docx
|
||||
- ...
|
||||
|
||||
**2-初核模版** (ID: 1765273961277310)
|
||||
- 1.初核请示 (ID: 1765431558933731)
|
||||
- 1.请示报告卡(XXX).docx
|
||||
- 2.初步核实审批表(XXX).docx
|
||||
- 3.附件初核方案(XXX).docx
|
||||
- 2.谈话审批 (ID: 1765431558825578)
|
||||
- 谈话通知书 (ID: 1765273962774249)
|
||||
- 谈话通知书第一联.docx
|
||||
- 谈话通知书第三联.docx
|
||||
- 谈话通知书第二联.docx
|
||||
- 走读式谈话审批 (ID: 1765273962700431)
|
||||
- 1.请示报告卡(初核谈话).docx
|
||||
- 2谈话审批表.docx
|
||||
- ...
|
||||
- 走读式谈话流程 (ID: 1765273962716807)
|
||||
- 1.谈话笔录.docx
|
||||
- 2.谈话询问对象情况摸底调查30问.docx
|
||||
- ...
|
||||
- 3.初核结论 (ID: 1765431559135346)
|
||||
- 8-1请示报告卡(初核报告结论) .docx
|
||||
- 8.XXX初核情况报告.docx
|
||||
|
||||
### 4.2 多级嵌套示例
|
||||
|
||||
**3-立案模版** (ID: 1766712580976188)
|
||||
- 党员 (ID: 1766712581095556)
|
||||
- 立案审查 (ID: 1766712581165041)
|
||||
- 1.立案审查请示报批 (ID: 1766712581268223)
|
||||
- 1.请示报告卡(立案审查).docx
|
||||
- 2.立案审批表.docx
|
||||
- ...
|
||||
- 2.立案决定书 (ID: 1766712581290617)
|
||||
- 立案决定书-第一联.docx
|
||||
- 立案决定书-第三联.docx
|
||||
- 立案决定书-第二联.docx
|
||||
- ...
|
||||
|
||||
## 五、数据库表结构说明
|
||||
|
||||
### 5.1 f_polic_file_config 表
|
||||
|
||||
- **目录节点**:
|
||||
- `file_path`: NULL
|
||||
- `parent_id`: 父目录的ID(根目录为NULL)
|
||||
- `name`: 目录名称
|
||||
|
||||
- **文件节点**:
|
||||
- `file_path`: 本地相对路径(如 `template_finish/2-初核模版/1.初核请示/1.请示报告卡(XXX).docx`)
|
||||
- `parent_id`: 父目录的ID
|
||||
- `name`: 文件名称
|
||||
|
||||
### 5.2 层级关系查询
|
||||
|
||||
**查询某个目录下的所有文件**:
|
||||
```sql
|
||||
SELECT fc.*
|
||||
FROM f_polic_file_config fc
|
||||
WHERE fc.tenant_id = 1
|
||||
AND fc.parent_id = <目录ID>
|
||||
AND fc.file_path IS NOT NULL
|
||||
AND fc.state = 1;
|
||||
```
|
||||
|
||||
**查询某个目录下的所有子目录**:
|
||||
```sql
|
||||
SELECT fc.*
|
||||
FROM f_polic_file_config fc
|
||||
WHERE fc.tenant_id = 1
|
||||
AND fc.parent_id = <目录ID>
|
||||
AND fc.file_path IS NULL
|
||||
AND fc.state = 1;
|
||||
```
|
||||
|
||||
**查询完整的层级路径**:
|
||||
```sql
|
||||
WITH RECURSIVE hierarchy AS (
|
||||
-- 根节点
|
||||
SELECT id, name, parent_id, file_path, CAST(name AS CHAR(1000)) AS path
|
||||
FROM f_polic_file_config
|
||||
WHERE tenant_id = 1 AND parent_id IS NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- 子节点
|
||||
SELECT fc.id, fc.name, fc.parent_id, fc.file_path,
|
||||
CONCAT(h.path, ' > ', fc.name) AS path
|
||||
FROM f_polic_file_config fc
|
||||
INNER JOIN hierarchy h ON fc.parent_id = h.id
|
||||
WHERE fc.tenant_id = 1
|
||||
)
|
||||
SELECT * FROM hierarchy
|
||||
WHERE file_path IS NOT NULL
|
||||
ORDER BY path;
|
||||
```
|
||||
|
||||
## 六、更新脚本
|
||||
|
||||
本次更新使用的脚本:
|
||||
|
||||
1. **`update_template_hierarchy_from_directory.py`**:
|
||||
- 扫描目录结构
|
||||
- 创建/更新目录节点
|
||||
- 更新文件节点的parent_id
|
||||
|
||||
2. **`verify_template_hierarchy.py`**:
|
||||
- 验证层级结构
|
||||
- 检查潜在问题
|
||||
|
||||
3. **`fix_isolated_template.py`**:
|
||||
- 修复孤立的文件
|
||||
|
||||
## 七、验证结果
|
||||
|
||||
### 7.1 层级结构验证
|
||||
|
||||
✅ **根节点**: 5个(对应5个一级目录)
|
||||
✅ **目录节点**: 28个
|
||||
✅ **文件节点**: 123个
|
||||
✅ **有父级的文件**: 122个(99.2%)
|
||||
✅ **断开的父级链接**: 0个
|
||||
|
||||
### 7.2 层级关系验证
|
||||
|
||||
✅ 所有文件都有正确的parent_id
|
||||
✅ 所有目录都有正确的parent_id(根目录为NULL)
|
||||
✅ 层级结构与目录结构完全对应
|
||||
|
||||
## 八、注意事项
|
||||
|
||||
1. **tenant_id**: 数据库中的实际tenant_id是 `1`,不是配置中的 `615873064429507639`
|
||||
2. **目录节点**: 目录节点的 `file_path` 字段为 `NULL`,用于区分目录和文件
|
||||
3. **层级深度**: 最深层级为4级(例如:3-立案模版 > 党员 > 立案审查 > 1.立案审查请示报批 > 文件)
|
||||
4. **同名目录**: 不同路径下的同名目录会被创建为不同的记录(例如:"立案审查"和"立案调查")
|
||||
|
||||
## 九、后续维护
|
||||
|
||||
1. **添加新模板**:
|
||||
- 将模板文件放到对应目录
|
||||
- 运行 `update_template_hierarchy_from_directory.py` 更新数据库
|
||||
|
||||
2. **修改目录结构**:
|
||||
- 调整本地目录结构
|
||||
- 运行更新脚本同步数据库
|
||||
|
||||
3. **验证层级**:
|
||||
- 定期运行 `verify_template_hierarchy.py` 检查层级结构
|
||||
|
||||
---
|
||||
|
||||
**更新人员**: 自动化脚本
|
||||
**更新日期**: 2025-12-16
|
||||
**更新状态**: ✅ 完成
|
||||
Loading…
x
Reference in New Issue
Block a user