修复文档生成逻辑中的冲突,更新文件路径和下载链接的返回逻辑,增强用户体验。同时,优化前端文件列表加载功能,支持加载所有可用模板,并添加清空文件列表的功能。
This commit is contained in:
parent
28bf100ca4
commit
d8d2817aed
6
app.py
6
app.py
@ -706,7 +706,6 @@ def generate_document():
|
|||||||
return error_response(3001, f"文档生成失败: {str(e)}")
|
return error_response(3001, f"文档生成失败: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
@app.route('/fPolicTask/getDocument', methods=['POST'])
|
@app.route('/fPolicTask/getDocument', methods=['POST'])
|
||||||
def get_document_by_task():
|
def get_document_by_task():
|
||||||
"""
|
"""
|
||||||
@ -810,7 +809,8 @@ def get_document_by_task():
|
|||||||
result_file_list.append({
|
result_file_list.append({
|
||||||
'fileId': file_id,
|
'fileId': file_id,
|
||||||
'fileName': generated_file_name, # 使用生成的文档名
|
'fileName': generated_file_name, # 使用生成的文档名
|
||||||
'filePath': result['filePath']
|
'filePath': result['filePath'], # MinIO相对路径
|
||||||
|
'downloadUrl': result.get('downloadUrl') # MinIO预签名下载URL(完整链接)
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -835,8 +835,6 @@ def get_document_by_task():
|
|||||||
return error_response(3001, f"文档生成失败: {str(e)}")
|
return error_response(3001, f"文档生成失败: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> parent of 4897c96 (添加通过taskId获取文档的接口,支持文件列表查询和参数验证,增强错误处理能力。同时,优化文档生成逻辑,确保生成的文档名称和路径的准确性。)
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# 确保static目录存在
|
# 确保static目录存在
|
||||||
os.makedirs('static', exist_ok=True)
|
os.makedirs('static', exist_ok=True)
|
||||||
|
|||||||
117
check_and_fix_duplicates.py
Normal file
117
check_and_fix_duplicates.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
"""
|
||||||
|
检查并修复重复记录
|
||||||
|
"""
|
||||||
|
import pymysql
|
||||||
|
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
UPDATED_BY = 655162080928945152
|
||||||
|
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 检查"1.初核请示"下的所有记录
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT id, name, file_path, parent_id
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND parent_id = %s
|
||||||
|
ORDER BY id
|
||||||
|
""", (TENANT_ID, 1765431558933731)) # 1.初核请示
|
||||||
|
|
||||||
|
results = cursor.fetchall()
|
||||||
|
print(f"'1.初核请示'下有 {len(results)} 条记录:\n")
|
||||||
|
for r in results:
|
||||||
|
print(f"ID: {r['id']}, name: {r['name']}, file_path: {r['file_path']}")
|
||||||
|
|
||||||
|
# 检查"1请示报告卡"的记录
|
||||||
|
request_cards = [r for r in results if r['name'] == '1请示报告卡']
|
||||||
|
if len(request_cards) > 1:
|
||||||
|
print(f"\n发现 {len(request_cards)} 个重复的'1请示报告卡'记录")
|
||||||
|
# 保留file_path正确的那个
|
||||||
|
correct_one = None
|
||||||
|
for r in request_cards:
|
||||||
|
if r['file_path'] and '1.请示报告卡(XXX)' in r['file_path']:
|
||||||
|
correct_one = r
|
||||||
|
break
|
||||||
|
|
||||||
|
if correct_one:
|
||||||
|
# 删除其他的
|
||||||
|
for r in request_cards:
|
||||||
|
if r['id'] != correct_one['id']:
|
||||||
|
# 删除关联关系
|
||||||
|
cursor.execute("""
|
||||||
|
DELETE FROM f_polic_file_field
|
||||||
|
WHERE tenant_id = %s AND file_id = %s
|
||||||
|
""", (TENANT_ID, r['id']))
|
||||||
|
# 删除模板记录
|
||||||
|
cursor.execute("""
|
||||||
|
DELETE FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND id = %s
|
||||||
|
""", (TENANT_ID, r['id']))
|
||||||
|
print(f"[DELETE] 删除重复记录: ID {r['id']}, file_path: {r['file_path']}")
|
||||||
|
|
||||||
|
# 检查"走读式谈话审批"下是否有"1请示报告卡"
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT id, name, file_path, parent_id
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND parent_id = %s AND name = %s
|
||||||
|
""", (TENANT_ID, 1765273962700431, '1请示报告卡')) # 走读式谈话审批
|
||||||
|
|
||||||
|
result = cursor.fetchone()
|
||||||
|
if not result:
|
||||||
|
print("\n[WARN] '走读式谈话审批'下缺少'1请示报告卡'记录")
|
||||||
|
# 创建记录
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
timestamp = int(time.time() * 1000)
|
||||||
|
random_part = random.randint(100000, 999999)
|
||||||
|
new_id = timestamp * 1000 + random_part
|
||||||
|
|
||||||
|
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, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(insert_sql, (
|
||||||
|
new_id,
|
||||||
|
TENANT_ID,
|
||||||
|
1765273962700431, # 走读式谈话审批
|
||||||
|
'1请示报告卡',
|
||||||
|
None,
|
||||||
|
'/615873064429507639/TEMPLATE/2025/12/1.请示报告卡(初核谈话).docx',
|
||||||
|
655162080928945152,
|
||||||
|
655162080928945152,
|
||||||
|
1
|
||||||
|
))
|
||||||
|
print(f"[CREATE] 在'走读式谈话审批'下创建'1请示报告卡'记录 (ID: {new_id})")
|
||||||
|
else:
|
||||||
|
# 检查file_path是否正确
|
||||||
|
if result['file_path'] and '1.请示报告卡(初核谈话)' not in result['file_path']:
|
||||||
|
cursor.execute("""
|
||||||
|
UPDATE f_polic_file_config
|
||||||
|
SET file_path = %s, updated_time = NOW(), updated_by = %s
|
||||||
|
WHERE tenant_id = %s AND id = %s
|
||||||
|
""", ('/615873064429507639/TEMPLATE/2025/12/1.请示报告卡(初核谈话).docx', UPDATED_BY, TENANT_ID, result['id']))
|
||||||
|
print(f"[UPDATE] 修复'走读式谈话审批'下'1请示报告卡'的file_path")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
print("\n[OK] 修复完成")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
print(f"[ERROR] 修复失败: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
36
check_confidentiality_commitment.py
Normal file
36
check_confidentiality_commitment.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
"""查询保密承诺书相关的模板记录"""
|
||||||
|
import pymysql
|
||||||
|
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT id, name, file_path, parent_id
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND name LIKE %s
|
||||||
|
ORDER BY name
|
||||||
|
""", (TENANT_ID, '%保密承诺书%'))
|
||||||
|
|
||||||
|
results = cursor.fetchall()
|
||||||
|
print(f"找到 {len(results)} 条记录:\n")
|
||||||
|
for r in results:
|
||||||
|
print(f"ID: {r['id']}")
|
||||||
|
print(f"名称: {r['name']}")
|
||||||
|
print(f"文件路径: {r['file_path']}")
|
||||||
|
print(f"父节点ID: {r['parent_id']}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
361
cleanup_duplicate_templates.py
Normal file
361
cleanup_duplicate_templates.py
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
"""
|
||||||
|
清理 f_polic_file_config 表中的重复和无效数据
|
||||||
|
确保文档模板结构和 template_finish/ 文件夹对应
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import pymysql
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Set, Optional
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
# 数据库连接配置
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
UPDATED_BY = 655162080928945152
|
||||||
|
TEMPLATE_BASE_DIR = 'template_finish'
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_template_name(name: str) -> str:
|
||||||
|
"""
|
||||||
|
标准化模板名称(去掉扩展名、括号内容、数字前缀等)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: 文件名或模板名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
标准化后的名称
|
||||||
|
"""
|
||||||
|
# 去掉扩展名
|
||||||
|
name = Path(name).stem if '.' in name else name
|
||||||
|
|
||||||
|
# 去掉括号内容
|
||||||
|
name = re.sub(r'[((].*?[))]', '', name)
|
||||||
|
name = name.strip()
|
||||||
|
|
||||||
|
# 去掉数字前缀和点号
|
||||||
|
name = re.sub(r'^\d+[\.\-]?\s*', '', name)
|
||||||
|
name = name.strip()
|
||||||
|
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def scan_template_files(base_dir: str) -> Dict[str, Dict]:
|
||||||
|
"""
|
||||||
|
扫描模板文件夹,获取所有有效的模板文件
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,key为标准化名称,value为模板信息列表(可能有多个同名文件)
|
||||||
|
"""
|
||||||
|
base_path = Path(base_dir)
|
||||||
|
if not base_path.exists():
|
||||||
|
print(f"错误: 目录不存在 - {base_dir}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
templates = defaultdict(list)
|
||||||
|
|
||||||
|
print("=" * 80)
|
||||||
|
print("扫描模板文件...")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
for docx_file in sorted(base_path.rglob("*.docx")):
|
||||||
|
# 跳过临时文件
|
||||||
|
if docx_file.name.startswith("~$"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
relative_path = docx_file.relative_to(base_path)
|
||||||
|
file_name = docx_file.name
|
||||||
|
normalized_name = normalize_template_name(file_name)
|
||||||
|
|
||||||
|
templates[normalized_name].append({
|
||||||
|
'file_path': str(docx_file),
|
||||||
|
'relative_path': str(relative_path),
|
||||||
|
'file_name': file_name,
|
||||||
|
'normalized_name': normalized_name
|
||||||
|
})
|
||||||
|
|
||||||
|
print(f"总共扫描到 {sum(len(v) for v in templates.values())} 个模板文件")
|
||||||
|
print(f"唯一模板名称: {len(templates)} 个")
|
||||||
|
|
||||||
|
return dict(templates)
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_templates_from_db(conn) -> Dict[str, List[Dict]]:
|
||||||
|
"""
|
||||||
|
从数据库获取所有模板,按标准化名称分组
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,key为标准化名称,value为模板记录列表
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
SELECT id, name, file_path, parent_id, state, input_data, created_time, updated_time
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s
|
||||||
|
ORDER BY created_time DESC
|
||||||
|
"""
|
||||||
|
cursor.execute(sql, (TENANT_ID,))
|
||||||
|
templates = cursor.fetchall()
|
||||||
|
|
||||||
|
result = defaultdict(list)
|
||||||
|
for template in templates:
|
||||||
|
normalized_name = normalize_template_name(template['name'])
|
||||||
|
result[normalized_name].append({
|
||||||
|
'id': template['id'],
|
||||||
|
'name': template['name'],
|
||||||
|
'normalized_name': normalized_name,
|
||||||
|
'file_path': template['file_path'],
|
||||||
|
'parent_id': template['parent_id'],
|
||||||
|
'state': template['state'],
|
||||||
|
'input_data': template['input_data'],
|
||||||
|
'created_time': template['created_time'],
|
||||||
|
'updated_time': template['updated_time']
|
||||||
|
})
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
return dict(result)
|
||||||
|
|
||||||
|
|
||||||
|
def find_duplicates(db_templates: Dict[str, List[Dict]]) -> Dict[str, List[Dict]]:
|
||||||
|
"""
|
||||||
|
找出重复的模板(同一标准化名称有多个记录)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,key为标准化名称,value为重复的模板记录列表
|
||||||
|
"""
|
||||||
|
duplicates = {}
|
||||||
|
for normalized_name, templates in db_templates.items():
|
||||||
|
if len(templates) > 1:
|
||||||
|
duplicates[normalized_name] = templates
|
||||||
|
return duplicates
|
||||||
|
|
||||||
|
|
||||||
|
def select_best_template(templates: List[Dict], valid_template_files: List[Dict]) -> Optional[Dict]:
|
||||||
|
"""
|
||||||
|
从多个重复的模板中选择最好的一个(保留最新的、有效的)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
templates: 数据库中的模板记录列表
|
||||||
|
valid_template_files: 有效的模板文件列表
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
应该保留的模板记录,或None
|
||||||
|
"""
|
||||||
|
if not templates:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 优先选择:state=1 且 file_path 有效的
|
||||||
|
enabled_templates = [t for t in templates if t.get('state') == 1]
|
||||||
|
|
||||||
|
if enabled_templates:
|
||||||
|
# 如果有多个启用的,选择最新的
|
||||||
|
enabled_templates.sort(key=lambda x: x.get('updated_time') or x.get('created_time'), reverse=True)
|
||||||
|
return enabled_templates[0]
|
||||||
|
|
||||||
|
# 如果没有启用的,选择最新的
|
||||||
|
templates.sort(key=lambda x: x.get('updated_time') or x.get('created_time'), reverse=True)
|
||||||
|
return templates[0]
|
||||||
|
|
||||||
|
|
||||||
|
def delete_template_and_relations(conn, template_id: int):
|
||||||
|
"""
|
||||||
|
删除模板及其关联关系
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn: 数据库连接
|
||||||
|
template_id: 模板ID
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 删除字段关联
|
||||||
|
delete_relations_sql = """
|
||||||
|
DELETE FROM f_polic_file_field
|
||||||
|
WHERE tenant_id = %s AND file_id = %s
|
||||||
|
"""
|
||||||
|
cursor.execute(delete_relations_sql, (TENANT_ID, template_id))
|
||||||
|
relations_deleted = cursor.rowcount
|
||||||
|
|
||||||
|
# 删除模板配置
|
||||||
|
delete_template_sql = """
|
||||||
|
DELETE FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND id = %s
|
||||||
|
"""
|
||||||
|
cursor.execute(delete_template_sql, (TENANT_ID, template_id))
|
||||||
|
template_deleted = cursor.rowcount
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
return relations_deleted, template_deleted
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise Exception(f"删除模板失败: {str(e)}")
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def mark_invalid_templates(conn, valid_template_names: Set[str]):
|
||||||
|
"""
|
||||||
|
标记无效的模板(不在template_finish文件夹中的模板)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn: 数据库连接
|
||||||
|
valid_template_names: 有效的模板名称集合(标准化后的)
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 获取所有模板
|
||||||
|
sql = """
|
||||||
|
SELECT id, name FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s
|
||||||
|
"""
|
||||||
|
cursor.execute(sql, (TENANT_ID,))
|
||||||
|
all_templates = cursor.fetchall()
|
||||||
|
|
||||||
|
invalid_count = 0
|
||||||
|
for template in all_templates:
|
||||||
|
template_id = template[0]
|
||||||
|
template_name = template[1]
|
||||||
|
normalized_name = normalize_template_name(template_name)
|
||||||
|
|
||||||
|
# 检查是否在有效模板列表中
|
||||||
|
if normalized_name not in valid_template_names:
|
||||||
|
# 标记为未启用
|
||||||
|
update_sql = """
|
||||||
|
UPDATE f_polic_file_config
|
||||||
|
SET state = 0, updated_time = NOW(), updated_by = %s
|
||||||
|
WHERE id = %s AND tenant_id = %s
|
||||||
|
"""
|
||||||
|
cursor.execute(update_sql, (UPDATED_BY, template_id, TENANT_ID))
|
||||||
|
invalid_count += 1
|
||||||
|
print(f" [WARN] 标记无效模板: {template_name} (ID: {template_id})")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
print(f"\n总共标记 {invalid_count} 个无效模板")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise Exception(f"标记无效模板失败: {str(e)}")
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
print("=" * 80)
|
||||||
|
print("清理重复和无效的模板数据")
|
||||||
|
print("=" * 80)
|
||||||
|
print()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 连接数据库
|
||||||
|
print("1. 连接数据库...")
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
print("[OK] 数据库连接成功\n")
|
||||||
|
|
||||||
|
# 扫描模板文件
|
||||||
|
print("2. 扫描模板文件...")
|
||||||
|
valid_templates = scan_template_files(TEMPLATE_BASE_DIR)
|
||||||
|
valid_template_names = set(valid_templates.keys())
|
||||||
|
print(f"[OK] 找到 {len(valid_template_names)} 个有效模板名称\n")
|
||||||
|
|
||||||
|
# 获取数据库中的模板
|
||||||
|
print("3. 获取数据库中的模板...")
|
||||||
|
db_templates = get_all_templates_from_db(conn)
|
||||||
|
print(f"[OK] 数据库中有 {sum(len(v) for v in db_templates.values())} 个模板记录")
|
||||||
|
print(f"[OK] 唯一模板名称: {len(db_templates)} 个\n")
|
||||||
|
|
||||||
|
# 找出重复的模板
|
||||||
|
print("4. 查找重复的模板...")
|
||||||
|
duplicates = find_duplicates(db_templates)
|
||||||
|
print(f"[OK] 找到 {len(duplicates)} 个重复的模板名称\n")
|
||||||
|
|
||||||
|
# 处理重复模板
|
||||||
|
print("5. 处理重复模板...")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
total_deleted = 0
|
||||||
|
total_relations_deleted = 0
|
||||||
|
|
||||||
|
for normalized_name, templates in duplicates.items():
|
||||||
|
print(f"\n处理重复模板: {normalized_name}")
|
||||||
|
print(f" 重复记录数: {len(templates)}")
|
||||||
|
|
||||||
|
# 获取对应的有效模板文件
|
||||||
|
valid_files = valid_templates.get(normalized_name, [])
|
||||||
|
|
||||||
|
# 选择要保留的模板
|
||||||
|
keep_template = select_best_template(templates, valid_files)
|
||||||
|
|
||||||
|
if keep_template:
|
||||||
|
print(f" [KEEP] 保留模板: {keep_template['name']} (ID: {keep_template['id']})")
|
||||||
|
|
||||||
|
# 删除其他重复的模板
|
||||||
|
for template in templates:
|
||||||
|
if template['id'] != keep_template['id']:
|
||||||
|
print(f" [DELETE] 删除重复模板: {template['name']} (ID: {template['id']})")
|
||||||
|
relations_deleted, template_deleted = delete_template_and_relations(conn, template['id'])
|
||||||
|
total_relations_deleted += relations_deleted
|
||||||
|
total_deleted += template_deleted
|
||||||
|
else:
|
||||||
|
print(f" [WARN] 无法确定要保留的模板,跳过")
|
||||||
|
|
||||||
|
print(f"\n[OK] 删除重复模板: {total_deleted} 个")
|
||||||
|
print(f"[OK] 删除关联关系: {total_relations_deleted} 条\n")
|
||||||
|
|
||||||
|
# 标记无效模板
|
||||||
|
print("6. 标记无效模板...")
|
||||||
|
mark_invalid_templates(conn, valid_template_names)
|
||||||
|
|
||||||
|
# 统计最终结果
|
||||||
|
print("\n7. 统计最终结果...")
|
||||||
|
final_templates = get_all_templates_from_db(conn)
|
||||||
|
enabled_count = sum(1 for templates in final_templates.values()
|
||||||
|
for t in templates if t.get('state') == 1)
|
||||||
|
disabled_count = sum(1 for templates in final_templates.values()
|
||||||
|
for t in templates if t.get('state') != 1)
|
||||||
|
|
||||||
|
print(f"[OK] 最终模板总数: {sum(len(v) for v in final_templates.values())}")
|
||||||
|
print(f"[OK] 启用模板数: {enabled_count}")
|
||||||
|
print(f"[OK] 禁用模板数: {disabled_count}")
|
||||||
|
print(f"[OK] 唯一模板名称: {len(final_templates)}")
|
||||||
|
|
||||||
|
# 打印最终模板列表
|
||||||
|
print("\n8. 最终模板列表(启用的):")
|
||||||
|
print("=" * 80)
|
||||||
|
for normalized_name, templates in sorted(final_templates.items()):
|
||||||
|
enabled = [t for t in templates if t.get('state') == 1]
|
||||||
|
if enabled:
|
||||||
|
for template in enabled:
|
||||||
|
print(f" - {template['name']} (ID: {template['id']})")
|
||||||
|
|
||||||
|
print("\n" + "=" * 80)
|
||||||
|
print("清理完成!")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n[ERROR] 发生错误: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.rollback()
|
||||||
|
finally:
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.close()
|
||||||
|
print("\n数据库连接已关闭")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
158
finalize_template_hierarchy.py
Normal file
158
finalize_template_hierarchy.py
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
"""
|
||||||
|
最终完善模板层级结构
|
||||||
|
修复文件路径错误和重复问题
|
||||||
|
"""
|
||||||
|
import pymysql
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
CREATED_BY = 655162080928945152
|
||||||
|
UPDATED_BY = 655162080928945152
|
||||||
|
|
||||||
|
def generate_id():
|
||||||
|
timestamp = int(time.time() * 1000)
|
||||||
|
random_part = random.randint(100000, 999999)
|
||||||
|
return timestamp * 1000 + random_part
|
||||||
|
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 检查"1请示报告卡"的记录
|
||||||
|
# 根据目录结构,应该有两个不同的文件:
|
||||||
|
# 1. "1.初核请示"下的"1.请示报告卡(XXX).docx"
|
||||||
|
# 2. "走读式谈话审批"下的"1.请示报告卡(初核谈话).docx"
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT id, name, file_path, parent_id
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND name = %s
|
||||||
|
ORDER BY id
|
||||||
|
""", (TENANT_ID, '1请示报告卡'))
|
||||||
|
|
||||||
|
results = cursor.fetchall()
|
||||||
|
|
||||||
|
# 检查是否在"1.初核请示"下有记录
|
||||||
|
in_initial_request = any(r['parent_id'] == 1765431558933731 for r in results)
|
||||||
|
# 检查是否在"走读式谈话审批"下有记录
|
||||||
|
in_interview_approval = any(r['parent_id'] == 1765273962700431 for r in results)
|
||||||
|
|
||||||
|
if not in_initial_request:
|
||||||
|
# 需要在"1.初核请示"下创建记录
|
||||||
|
new_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, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(insert_sql, (
|
||||||
|
new_id,
|
||||||
|
TENANT_ID,
|
||||||
|
1765431558933731, # 1.初核请示
|
||||||
|
'1请示报告卡',
|
||||||
|
None,
|
||||||
|
'/615873064429507639/TEMPLATE/2025/12/1.请示报告卡(XXX).docx',
|
||||||
|
CREATED_BY,
|
||||||
|
CREATED_BY,
|
||||||
|
1
|
||||||
|
))
|
||||||
|
print(f"[CREATE] 在'1.初核请示'下创建'1请示报告卡'记录 (ID: {new_id})")
|
||||||
|
|
||||||
|
if not in_interview_approval:
|
||||||
|
# 需要在"走读式谈话审批"下创建记录
|
||||||
|
new_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, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(insert_sql, (
|
||||||
|
new_id,
|
||||||
|
TENANT_ID,
|
||||||
|
1765273962700431, # 走读式谈话审批
|
||||||
|
'1请示报告卡',
|
||||||
|
None,
|
||||||
|
'/615873064429507639/TEMPLATE/2025/12/1.请示报告卡(初核谈话).docx',
|
||||||
|
CREATED_BY,
|
||||||
|
CREATED_BY,
|
||||||
|
1
|
||||||
|
))
|
||||||
|
print(f"[CREATE] 在'走读式谈话审批'下创建'1请示报告卡'记录 (ID: {new_id})")
|
||||||
|
|
||||||
|
# 更新现有记录的文件路径
|
||||||
|
for result in results:
|
||||||
|
if result['parent_id'] == 1765431558933731: # 1.初核请示
|
||||||
|
correct_path = '/615873064429507639/TEMPLATE/2025/12/1.请示报告卡(XXX).docx'
|
||||||
|
elif result['parent_id'] == 1765273962700431: # 走读式谈话审批
|
||||||
|
correct_path = '/615873064429507639/TEMPLATE/2025/12/1.请示报告卡(初核谈话).docx'
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if result['file_path'] != correct_path:
|
||||||
|
cursor.execute("""
|
||||||
|
UPDATE f_polic_file_config
|
||||||
|
SET file_path = %s, updated_time = NOW(), updated_by = %s
|
||||||
|
WHERE tenant_id = %s AND id = %s
|
||||||
|
""", (correct_path, UPDATED_BY, TENANT_ID, result['id']))
|
||||||
|
print(f"[UPDATE] 修复'1请示报告卡'的文件路径 (ID: {result['id']}): {result['file_path']} -> {correct_path}")
|
||||||
|
|
||||||
|
# 检查重复的"XXX初核情况报告"
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT id, name, file_path, parent_id
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND name LIKE %s
|
||||||
|
ORDER BY id
|
||||||
|
""", (TENANT_ID, '%XXX初核情况报告%'))
|
||||||
|
|
||||||
|
results = cursor.fetchall()
|
||||||
|
if len(results) > 1:
|
||||||
|
# 保留最新的,删除旧的
|
||||||
|
# 或者根据file_path判断哪个是正确的
|
||||||
|
# 根据目录结构,应该是"8.XXX初核情况报告.docx"
|
||||||
|
correct_name = 'XXX初核情况报告'
|
||||||
|
correct_path = '/615873064429507639/TEMPLATE/2025/12/8.XXX初核情况报告.docx'
|
||||||
|
|
||||||
|
for r in results:
|
||||||
|
if r['name'] == '8.XXX初核情况报告':
|
||||||
|
# 这个应该删除(名称带数字前缀)
|
||||||
|
cursor.execute("""
|
||||||
|
DELETE FROM f_polic_file_field
|
||||||
|
WHERE tenant_id = %s AND file_id = %s
|
||||||
|
""", (TENANT_ID, r['id']))
|
||||||
|
cursor.execute("""
|
||||||
|
DELETE FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND id = %s
|
||||||
|
""", (TENANT_ID, r['id']))
|
||||||
|
print(f"[DELETE] 删除重复记录: {r['name']} (ID: {r['id']})")
|
||||||
|
elif r['name'] == 'XXX初核情况报告':
|
||||||
|
# 更新这个记录的文件路径
|
||||||
|
if r['file_path'] != correct_path:
|
||||||
|
cursor.execute("""
|
||||||
|
UPDATE f_polic_file_config
|
||||||
|
SET file_path = %s, updated_time = NOW(), updated_by = %s
|
||||||
|
WHERE tenant_id = %s AND id = %s
|
||||||
|
""", (correct_path, UPDATED_BY, TENANT_ID, r['id']))
|
||||||
|
print(f"[UPDATE] 更新'XXX初核情况报告'的文件路径: {r['file_path']} -> {correct_path}")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
print("\n[OK] 修复完成")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
print(f"[ERROR] 修复失败: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
131
fix_duplicate_request_report_card.py
Normal file
131
fix_duplicate_request_report_card.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
"""
|
||||||
|
修复重复的"1请示报告卡"记录
|
||||||
|
确保每个文件在正确的位置只有一个记录
|
||||||
|
"""
|
||||||
|
import pymysql
|
||||||
|
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
UPDATED_BY = 655162080928945152
|
||||||
|
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 查找所有"1请示报告卡"记录
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT id, name, file_path, parent_id
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND name = %s
|
||||||
|
ORDER BY id
|
||||||
|
""", (TENANT_ID, '1请示报告卡'))
|
||||||
|
|
||||||
|
results = cursor.fetchall()
|
||||||
|
print(f"找到 {len(results)} 条'1请示报告卡'记录:\n")
|
||||||
|
|
||||||
|
# 根据file_path和parent_id判断哪些是正确的
|
||||||
|
correct_records = []
|
||||||
|
for r in results:
|
||||||
|
print(f"ID: {r['id']}, file_path: {r['file_path']}, parent_id: {r['parent_id']}")
|
||||||
|
|
||||||
|
# 判断是否正确
|
||||||
|
if r['parent_id'] == 1765431558933731: # 1.初核请示
|
||||||
|
if '1.请示报告卡(XXX)' in (r['file_path'] or ''):
|
||||||
|
correct_records.append(r)
|
||||||
|
elif r['parent_id'] == 1765273962700431: # 走读式谈话审批
|
||||||
|
if '1.请示报告卡(初核谈话)' in (r['file_path'] or ''):
|
||||||
|
correct_records.append(r)
|
||||||
|
|
||||||
|
print(f"\n正确的记录数: {len(correct_records)}")
|
||||||
|
|
||||||
|
# 删除不正确的记录
|
||||||
|
for r in results:
|
||||||
|
if r not in correct_records:
|
||||||
|
# 先删除关联关系
|
||||||
|
cursor.execute("""
|
||||||
|
DELETE FROM f_polic_file_field
|
||||||
|
WHERE tenant_id = %s AND file_id = %s
|
||||||
|
""", (TENANT_ID, r['id']))
|
||||||
|
# 删除模板记录
|
||||||
|
cursor.execute("""
|
||||||
|
DELETE FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND id = %s
|
||||||
|
""", (TENANT_ID, r['id']))
|
||||||
|
print(f"[DELETE] 删除不正确的记录: ID {r['id']}, file_path: {r['file_path']}, parent_id: {r['parent_id']}")
|
||||||
|
|
||||||
|
# 确保两个位置都有正确的记录
|
||||||
|
has_initial_request = any(r['parent_id'] == 1765431558933731 for r in correct_records)
|
||||||
|
has_interview_approval = any(r['parent_id'] == 1765273962700431 for r in correct_records)
|
||||||
|
|
||||||
|
if not has_initial_request:
|
||||||
|
# 创建"1.初核请示"下的记录
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
timestamp = int(time.time() * 1000)
|
||||||
|
random_part = random.randint(100000, 999999)
|
||||||
|
new_id = timestamp * 1000 + random_part
|
||||||
|
|
||||||
|
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, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(insert_sql, (
|
||||||
|
new_id,
|
||||||
|
TENANT_ID,
|
||||||
|
1765431558933731, # 1.初核请示
|
||||||
|
'1请示报告卡',
|
||||||
|
None,
|
||||||
|
'/615873064429507639/TEMPLATE/2025/12/1.请示报告卡(XXX).docx',
|
||||||
|
655162080928945152,
|
||||||
|
655162080928945152,
|
||||||
|
1
|
||||||
|
))
|
||||||
|
print(f"[CREATE] 在'1.初核请示'下创建'1请示报告卡'记录 (ID: {new_id})")
|
||||||
|
|
||||||
|
if not has_interview_approval:
|
||||||
|
# 创建"走读式谈话审批"下的记录
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
timestamp = int(time.time() * 1000)
|
||||||
|
random_part = random.randint(100000, 999999)
|
||||||
|
new_id = timestamp * 1000 + random_part
|
||||||
|
|
||||||
|
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, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(insert_sql, (
|
||||||
|
new_id,
|
||||||
|
TENANT_ID,
|
||||||
|
1765273962700431, # 走读式谈话审批
|
||||||
|
'1请示报告卡',
|
||||||
|
None,
|
||||||
|
'/615873064429507639/TEMPLATE/2025/12/1.请示报告卡(初核谈话).docx',
|
||||||
|
655162080928945152,
|
||||||
|
655162080928945152,
|
||||||
|
1
|
||||||
|
))
|
||||||
|
print(f"[CREATE] 在'走读式谈话审批'下创建'1请示报告卡'记录 (ID: {new_id})")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
print("\n[OK] 修复完成")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
print(f"[ERROR] 修复失败: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
61
fix_remaining_hierarchy_issues.py
Normal file
61
fix_remaining_hierarchy_issues.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
"""
|
||||||
|
修复剩余的层级结构问题
|
||||||
|
"""
|
||||||
|
import pymysql
|
||||||
|
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
UPDATED_BY = 655162080928945152
|
||||||
|
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 1. 修复"2保密承诺书"的parent_id(应该在"走读式谈话流程"下)
|
||||||
|
# "走读式谈话流程"的ID是 1765273962716807
|
||||||
|
cursor.execute("""
|
||||||
|
UPDATE f_polic_file_config
|
||||||
|
SET parent_id = %s, updated_time = NOW(), updated_by = %s
|
||||||
|
WHERE tenant_id = %s AND id = %s
|
||||||
|
""", (1765273962716807, UPDATED_BY, TENANT_ID, 1765425919729046))
|
||||||
|
print(f"[UPDATE] 更新'2保密承诺书'的parent_id: {cursor.rowcount} 条")
|
||||||
|
|
||||||
|
# 2. 检查"8.XXX初核情况报告"的位置(应该在"3.初核结论"下,而不是"走读式谈话流程"下)
|
||||||
|
# "3.初核结论"的ID是 1765431559135346
|
||||||
|
# 先查找"8.XXX初核情况报告"的ID
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT id, name, parent_id
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND name LIKE %s
|
||||||
|
""", (TENANT_ID, '%XXX初核情况报告%'))
|
||||||
|
result = cursor.fetchone()
|
||||||
|
if result:
|
||||||
|
file_id, file_name, current_parent = result
|
||||||
|
if current_parent != 1765431559135346:
|
||||||
|
cursor.execute("""
|
||||||
|
UPDATE f_polic_file_config
|
||||||
|
SET parent_id = %s, updated_time = NOW(), updated_by = %s
|
||||||
|
WHERE tenant_id = %s AND id = %s
|
||||||
|
""", (1765431559135346, UPDATED_BY, TENANT_ID, file_id))
|
||||||
|
print(f"[UPDATE] 更新'{file_name}'的parent_id: {cursor.rowcount} 条")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
print("\n[OK] 修复完成")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
print(f"[ERROR] 修复失败: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
234
fix_template_names.py
Normal file
234
fix_template_names.py
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
"""
|
||||||
|
检查并修复 f_polic_file_config 表中模板名称与文件名的对应关系
|
||||||
|
确保 name 字段与模板文档名称(去掉扩展名)完全一致
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pymysql
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
|
# 设置输出编码为UTF-8(Windows兼容)
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
import io
|
||||||
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||||
|
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||||
|
|
||||||
|
# 数据库连接配置
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
UPDATED_BY = 655162080928945152
|
||||||
|
TEMPLATE_BASE_DIR = 'template_finish'
|
||||||
|
|
||||||
|
|
||||||
|
def scan_template_files(base_dir: str) -> Dict[str, str]:
|
||||||
|
"""
|
||||||
|
扫描模板文件夹,获取所有模板文件信息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,key为MinIO路径(用于匹配),value为文件名(不含扩展名)
|
||||||
|
"""
|
||||||
|
base_path = Path(base_dir)
|
||||||
|
if not base_path.exists():
|
||||||
|
print(f"错误: 目录不存在 - {base_dir}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
templates = {}
|
||||||
|
|
||||||
|
print("=" * 80)
|
||||||
|
print("扫描模板文件...")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
for docx_file in sorted(base_path.rglob("*.docx")):
|
||||||
|
# 跳过临时文件
|
||||||
|
if docx_file.name.startswith("~$"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 获取文件名(不含扩展名)
|
||||||
|
file_name_without_ext = docx_file.stem
|
||||||
|
|
||||||
|
# 构建MinIO路径(用于匹配数据库中的file_path)
|
||||||
|
from datetime import datetime
|
||||||
|
now = datetime.now()
|
||||||
|
minio_path = f'/615873064429507639/TEMPLATE/{now.year}/{now.month:02d}/{docx_file.name}'
|
||||||
|
|
||||||
|
templates[minio_path] = {
|
||||||
|
'file_name': docx_file.name,
|
||||||
|
'name_without_ext': file_name_without_ext,
|
||||||
|
'relative_path': str(docx_file.relative_to(base_path))
|
||||||
|
}
|
||||||
|
|
||||||
|
print(f"找到 {len(templates)} 个模板文件\n")
|
||||||
|
return templates
|
||||||
|
|
||||||
|
|
||||||
|
def get_db_templates(conn) -> Dict[str, Dict]:
|
||||||
|
"""
|
||||||
|
获取数据库中所有模板记录
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,key为file_path,value为模板信息
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
SELECT id, name, file_path, parent_id
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND file_path IS NOT NULL
|
||||||
|
"""
|
||||||
|
cursor.execute(sql, (TENANT_ID,))
|
||||||
|
templates = cursor.fetchall()
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for template in templates:
|
||||||
|
if template['file_path']:
|
||||||
|
result[template['file_path']] = {
|
||||||
|
'id': template['id'],
|
||||||
|
'name': template['name'],
|
||||||
|
'file_path': template['file_path'],
|
||||||
|
'parent_id': template['parent_id']
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def update_template_name(conn, template_id: int, new_name: str, old_name: str):
|
||||||
|
"""
|
||||||
|
更新模板名称
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
update_sql = """
|
||||||
|
UPDATE f_polic_file_config
|
||||||
|
SET name = %s, updated_time = NOW(), updated_by = %s
|
||||||
|
WHERE id = %s AND tenant_id = %s
|
||||||
|
"""
|
||||||
|
cursor.execute(update_sql, (new_name, UPDATED_BY, template_id, TENANT_ID))
|
||||||
|
conn.commit()
|
||||||
|
print(f" [UPDATE] ID: {template_id}")
|
||||||
|
print(f" 旧名称: {old_name}")
|
||||||
|
print(f" 新名称: {new_name}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
print(f" [ERROR] 更新失败: {str(e)}")
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def match_file_path(file_path: str, db_paths: List[str]) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
匹配文件路径(可能日期不同)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path: 当前构建的MinIO路径
|
||||||
|
db_paths: 数据库中的所有路径列表
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
匹配的数据库路径,如果找到的话
|
||||||
|
"""
|
||||||
|
# 提取文件名
|
||||||
|
file_name = Path(file_path).name
|
||||||
|
|
||||||
|
# 在数据库路径中查找相同文件名的路径
|
||||||
|
for db_path in db_paths:
|
||||||
|
if Path(db_path).name == file_name:
|
||||||
|
return db_path
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
print("=" * 80)
|
||||||
|
print("检查并修复模板名称")
|
||||||
|
print("=" * 80)
|
||||||
|
print()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 连接数据库
|
||||||
|
print("1. 连接数据库...")
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
print("[OK] 数据库连接成功\n")
|
||||||
|
|
||||||
|
# 扫描模板文件
|
||||||
|
print("2. 扫描模板文件...")
|
||||||
|
file_templates = scan_template_files(TEMPLATE_BASE_DIR)
|
||||||
|
|
||||||
|
# 获取数据库模板
|
||||||
|
print("3. 获取数据库模板...")
|
||||||
|
db_templates = get_db_templates(conn)
|
||||||
|
print(f"[OK] 找到 {len(db_templates)} 个数据库模板\n")
|
||||||
|
|
||||||
|
# 检查并更新
|
||||||
|
print("4. 检查并更新模板名称...")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
updated_count = 0
|
||||||
|
not_found_count = 0
|
||||||
|
matched_count = 0
|
||||||
|
|
||||||
|
# 遍历文件模板
|
||||||
|
for file_path, file_info in file_templates.items():
|
||||||
|
file_name = file_info['file_name']
|
||||||
|
expected_name = file_info['name_without_ext']
|
||||||
|
|
||||||
|
# 尝试直接匹配
|
||||||
|
db_template = db_templates.get(file_path)
|
||||||
|
|
||||||
|
# 如果直接匹配失败,尝试通过文件名匹配
|
||||||
|
if not db_template:
|
||||||
|
matched_path = match_file_path(file_path, list(db_templates.keys()))
|
||||||
|
if matched_path:
|
||||||
|
db_template = db_templates[matched_path]
|
||||||
|
|
||||||
|
if db_template:
|
||||||
|
matched_count += 1
|
||||||
|
current_name = db_template['name']
|
||||||
|
|
||||||
|
# 检查名称是否一致
|
||||||
|
if current_name != expected_name:
|
||||||
|
print(f"\n文件: {file_name}")
|
||||||
|
if update_template_name(conn, db_template['id'], expected_name, current_name):
|
||||||
|
updated_count += 1
|
||||||
|
else:
|
||||||
|
print(f" [OK] {file_name} - 名称已正确")
|
||||||
|
else:
|
||||||
|
not_found_count += 1
|
||||||
|
print(f" [WARN] 未找到: {file_name}")
|
||||||
|
|
||||||
|
print("\n" + "=" * 80)
|
||||||
|
print("检查完成")
|
||||||
|
print("=" * 80)
|
||||||
|
print(f"总文件数: {len(file_templates)}")
|
||||||
|
print(f"匹配成功: {matched_count}")
|
||||||
|
print(f"更新数量: {updated_count}")
|
||||||
|
print(f"未找到: {not_found_count}")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n[ERROR] 发生错误: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.rollback()
|
||||||
|
finally:
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.close()
|
||||||
|
print("\n数据库连接已关闭")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
372
process_confidentiality_commitment_non_party.py
Normal file
372
process_confidentiality_commitment_non_party.py
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
"""
|
||||||
|
处理"6.1保密承诺书(谈话对象使用-非中共党员用).docx"
|
||||||
|
- 解析占位符
|
||||||
|
- 上传到MinIO
|
||||||
|
- 更新数据库
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import pymysql
|
||||||
|
from minio import Minio
|
||||||
|
from minio.error import S3Error
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from docx import Document
|
||||||
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
# 设置输出编码为UTF-8(Windows兼容)
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
import io
|
||||||
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||||
|
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||||
|
|
||||||
|
# MinIO连接配置
|
||||||
|
MINIO_CONFIG = {
|
||||||
|
'endpoint': 'minio.datacubeworld.com:9000',
|
||||||
|
'access_key': 'JOLXFXny3avFSzB0uRA5',
|
||||||
|
'secret_key': 'G1BR8jStNfovkfH5ou39EmPl34E4l7dGrnd3Cz0I',
|
||||||
|
'secure': True
|
||||||
|
}
|
||||||
|
|
||||||
|
# 数据库连接配置
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 固定值
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
CREATED_BY = 655162080928945152
|
||||||
|
UPDATED_BY = 655162080928945152
|
||||||
|
BUCKET_NAME = 'finyx'
|
||||||
|
|
||||||
|
# 文件路径
|
||||||
|
TEMPLATE_FILE = 'template_finish/2-初核模版/2.谈话审批/走读式谈话流程/6.1保密承诺书(谈话对象使用-非中共党员用).docx'
|
||||||
|
PARENT_ID = 1765273962716807 # 走读式谈话流程的ID
|
||||||
|
TEMPLATE_NAME = '6.1保密承诺书(谈话对象使用-非中共党员用)'
|
||||||
|
|
||||||
|
|
||||||
|
def generate_id():
|
||||||
|
"""生成ID"""
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
timestamp = int(time.time() * 1000)
|
||||||
|
random_part = random.randint(100000, 999999)
|
||||||
|
return timestamp * 1000 + random_part
|
||||||
|
|
||||||
|
|
||||||
|
def extract_placeholders_from_docx(file_path: str) -> List[str]:
|
||||||
|
"""
|
||||||
|
从docx文件中提取所有占位符
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path: docx文件路径
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
占位符列表,格式: ['field_code1', 'field_code2', ...]
|
||||||
|
"""
|
||||||
|
placeholders = set()
|
||||||
|
pattern = r'\{\{([^}]+)\}\}' # 匹配 {{field_code}} 格式
|
||||||
|
|
||||||
|
try:
|
||||||
|
doc = Document(file_path)
|
||||||
|
|
||||||
|
# 从段落中提取占位符
|
||||||
|
for paragraph in doc.paragraphs:
|
||||||
|
text = paragraph.text
|
||||||
|
matches = re.findall(pattern, text)
|
||||||
|
for match in matches:
|
||||||
|
placeholders.add(match.strip())
|
||||||
|
|
||||||
|
# 从表格中提取占位符
|
||||||
|
for table in doc.tables:
|
||||||
|
for row in table.rows:
|
||||||
|
for cell in row.cells:
|
||||||
|
for paragraph in cell.paragraphs:
|
||||||
|
text = paragraph.text
|
||||||
|
matches = re.findall(pattern, text)
|
||||||
|
for match in matches:
|
||||||
|
placeholders.add(match.strip())
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" 错误: 读取文件失败 - {str(e)}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
return sorted(list(placeholders))
|
||||||
|
|
||||||
|
|
||||||
|
def upload_to_minio(file_path: str, minio_client: Minio) -> str:
|
||||||
|
"""
|
||||||
|
上传文件到MinIO
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path: 本地文件路径
|
||||||
|
minio_client: MinIO客户端实例
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
MinIO中的相对路径
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 检查存储桶是否存在
|
||||||
|
found = minio_client.bucket_exists(BUCKET_NAME)
|
||||||
|
if not found:
|
||||||
|
raise Exception(f"存储桶 '{BUCKET_NAME}' 不存在,请先创建")
|
||||||
|
|
||||||
|
# 生成MinIO对象路径(使用当前日期)
|
||||||
|
now = datetime.now()
|
||||||
|
file_name = Path(file_path).name
|
||||||
|
object_name = f'{TENANT_ID}/TEMPLATE/{now.year}/{now.month:02d}/{file_name}'
|
||||||
|
|
||||||
|
# 上传文件
|
||||||
|
minio_client.fput_object(
|
||||||
|
BUCKET_NAME,
|
||||||
|
object_name,
|
||||||
|
file_path,
|
||||||
|
content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 返回相对路径(以/开头)
|
||||||
|
return f"/{object_name}"
|
||||||
|
|
||||||
|
except S3Error as e:
|
||||||
|
raise Exception(f"MinIO错误: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"上传文件时发生错误: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_db_fields(conn) -> Dict[str, Dict]:
|
||||||
|
"""
|
||||||
|
获取数据库中所有字段(field_type=2的输出字段)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,key为filed_code,value为字段信息
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
SELECT id, name, filed_code, field_type
|
||||||
|
FROM f_polic_field
|
||||||
|
WHERE tenant_id = %s AND field_type = 2
|
||||||
|
"""
|
||||||
|
cursor.execute(sql, (TENANT_ID,))
|
||||||
|
fields = cursor.fetchall()
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for field in fields:
|
||||||
|
result[field['filed_code']] = {
|
||||||
|
'id': field['id'],
|
||||||
|
'name': field['name'],
|
||||||
|
'filed_code': field['filed_code'],
|
||||||
|
'field_type': field['field_type']
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def match_placeholders_to_fields(placeholders: List[str], fields: Dict[str, Dict]) -> Tuple[List[int], List[str]]:
|
||||||
|
"""
|
||||||
|
匹配占位符到数据库字段
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(匹配的字段ID列表, 未匹配的占位符列表)
|
||||||
|
"""
|
||||||
|
matched_field_ids = []
|
||||||
|
unmatched_placeholders = []
|
||||||
|
|
||||||
|
for placeholder in placeholders:
|
||||||
|
if placeholder in fields:
|
||||||
|
matched_field_ids.append(fields[placeholder]['id'])
|
||||||
|
else:
|
||||||
|
unmatched_placeholders.append(placeholder)
|
||||||
|
|
||||||
|
return matched_field_ids, unmatched_placeholders
|
||||||
|
|
||||||
|
|
||||||
|
def create_or_update_template(conn, template_name: str, file_path: str, minio_path: str, parent_id: Optional[int]) -> int:
|
||||||
|
"""
|
||||||
|
创建或更新模板记录
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
模板ID
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 查找是否已存在(通过名称和parent_id匹配)
|
||||||
|
sql = """
|
||||||
|
SELECT id, name, file_path, parent_id
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND name = %s AND parent_id = %s
|
||||||
|
"""
|
||||||
|
cursor.execute(sql, (TENANT_ID, template_name, parent_id))
|
||||||
|
existing = cursor.fetchone()
|
||||||
|
|
||||||
|
if existing:
|
||||||
|
# 更新现有记录
|
||||||
|
template_id = existing['id']
|
||||||
|
update_sql = """
|
||||||
|
UPDATE f_polic_file_config
|
||||||
|
SET file_path = %s, updated_time = NOW(), updated_by = %s, state = 1
|
||||||
|
WHERE id = %s AND tenant_id = %s
|
||||||
|
"""
|
||||||
|
cursor.execute(update_sql, (minio_path, UPDATED_BY, template_id, TENANT_ID))
|
||||||
|
conn.commit()
|
||||||
|
print(f" [UPDATE] 更新模板记录 (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, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(insert_sql, (
|
||||||
|
template_id,
|
||||||
|
TENANT_ID,
|
||||||
|
parent_id,
|
||||||
|
template_name,
|
||||||
|
None, # input_data
|
||||||
|
minio_path,
|
||||||
|
CREATED_BY,
|
||||||
|
CREATED_BY,
|
||||||
|
1 # state: 1表示启用
|
||||||
|
))
|
||||||
|
conn.commit()
|
||||||
|
print(f" [CREATE] 创建模板记录 (ID: {template_id})")
|
||||||
|
return template_id
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise Exception(f"创建或更新模板失败: {str(e)}")
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def update_template_field_relations(conn, template_id: int, field_ids: List[int]):
|
||||||
|
"""
|
||||||
|
更新模板-字段关联关系
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 删除旧的关联关系
|
||||||
|
delete_sql = """
|
||||||
|
DELETE FROM f_polic_file_field
|
||||||
|
WHERE tenant_id = %s AND file_id = %s
|
||||||
|
"""
|
||||||
|
cursor.execute(delete_sql, (TENANT_ID, template_id))
|
||||||
|
|
||||||
|
# 插入新的关联关系
|
||||||
|
if field_ids:
|
||||||
|
insert_sql = """
|
||||||
|
INSERT INTO f_polic_file_field
|
||||||
|
(tenant_id, file_id, filed_id, created_time, created_by, updated_time, updated_by)
|
||||||
|
VALUES (%s, %s, %s, NOW(), %s, NOW(), %s)
|
||||||
|
"""
|
||||||
|
for field_id in field_ids:
|
||||||
|
cursor.execute(insert_sql, (TENANT_ID, template_id, field_id, CREATED_BY, CREATED_BY))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
print(f" [UPDATE] 更新字段关联关系: {len(field_ids)} 个字段")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise Exception(f"更新字段关联关系失败: {str(e)}")
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
print("=" * 80)
|
||||||
|
print("处理保密承诺书(非中共党员用)模板")
|
||||||
|
print("=" * 80)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 检查文件是否存在
|
||||||
|
if not os.path.exists(TEMPLATE_FILE):
|
||||||
|
print(f"错误: 文件不存在 - {TEMPLATE_FILE}")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"文件路径: {TEMPLATE_FILE}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 1. 提取占位符
|
||||||
|
print("1. 提取占位符...")
|
||||||
|
placeholders = extract_placeholders_from_docx(TEMPLATE_FILE)
|
||||||
|
print(f" 找到 {len(placeholders)} 个占位符:")
|
||||||
|
for i, placeholder in enumerate(placeholders, 1):
|
||||||
|
print(f" {i}. {{{{ {placeholder} }}}}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 2. 连接数据库和MinIO
|
||||||
|
print("2. 连接数据库和MinIO...")
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
minio_client = Minio(
|
||||||
|
MINIO_CONFIG['endpoint'],
|
||||||
|
access_key=MINIO_CONFIG['access_key'],
|
||||||
|
secret_key=MINIO_CONFIG['secret_key'],
|
||||||
|
secure=MINIO_CONFIG['secure']
|
||||||
|
)
|
||||||
|
print(" [OK] 连接成功\n")
|
||||||
|
|
||||||
|
# 3. 获取数据库字段
|
||||||
|
print("3. 获取数据库字段...")
|
||||||
|
db_fields = get_db_fields(conn)
|
||||||
|
print(f" [OK] 找到 {len(db_fields)} 个输出字段\n")
|
||||||
|
|
||||||
|
# 4. 匹配占位符到字段
|
||||||
|
print("4. 匹配占位符到字段...")
|
||||||
|
matched_field_ids, unmatched_placeholders = match_placeholders_to_fields(placeholders, db_fields)
|
||||||
|
print(f" 匹配成功: {len(matched_field_ids)} 个")
|
||||||
|
print(f" 未匹配: {len(unmatched_placeholders)} 个")
|
||||||
|
if unmatched_placeholders:
|
||||||
|
print(f" 未匹配的占位符: {', '.join(unmatched_placeholders)}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 5. 上传到MinIO
|
||||||
|
print("5. 上传到MinIO...")
|
||||||
|
minio_path = upload_to_minio(TEMPLATE_FILE, minio_client)
|
||||||
|
print(f" [OK] MinIO路径: {minio_path}\n")
|
||||||
|
|
||||||
|
# 6. 创建或更新数据库记录
|
||||||
|
print("6. 创建或更新数据库记录...")
|
||||||
|
template_id = create_or_update_template(conn, TEMPLATE_NAME, TEMPLATE_FILE, minio_path, PARENT_ID)
|
||||||
|
print(f" [OK] 模板ID: {template_id}\n")
|
||||||
|
|
||||||
|
# 7. 更新字段关联关系
|
||||||
|
print("7. 更新字段关联关系...")
|
||||||
|
update_template_field_relations(conn, template_id, matched_field_ids)
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("=" * 80)
|
||||||
|
print("处理完成!")
|
||||||
|
print("=" * 80)
|
||||||
|
print(f"模板ID: {template_id}")
|
||||||
|
print(f"MinIO路径: {minio_path}")
|
||||||
|
print(f"关联字段数: {len(matched_field_ids)}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n[ERROR] 发生错误: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.rollback()
|
||||||
|
finally:
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.close()
|
||||||
|
print("\n数据库连接已关闭")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
Binary file not shown.
@ -138,7 +138,6 @@ class DocumentService:
|
|||||||
doc = Document(template_path)
|
doc = Document(template_path)
|
||||||
print(f"[DEBUG] 文档包含 {len(doc.paragraphs)} 个段落, {len(doc.tables)} 个表格")
|
print(f"[DEBUG] 文档包含 {len(doc.paragraphs)} 个段落, {len(doc.tables)} 个表格")
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
def replace_placeholder_in_paragraph(paragraph):
|
def replace_placeholder_in_paragraph(paragraph):
|
||||||
"""在段落中替换占位符(处理跨run的情况)"""
|
"""在段落中替换占位符(处理跨run的情况)"""
|
||||||
try:
|
try:
|
||||||
@ -204,18 +203,6 @@ class DocumentService:
|
|||||||
if placeholder in before_text and placeholder not in after_text:
|
if placeholder in before_text and placeholder not in after_text:
|
||||||
replaced_placeholders.add(field_code)
|
replaced_placeholders.add(field_code)
|
||||||
total_replacements += before_text.count(placeholder)
|
total_replacements += before_text.count(placeholder)
|
||||||
=======
|
|
||||||
# 替换占位符 {{field_code}} 为实际值
|
|
||||||
for paragraph in doc.paragraphs:
|
|
||||||
# 替换段落文本中的占位符
|
|
||||||
for field_code, field_value in field_data.items():
|
|
||||||
placeholder = f"{{{{{field_code}}}}}"
|
|
||||||
if placeholder in paragraph.text:
|
|
||||||
# 替换占位符
|
|
||||||
for run in paragraph.runs:
|
|
||||||
if placeholder in run.text:
|
|
||||||
run.text = run.text.replace(placeholder, field_value or '')
|
|
||||||
>>>>>>> parent of 4897c96 (添加通过taskId获取文档的接口,支持文件列表查询和参数验证,增强错误处理能力。同时,优化文档生成逻辑,确保生成的文档名称和路径的准确性。)
|
|
||||||
|
|
||||||
# 替换表格中的占位符
|
# 替换表格中的占位符
|
||||||
try:
|
try:
|
||||||
@ -264,7 +251,6 @@ class DocumentService:
|
|||||||
for table in doc.tables:
|
for table in doc.tables:
|
||||||
for row in table.rows:
|
for row in table.rows:
|
||||||
for cell in row.cells:
|
for cell in row.cells:
|
||||||
<<<<<<< HEAD
|
|
||||||
if hasattr(cell, 'paragraphs'):
|
if hasattr(cell, 'paragraphs'):
|
||||||
for paragraph in cell.paragraphs:
|
for paragraph in cell.paragraphs:
|
||||||
text = paragraph.text
|
text = paragraph.text
|
||||||
@ -281,15 +267,6 @@ class DocumentService:
|
|||||||
print(f" - ⚠️ 仍有未替换的占位符: {sorted(remaining_placeholders)}")
|
print(f" - ⚠️ 仍有未替换的占位符: {sorted(remaining_placeholders)}")
|
||||||
else:
|
else:
|
||||||
print(f" - ✓ 所有占位符已成功替换")
|
print(f" - ✓ 所有占位符已成功替换")
|
||||||
=======
|
|
||||||
for paragraph in cell.paragraphs:
|
|
||||||
for field_code, field_value in field_data.items():
|
|
||||||
placeholder = f"{{{{{field_code}}}}}"
|
|
||||||
if placeholder in paragraph.text:
|
|
||||||
for run in paragraph.runs:
|
|
||||||
if placeholder in run.text:
|
|
||||||
run.text = run.text.replace(placeholder, field_value or '')
|
|
||||||
>>>>>>> parent of 4897c96 (添加通过taskId获取文档的接口,支持文件列表查询和参数验证,增强错误处理能力。同时,优化文档生成逻辑,确保生成的文档名称和路径的准确性。)
|
|
||||||
|
|
||||||
# 保存到临时文件
|
# 保存到临时文件
|
||||||
temp_dir = tempfile.gettempdir()
|
temp_dir = tempfile.gettempdir()
|
||||||
@ -493,9 +470,11 @@ class DocumentService:
|
|||||||
if target_name and target_name.strip():
|
if target_name and target_name.strip():
|
||||||
suffix = f"_{target_name.strip()}"
|
suffix = f"_{target_name.strip()}"
|
||||||
|
|
||||||
<<<<<<< HEAD
|
# 生成新文件名(确保是.docx格式)
|
||||||
# 生成新文件名
|
generated_name = f"{base_name}{suffix}.docx"
|
||||||
return f"{base_name}{suffix}.docx"
|
print(f"[DEBUG] 文档名称生成: '{original_file_name}' -> '{generated_name}' (base_name='{base_name}', suffix='{suffix}')")
|
||||||
|
|
||||||
|
return generated_name
|
||||||
|
|
||||||
def generate_presigned_download_url(self, file_path: str, expires_days: int = 7) -> Optional[str]:
|
def generate_presigned_download_url(self, file_path: str, expires_days: int = 7) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
@ -530,11 +509,4 @@ class DocumentService:
|
|||||||
# 如果生成URL失败,记录错误但不影响主流程
|
# 如果生成URL失败,记录错误但不影响主流程
|
||||||
print(f"生成预签名URL失败: {str(e)}")
|
print(f"生成预签名URL失败: {str(e)}")
|
||||||
return None
|
return None
|
||||||
=======
|
|
||||||
# 生成新文件名(确保是.docx格式)
|
|
||||||
generated_name = f"{base_name}{suffix}.docx"
|
|
||||||
print(f"[DEBUG] 文档名称生成: '{original_file_name}' -> '{generated_name}' (base_name='{base_name}', suffix='{suffix}')")
|
|
||||||
|
|
||||||
return generated_name
|
|
||||||
>>>>>>> e3f4a394c1a4333db2fd3a9383be29fa9d9055e0
|
|
||||||
|
|
||||||
|
|||||||
@ -326,10 +326,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>文件列表</label>
|
<label>文件列表(文档模板类型)</label>
|
||||||
<div style="margin-bottom: 10px;">
|
<div style="margin-bottom: 10px;">
|
||||||
<button class="btn btn-secondary" onclick="loadAvailableFiles()" style="margin-right: 10px;">📋 加载可用文件列表</button>
|
<button class="btn btn-secondary" onclick="loadAvailableFiles()" style="margin-right: 10px;">📋 加载全部可用模板</button>
|
||||||
<button class="btn btn-secondary" onclick="addFileItem()">+ 手动添加文件</button>
|
<button class="btn btn-secondary" onclick="addFileItem()" style="margin-right: 10px;">+ 手动添加文件</button>
|
||||||
|
<button class="btn btn-danger" onclick="clearAllFiles()">🗑️ 清空列表</button>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom: 10px; padding: 10px; background: #f0f0f0; border-radius: 4px; font-size: 13px; color: #666;">
|
||||||
|
💡 提示:点击"加载全部可用模板"可以加载所有可用的文档模板类型,方便测试不同模板的生成效果
|
||||||
</div>
|
</div>
|
||||||
<div id="fileListContainer">
|
<div id="fileListContainer">
|
||||||
<!-- 动态生成的文件列表 -->
|
<!-- 动态生成的文件列表 -->
|
||||||
@ -568,16 +572,12 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加前5个文件作为示例
|
// 加载所有可用文件
|
||||||
filesWithPath.slice(0, 5).forEach(file => {
|
filesWithPath.forEach(file => {
|
||||||
addFileItem(file.fileId, file.fileName);
|
addFileItem(file.fileId, file.fileName);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (filesWithPath.length > 5) {
|
alert(`已加载全部 ${filesWithPath.length} 个可用文件模板`);
|
||||||
alert(`已加载前5个文件,共找到 ${filesWithPath.length} 个可用文件`);
|
|
||||||
} else {
|
|
||||||
alert(`已加载 ${filesWithPath.length} 个可用文件`);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
alert('获取文件列表失败: ' + (result.errorMsg || '未知错误'));
|
alert('获取文件列表失败: ' + (result.errorMsg || '未知错误'));
|
||||||
}
|
}
|
||||||
@ -603,7 +603,7 @@
|
|||||||
addGenerateField('department_opinion', '');
|
addGenerateField('department_opinion', '');
|
||||||
addGenerateField('filler_name', '');
|
addGenerateField('filler_name', '');
|
||||||
|
|
||||||
// 自动加载可用的文件列表(只加载前2个作为示例)
|
// 自动加载所有可用的文件列表
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/file-configs');
|
const response = await fetch('/api/file-configs');
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
@ -612,19 +612,19 @@
|
|||||||
// 只添加有filePath的文件(有模板文件的)
|
// 只添加有filePath的文件(有模板文件的)
|
||||||
const filesWithPath = result.data.fileConfigs.filter(f => f.filePath);
|
const filesWithPath = result.data.fileConfigs.filter(f => f.filePath);
|
||||||
|
|
||||||
// 添加前2个文件作为示例
|
// 加载所有可用文件
|
||||||
filesWithPath.slice(0, 2).forEach(file => {
|
filesWithPath.forEach(file => {
|
||||||
addFileItem(file.fileId, file.fileName);
|
addFileItem(file.fileId, file.fileName);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (filesWithPath.length > 0) {
|
||||||
|
console.log(`已自动加载 ${filesWithPath.length} 个可用文件模板`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果加载失败,使用默认的fileId
|
console.warn('未找到可用的文件配置');
|
||||||
addFileItem(1765273961883544, '初步核实审批表.doc'); // 2.初步核实审批表(XXX)
|
|
||||||
addFileItem(1765273961563507, '请示报告卡.doc'); // 1.请示报告卡(XXX)
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 如果加载失败,使用默认的fileId
|
console.warn('自动加载文件列表失败:', error);
|
||||||
addFileItem(1765273961883544, '初步核实审批表.doc');
|
|
||||||
addFileItem(1765273961563507, '请示报告卡.doc');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -769,8 +769,15 @@
|
|||||||
result.data.fpolicFieldParamFileList.forEach(file => {
|
result.data.fpolicFieldParamFileList.forEach(file => {
|
||||||
html += `<div class="result-item">
|
html += `<div class="result-item">
|
||||||
<strong>${file.fileName}:</strong><br>
|
<strong>${file.fileName}:</strong><br>
|
||||||
文件路径: ${file.filePath || '(无路径)'}
|
文件路径: ${file.filePath || '(无路径)'}<br>`;
|
||||||
</div>`;
|
|
||||||
|
// 如果有下载链接,显示可点击的链接
|
||||||
|
if (file.downloadUrl) {
|
||||||
|
html += `下载链接: <a href="${file.downloadUrl}" target="_blank" style="color: #667eea; text-decoration: underline; word-break: break-all;">${file.downloadUrl}</a><br>`;
|
||||||
|
html += `<button class="btn btn-secondary" onclick="window.open('${file.downloadUrl}', '_blank')" style="margin-top: 5px; padding: 6px 15px; font-size: 14px;">📥 下载文档</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
html += `</div>`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -796,6 +803,12 @@
|
|||||||
btn.closest('.field-row').remove();
|
btn.closest('.field-row').remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearAllFiles() {
|
||||||
|
if (confirm('确定要清空所有文件列表吗?')) {
|
||||||
|
document.getElementById('fileListContainer').innerHTML = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function displayError(tabType, errorMsg) {
|
function displayError(tabType, errorMsg) {
|
||||||
const resultSection = document.getElementById(tabType + 'ResultSection');
|
const resultSection = document.getElementById(tabType + 'ResultSection');
|
||||||
const resultBox = document.getElementById(tabType + 'ResultBox');
|
const resultBox = document.getElementById(tabType + 'ResultBox');
|
||||||
|
|||||||
BIN
template_finish/2-初核模版/1.初核请示/~$初步核实审批表(XXX).docx
Normal file
BIN
template_finish/2-初核模版/1.初核请示/~$初步核实审批表(XXX).docx
Normal file
Binary file not shown.
BIN
template_finish/2-初核模版/1.初核请示/~$请示报告卡(XXX).docx
Normal file
BIN
template_finish/2-初核模版/1.初核请示/~$请示报告卡(XXX).docx
Normal file
Binary file not shown.
Binary file not shown.
BIN
template_finish/2-初核模版/3.初核结论/~$1请示报告卡(初核报告结论) .docx
Normal file
BIN
template_finish/2-初核模版/3.初核结论/~$1请示报告卡(初核报告结论) .docx
Normal file
Binary file not shown.
BIN
template_finish/2-初核模版/3.初核结论/~$XXX初核情况报告.docx
Normal file
BIN
template_finish/2-初核模版/3.初核结论/~$XXX初核情况报告.docx
Normal file
Binary file not shown.
472
update_template_hierarchy.py
Normal file
472
update_template_hierarchy.py
Normal file
@ -0,0 +1,472 @@
|
|||||||
|
"""
|
||||||
|
根据 template_finish/ 目录结构更新 f_polic_file_config 表中的层级结构
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import pymysql
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
# 设置输出编码为UTF-8(Windows兼容)
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
import io
|
||||||
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||||
|
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||||
|
|
||||||
|
# 数据库连接配置
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
CREATED_BY = 655162080928945152
|
||||||
|
UPDATED_BY = 655162080928945152
|
||||||
|
TEMPLATE_BASE_DIR = 'template_finish'
|
||||||
|
|
||||||
|
|
||||||
|
def generate_id():
|
||||||
|
"""生成ID"""
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
timestamp = int(time.time() * 1000)
|
||||||
|
random_part = random.randint(100000, 999999)
|
||||||
|
return timestamp * 1000 + random_part
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_name(name: str) -> str:
|
||||||
|
"""
|
||||||
|
标准化名称(去掉扩展名、括号内容、数字前缀等)
|
||||||
|
用于匹配数据库中的记录
|
||||||
|
"""
|
||||||
|
# 去掉扩展名
|
||||||
|
name = Path(name).stem if '.' in name else name
|
||||||
|
|
||||||
|
# 去掉括号内容
|
||||||
|
import re
|
||||||
|
name = re.sub(r'[((].*?[))]', '', name)
|
||||||
|
name = name.strip()
|
||||||
|
|
||||||
|
# 去掉数字前缀和点号
|
||||||
|
name = re.sub(r'^\d+[\.\-]?\s*', '', name)
|
||||||
|
name = name.strip()
|
||||||
|
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def scan_directory_structure(base_dir: str) -> Dict:
|
||||||
|
"""
|
||||||
|
扫描目录结构,构建层级关系
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,包含目录和文件的层级信息
|
||||||
|
"""
|
||||||
|
base_path = Path(base_dir)
|
||||||
|
if not base_path.exists():
|
||||||
|
print(f"错误: 目录不存在 - {base_dir}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
structure = {
|
||||||
|
'directories': [], # 目录节点列表
|
||||||
|
'files': [], # 文件节点列表
|
||||||
|
'name_to_id': {} # 名称到ID的映射(用于查找parent_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
print("=" * 80)
|
||||||
|
print("扫描目录结构...")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
# 遍历所有目录和文件
|
||||||
|
for item in base_path.rglob("*"):
|
||||||
|
relative_path = item.relative_to(base_path)
|
||||||
|
parts = relative_path.parts
|
||||||
|
|
||||||
|
if item.is_dir():
|
||||||
|
# 目录节点
|
||||||
|
level = len(parts) - 1 # 层级(从0开始)
|
||||||
|
dir_name = parts[-1]
|
||||||
|
parent_path = str(Path(*parts[:-1])) if len(parts) > 1 else None
|
||||||
|
|
||||||
|
structure['directories'].append({
|
||||||
|
'name': dir_name,
|
||||||
|
'path': str(relative_path),
|
||||||
|
'level': level,
|
||||||
|
'parent_path': parent_path,
|
||||||
|
'parent_id': None # 稍后设置
|
||||||
|
})
|
||||||
|
|
||||||
|
elif item.is_file() and item.suffix == '.docx' and not item.name.startswith("~$"):
|
||||||
|
# 文件节点
|
||||||
|
level = len(parts) - 1
|
||||||
|
file_name = item.name
|
||||||
|
parent_path = str(Path(*parts[:-1])) if len(parts) > 1 else None
|
||||||
|
|
||||||
|
structure['files'].append({
|
||||||
|
'name': file_name,
|
||||||
|
'path': str(relative_path),
|
||||||
|
'level': level,
|
||||||
|
'parent_path': parent_path,
|
||||||
|
'parent_id': None, # 稍后设置
|
||||||
|
'file_path': str(item)
|
||||||
|
})
|
||||||
|
|
||||||
|
# 按层级排序
|
||||||
|
structure['directories'].sort(key=lambda x: (x['level'], x['path']))
|
||||||
|
structure['files'].sort(key=lambda x: (x['level'], x['path']))
|
||||||
|
|
||||||
|
print(f"找到 {len(structure['directories'])} 个目录节点")
|
||||||
|
print(f"找到 {len(structure['files'])} 个文件节点")
|
||||||
|
|
||||||
|
return structure
|
||||||
|
|
||||||
|
|
||||||
|
def get_existing_templates(conn) -> Dict:
|
||||||
|
"""
|
||||||
|
获取数据库中现有的模板记录
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,key为标准化名称,value为模板信息
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for template in templates:
|
||||||
|
normalized_name = normalize_name(template['name'])
|
||||||
|
result[normalized_name] = {
|
||||||
|
'id': template['id'],
|
||||||
|
'name': template['name'],
|
||||||
|
'normalized_name': normalized_name,
|
||||||
|
'parent_id': template['parent_id'],
|
||||||
|
'file_path': template['file_path'],
|
||||||
|
'state': template['state']
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def find_template_by_name(existing_templates: Dict, name: str, prefer_directory: bool = False) -> Optional[Dict]:
|
||||||
|
"""
|
||||||
|
根据名称查找模板(支持标准化匹配)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: 模板名称
|
||||||
|
prefer_directory: 是否优先匹配目录节点
|
||||||
|
"""
|
||||||
|
normalized = normalize_name(name)
|
||||||
|
|
||||||
|
# 精确匹配标准化名称
|
||||||
|
if normalized in existing_templates:
|
||||||
|
template = existing_templates[normalized]
|
||||||
|
# 如果prefer_directory为True,且找到的是文件,继续查找目录
|
||||||
|
if prefer_directory and template.get('file_path') is not None:
|
||||||
|
pass # 继续查找
|
||||||
|
else:
|
||||||
|
return template
|
||||||
|
|
||||||
|
# 模糊匹配(包含关系)
|
||||||
|
candidates = []
|
||||||
|
for key, template in existing_templates.items():
|
||||||
|
if key.startswith("DIR:"):
|
||||||
|
# 目录节点
|
||||||
|
if normalized in template.get('normalized_name', '') or template.get('normalized_name', '') in normalized:
|
||||||
|
candidates.append((template, True))
|
||||||
|
else:
|
||||||
|
# 文件节点
|
||||||
|
if normalized in template.get('normalized_name', '') or template.get('normalized_name', '') in normalized:
|
||||||
|
candidates.append((template, False))
|
||||||
|
|
||||||
|
# 如果prefer_directory,优先返回目录节点
|
||||||
|
if prefer_directory:
|
||||||
|
for template, is_dir in candidates:
|
||||||
|
if is_dir:
|
||||||
|
return template
|
||||||
|
|
||||||
|
# 返回第一个匹配的
|
||||||
|
if candidates:
|
||||||
|
return candidates[0][0]
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def create_or_update_directory(conn, dir_info: Dict, parent_id: Optional[int], existing_templates: Dict) -> int:
|
||||||
|
"""
|
||||||
|
创建或更新目录节点
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
目录节点的ID
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 先通过路径查找(最准确)
|
||||||
|
path_key = f"DIR:{dir_info['path']}"
|
||||||
|
existing = existing_templates.get(path_key)
|
||||||
|
|
||||||
|
# 如果没找到,再通过名称查找(优先目录节点)
|
||||||
|
if not existing:
|
||||||
|
existing = find_template_by_name(existing_templates, dir_info['name'], prefer_directory=True)
|
||||||
|
# 确保找到的是目录节点(file_path为None)
|
||||||
|
if existing and existing.get('file_path') is not None:
|
||||||
|
existing = None
|
||||||
|
|
||||||
|
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_info['name']} (ID: {template_id}, parent_id: {parent_id})")
|
||||||
|
else:
|
||||||
|
print(f" [KEEP] 保持目录: {dir_info['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, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(insert_sql, (
|
||||||
|
template_id,
|
||||||
|
TENANT_ID,
|
||||||
|
parent_id,
|
||||||
|
dir_info['name'],
|
||||||
|
None, # input_data
|
||||||
|
None, # file_path(目录节点没有文件路径)
|
||||||
|
CREATED_BY,
|
||||||
|
CREATED_BY,
|
||||||
|
1 # state: 1表示启用
|
||||||
|
))
|
||||||
|
conn.commit()
|
||||||
|
print(f" [CREATE] 创建目录: {dir_info['name']} (ID: {template_id}, parent_id: {parent_id})")
|
||||||
|
return template_id
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise Exception(f"创建或更新目录失败: {str(e)}")
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def update_file_parent(conn, file_info: Dict, parent_id: Optional[int], existing_templates: Dict) -> Optional[int]:
|
||||||
|
"""
|
||||||
|
更新文件节点的parent_id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
文件节点的ID,如果未找到则返回None
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 查找文件(使用文件名匹配)
|
||||||
|
existing = find_template_by_name(existing_templates, file_info['name'])
|
||||||
|
|
||||||
|
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})")
|
||||||
|
return template_id
|
||||||
|
else:
|
||||||
|
print(f" [WARN] 未找到文件: {file_info['name']}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise Exception(f"更新文件parent_id失败: {str(e)}")
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def build_path_to_id_map(structure: Dict, existing_templates: Dict, conn) -> Dict[str, int]:
|
||||||
|
"""
|
||||||
|
构建路径到ID的映射
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,key为路径,value为ID
|
||||||
|
"""
|
||||||
|
path_to_id = {}
|
||||||
|
|
||||||
|
# 处理目录节点(按层级顺序,确保父节点先处理)
|
||||||
|
# 按层级和路径排序
|
||||||
|
sorted_dirs = sorted(structure['directories'], key=lambda x: (x['level'], x['path']))
|
||||||
|
|
||||||
|
for dir_info in sorted_dirs:
|
||||||
|
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, dir_info, parent_id, existing_templates)
|
||||||
|
path_to_id[dir_info['path']] = dir_id
|
||||||
|
|
||||||
|
# 更新existing_templates,以便后续查找(使用完整路径作为key避免冲突)
|
||||||
|
key = f"DIR:{dir_info['path']}"
|
||||||
|
existing_templates[key] = {
|
||||||
|
'id': dir_id,
|
||||||
|
'name': dir_info['name'],
|
||||||
|
'normalized_name': normalize_name(dir_info['name']),
|
||||||
|
'parent_id': parent_id,
|
||||||
|
'file_path': None,
|
||||||
|
'state': 1,
|
||||||
|
'path': dir_info['path']
|
||||||
|
}
|
||||||
|
# 同时用标准化名称存储(用于文件查找父目录)
|
||||||
|
normalized_key = normalize_name(dir_info['name'])
|
||||||
|
if normalized_key not in existing_templates or existing_templates[normalized_key].get('file_path') is not None:
|
||||||
|
# 只有当不存在或存在的是文件时才更新
|
||||||
|
existing_templates[normalized_key] = {
|
||||||
|
'id': dir_id,
|
||||||
|
'name': dir_info['name'],
|
||||||
|
'normalized_name': normalized_key,
|
||||||
|
'parent_id': parent_id,
|
||||||
|
'file_path': None,
|
||||||
|
'state': 1,
|
||||||
|
'path': dir_info['path']
|
||||||
|
}
|
||||||
|
|
||||||
|
return path_to_id
|
||||||
|
|
||||||
|
|
||||||
|
def update_file_hierarchy(structure: Dict, path_to_id: Dict[str, int], existing_templates: Dict, conn):
|
||||||
|
"""
|
||||||
|
更新文件节点的parent_id
|
||||||
|
"""
|
||||||
|
for file_info in structure['files']:
|
||||||
|
parent_id = None
|
||||||
|
if file_info['parent_path']:
|
||||||
|
parent_id = path_to_id.get(file_info['parent_path'])
|
||||||
|
|
||||||
|
update_file_parent(conn, file_info, parent_id, existing_templates)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
print("=" * 80)
|
||||||
|
print("更新模板层级结构")
|
||||||
|
print("=" * 80)
|
||||||
|
print()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 连接数据库
|
||||||
|
print("1. 连接数据库...")
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
print("[OK] 数据库连接成功\n")
|
||||||
|
|
||||||
|
# 扫描目录结构
|
||||||
|
print("2. 扫描目录结构...")
|
||||||
|
structure = scan_directory_structure(TEMPLATE_BASE_DIR)
|
||||||
|
if not structure:
|
||||||
|
print("错误: 未找到任何目录或文件")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 获取现有模板
|
||||||
|
print("\n3. 获取现有模板...")
|
||||||
|
existing_templates = get_existing_templates(conn)
|
||||||
|
print(f"[OK] 找到 {len(existing_templates)} 个现有模板\n")
|
||||||
|
|
||||||
|
# 构建路径到ID的映射(处理目录节点)
|
||||||
|
print("4. 创建/更新目录节点...")
|
||||||
|
print("=" * 80)
|
||||||
|
path_to_id = build_path_to_id_map(structure, existing_templates, conn)
|
||||||
|
print(f"\n[OK] 处理了 {len(path_to_id)} 个目录节点\n")
|
||||||
|
|
||||||
|
# 更新文件节点的parent_id
|
||||||
|
print("5. 更新文件节点的parent_id...")
|
||||||
|
print("=" * 80)
|
||||||
|
update_file_hierarchy(structure, path_to_id, existing_templates, conn)
|
||||||
|
print(f"\n[OK] 处理了 {len(structure['files'])} 个文件节点\n")
|
||||||
|
|
||||||
|
# 打印层级结构
|
||||||
|
print("6. 最终层级结构:")
|
||||||
|
print("=" * 80)
|
||||||
|
print_hierarchy(conn)
|
||||||
|
|
||||||
|
print("\n" + "=" * 80)
|
||||||
|
print("更新完成!")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n[ERROR] 发生错误: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.rollback()
|
||||||
|
finally:
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.close()
|
||||||
|
print("\n数据库连接已关闭")
|
||||||
|
|
||||||
|
|
||||||
|
def print_hierarchy(conn, parent_id=None, level=0, prefix=""):
|
||||||
|
"""打印层级结构"""
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if parent_id is None:
|
||||||
|
sql = """
|
||||||
|
SELECT id, name, parent_id, file_path
|
||||||
|
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, parent_id, file_path
|
||||||
|
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 = prefix + ("└── " if is_last else "├── ")
|
||||||
|
next_prefix = prefix + (" " if is_last else "│ ")
|
||||||
|
|
||||||
|
node_type = "📁" if item['file_path'] is None else "📄"
|
||||||
|
print(f"{current_prefix}{node_type} {item['name']} (ID: {item['id']})")
|
||||||
|
|
||||||
|
# 递归打印子节点
|
||||||
|
print_hierarchy(conn, item['id'], level + 1, next_prefix)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
405
update_template_hierarchy_final.py
Normal file
405
update_template_hierarchy_final.py
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
"""
|
||||||
|
根据 template_finish/ 目录结构更新 f_polic_file_config 表中的层级结构
|
||||||
|
使用file_path作为唯一标识,确保正确建立层级关系
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import pymysql
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
from collections import defaultdict
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# 设置输出编码为UTF-8(Windows兼容)
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
import io
|
||||||
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||||
|
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||||
|
|
||||||
|
# 数据库连接配置
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
CREATED_BY = 655162080928945152
|
||||||
|
UPDATED_BY = 655162080928945152
|
||||||
|
TEMPLATE_BASE_DIR = 'template_finish'
|
||||||
|
|
||||||
|
|
||||||
|
def generate_id():
|
||||||
|
"""生成ID"""
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
timestamp = int(time.time() * 1000)
|
||||||
|
random_part = random.randint(100000, 999999)
|
||||||
|
return timestamp * 1000 + random_part
|
||||||
|
|
||||||
|
|
||||||
|
def scan_directory_structure(base_dir: str) -> Dict:
|
||||||
|
"""
|
||||||
|
扫描目录结构,构建层级关系
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,包含目录和文件的层级信息
|
||||||
|
"""
|
||||||
|
base_path = Path(base_dir)
|
||||||
|
if not base_path.exists():
|
||||||
|
print(f"错误: 目录不存在 - {base_dir}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
structure = {
|
||||||
|
'directories': [], # 目录节点列表
|
||||||
|
'files': [] # 文件节点列表
|
||||||
|
}
|
||||||
|
|
||||||
|
print("=" * 80)
|
||||||
|
print("扫描目录结构...")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
# 遍历所有目录和文件
|
||||||
|
for item in base_path.rglob("*"):
|
||||||
|
relative_path = item.relative_to(base_path)
|
||||||
|
parts = relative_path.parts
|
||||||
|
|
||||||
|
if item.is_dir():
|
||||||
|
# 目录节点
|
||||||
|
level = len(parts) - 1 # 层级(从0开始)
|
||||||
|
dir_name = parts[-1]
|
||||||
|
parent_path = str(Path(*parts[:-1])) if len(parts) > 1 else None
|
||||||
|
|
||||||
|
structure['directories'].append({
|
||||||
|
'name': dir_name,
|
||||||
|
'path': str(relative_path),
|
||||||
|
'level': level,
|
||||||
|
'parent_path': parent_path
|
||||||
|
})
|
||||||
|
|
||||||
|
elif item.is_file() and item.suffix == '.docx' and not item.name.startswith("~$"):
|
||||||
|
# 文件节点
|
||||||
|
level = len(parts) - 1
|
||||||
|
file_name = item.name
|
||||||
|
parent_path = str(Path(*parts[:-1])) if len(parts) > 1 else None
|
||||||
|
|
||||||
|
# 构建MinIO路径
|
||||||
|
now = datetime.now()
|
||||||
|
minio_path = f'/615873064429507639/TEMPLATE/{now.year}/{now.month:02d}/{file_name}'
|
||||||
|
|
||||||
|
structure['files'].append({
|
||||||
|
'name': file_name,
|
||||||
|
'path': str(relative_path),
|
||||||
|
'level': level,
|
||||||
|
'parent_path': parent_path,
|
||||||
|
'file_path': str(item),
|
||||||
|
'minio_path': minio_path
|
||||||
|
})
|
||||||
|
|
||||||
|
# 按层级排序
|
||||||
|
structure['directories'].sort(key=lambda x: (x['level'], x['path']))
|
||||||
|
structure['files'].sort(key=lambda x: (x['level'], x['path']))
|
||||||
|
|
||||||
|
print(f"找到 {len(structure['directories'])} 个目录节点")
|
||||||
|
print(f"找到 {len(structure['files'])} 个文件节点")
|
||||||
|
|
||||||
|
return structure
|
||||||
|
|
||||||
|
|
||||||
|
def get_existing_templates(conn) -> Dict:
|
||||||
|
"""
|
||||||
|
获取数据库中现有的模板记录
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,key为file_path,value为模板信息
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
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作为key(如果存在)
|
||||||
|
result_by_path = {}
|
||||||
|
# 使用name作为key(用于目录节点)
|
||||||
|
result_by_name = {}
|
||||||
|
|
||||||
|
for template in templates:
|
||||||
|
if template['file_path']:
|
||||||
|
result_by_path[template['file_path']] = {
|
||||||
|
'id': template['id'],
|
||||||
|
'name': template['name'],
|
||||||
|
'parent_id': template['parent_id'],
|
||||||
|
'file_path': template['file_path'],
|
||||||
|
'state': template['state']
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# 目录节点
|
||||||
|
name = template['name']
|
||||||
|
if name not in result_by_name:
|
||||||
|
result_by_name[name] = []
|
||||||
|
result_by_name[name].append({
|
||||||
|
'id': template['id'],
|
||||||
|
'name': template['name'],
|
||||||
|
'parent_id': template['parent_id'],
|
||||||
|
'file_path': None,
|
||||||
|
'state': template['state']
|
||||||
|
})
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
return {
|
||||||
|
'by_path': result_by_path,
|
||||||
|
'by_name': result_by_name
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create_or_update_directory(conn, dir_name: str, parent_id: Optional[int], existing_templates: Dict) -> int:
|
||||||
|
"""
|
||||||
|
创建或更新目录节点
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
目录节点的ID
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 查找是否已存在(通过名称精确匹配,且file_path为None)
|
||||||
|
candidates = existing_templates['by_name'].get(dir_name, [])
|
||||||
|
existing = None
|
||||||
|
for candidate in candidates:
|
||||||
|
if candidate.get('file_path') is None: # 目录节点
|
||||||
|
# 如果parent_id匹配,优先选择
|
||||||
|
if candidate['parent_id'] == parent_id:
|
||||||
|
existing = candidate
|
||||||
|
break
|
||||||
|
elif existing is None:
|
||||||
|
existing = candidate
|
||||||
|
|
||||||
|
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, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(insert_sql, (
|
||||||
|
template_id,
|
||||||
|
TENANT_ID,
|
||||||
|
parent_id,
|
||||||
|
dir_name,
|
||||||
|
None, # input_data
|
||||||
|
None, # file_path(目录节点没有文件路径)
|
||||||
|
CREATED_BY,
|
||||||
|
CREATED_BY,
|
||||||
|
1 # state: 1表示启用
|
||||||
|
))
|
||||||
|
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
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise Exception(f"创建或更新目录失败: {str(e)}")
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def update_file_parent(conn, file_info: Dict, parent_id: Optional[int], existing_templates: Dict) -> Optional[int]:
|
||||||
|
"""
|
||||||
|
更新文件节点的parent_id
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_info: 文件信息,包含name、minio_path等
|
||||||
|
parent_id: 父节点ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
文件节点的ID,如果未找到则返回None
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
file_name = file_info['name']
|
||||||
|
minio_path = file_info.get('minio_path')
|
||||||
|
|
||||||
|
# 优先通过file_path(minio_path)匹配(最准确)
|
||||||
|
existing = None
|
||||||
|
if minio_path and minio_path in existing_templates['by_path']:
|
||||||
|
existing = existing_templates['by_path'][minio_path]
|
||||||
|
|
||||||
|
if not existing:
|
||||||
|
print(f" [WARN] 未找到文件: {file_name} (MinIO路径: {minio_path})")
|
||||||
|
return None
|
||||||
|
|
||||||
|
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_name} (ID: {template_id}, parent_id: {parent_id})")
|
||||||
|
else:
|
||||||
|
print(f" [KEEP] 保持文件: {file_name} (ID: {template_id})")
|
||||||
|
return template_id
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise Exception(f"更新文件parent_id失败: {str(e)}")
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
print("=" * 80)
|
||||||
|
print("更新模板层级结构")
|
||||||
|
print("=" * 80)
|
||||||
|
print()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 连接数据库
|
||||||
|
print("1. 连接数据库...")
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
print("[OK] 数据库连接成功\n")
|
||||||
|
|
||||||
|
# 扫描目录结构
|
||||||
|
print("2. 扫描目录结构...")
|
||||||
|
structure = scan_directory_structure(TEMPLATE_BASE_DIR)
|
||||||
|
if not structure:
|
||||||
|
print("错误: 未找到任何目录或文件")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 获取现有模板
|
||||||
|
print("\n3. 获取现有模板...")
|
||||||
|
existing_templates = get_existing_templates(conn)
|
||||||
|
print(f"[OK] 找到 {len(existing_templates['by_path'])} 个文件模板")
|
||||||
|
print(f"[OK] 找到 {sum(len(v) for v in existing_templates['by_name'].values())} 个目录模板\n")
|
||||||
|
|
||||||
|
# 构建路径到ID的映射(处理目录节点)
|
||||||
|
print("4. 创建/更新目录节点...")
|
||||||
|
print("=" * 80)
|
||||||
|
path_to_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'])
|
||||||
|
|
||||||
|
dir_id = create_or_update_directory(conn, dir_info['name'], parent_id, existing_templates)
|
||||||
|
path_to_id[dir_info['path']] = dir_id
|
||||||
|
|
||||||
|
print(f"\n[OK] 处理了 {len(path_to_id)} 个目录节点\n")
|
||||||
|
|
||||||
|
# 更新文件节点的parent_id
|
||||||
|
print("5. 更新文件节点的parent_id...")
|
||||||
|
print("=" * 80)
|
||||||
|
for file_info in structure['files']:
|
||||||
|
parent_id = None
|
||||||
|
if file_info['parent_path']:
|
||||||
|
parent_id = path_to_id.get(file_info['parent_path'])
|
||||||
|
|
||||||
|
update_file_parent(conn, file_info, parent_id, existing_templates)
|
||||||
|
|
||||||
|
print(f"\n[OK] 处理了 {len(structure['files'])} 个文件节点\n")
|
||||||
|
|
||||||
|
# 打印层级结构
|
||||||
|
print("6. 最终层级结构:")
|
||||||
|
print("=" * 80)
|
||||||
|
print_hierarchy(conn)
|
||||||
|
|
||||||
|
print("\n" + "=" * 80)
|
||||||
|
print("更新完成!")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n[ERROR] 发生错误: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.rollback()
|
||||||
|
finally:
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.close()
|
||||||
|
print("\n数据库连接已关闭")
|
||||||
|
|
||||||
|
|
||||||
|
def print_hierarchy(conn, parent_id=None, level=0, prefix=""):
|
||||||
|
"""打印层级结构"""
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if parent_id is None:
|
||||||
|
sql = """
|
||||||
|
SELECT id, name, parent_id, file_path
|
||||||
|
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, parent_id, file_path
|
||||||
|
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 = prefix + ("└── " if is_last else "├── ")
|
||||||
|
next_prefix = prefix + (" " if is_last else "│ ")
|
||||||
|
|
||||||
|
node_type = "📁" if item['file_path'] is None else "📄"
|
||||||
|
print(f"{current_prefix}{node_type} {item['name']} (ID: {item['id']})")
|
||||||
|
|
||||||
|
# 递归打印子节点
|
||||||
|
print_hierarchy(conn, item['id'], level + 1, next_prefix)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
445
update_template_hierarchy_v2.py
Normal file
445
update_template_hierarchy_v2.py
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
"""
|
||||||
|
根据 template_finish/ 目录结构更新 f_polic_file_config 表中的层级结构
|
||||||
|
使用路径作为唯一标识,确保正确建立层级关系
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import pymysql
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
# 设置输出编码为UTF-8(Windows兼容)
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
import io
|
||||||
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||||
|
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||||
|
|
||||||
|
# 数据库连接配置
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
CREATED_BY = 655162080928945152
|
||||||
|
UPDATED_BY = 655162080928945152
|
||||||
|
TEMPLATE_BASE_DIR = 'template_finish'
|
||||||
|
|
||||||
|
|
||||||
|
def generate_id():
|
||||||
|
"""生成ID"""
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
timestamp = int(time.time() * 1000)
|
||||||
|
random_part = random.randint(100000, 999999)
|
||||||
|
return timestamp * 1000 + random_part
|
||||||
|
|
||||||
|
|
||||||
|
def scan_directory_structure(base_dir: str) -> Dict:
|
||||||
|
"""
|
||||||
|
扫描目录结构,构建层级关系
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,包含目录和文件的层级信息
|
||||||
|
"""
|
||||||
|
base_path = Path(base_dir)
|
||||||
|
if not base_path.exists():
|
||||||
|
print(f"错误: 目录不存在 - {base_dir}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
structure = {
|
||||||
|
'directories': [], # 目录节点列表
|
||||||
|
'files': [] # 文件节点列表
|
||||||
|
}
|
||||||
|
|
||||||
|
print("=" * 80)
|
||||||
|
print("扫描目录结构...")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
# 遍历所有目录和文件
|
||||||
|
for item in base_path.rglob("*"):
|
||||||
|
relative_path = item.relative_to(base_path)
|
||||||
|
parts = relative_path.parts
|
||||||
|
|
||||||
|
if item.is_dir():
|
||||||
|
# 目录节点
|
||||||
|
level = len(parts) - 1 # 层级(从0开始)
|
||||||
|
dir_name = parts[-1]
|
||||||
|
parent_path = str(Path(*parts[:-1])) if len(parts) > 1 else None
|
||||||
|
|
||||||
|
structure['directories'].append({
|
||||||
|
'name': dir_name,
|
||||||
|
'path': str(relative_path),
|
||||||
|
'level': level,
|
||||||
|
'parent_path': parent_path
|
||||||
|
})
|
||||||
|
|
||||||
|
elif item.is_file() and item.suffix == '.docx' and not item.name.startswith("~$"):
|
||||||
|
# 文件节点
|
||||||
|
level = len(parts) - 1
|
||||||
|
file_name = item.name
|
||||||
|
parent_path = str(Path(*parts[:-1])) if len(parts) > 1 else None
|
||||||
|
|
||||||
|
structure['files'].append({
|
||||||
|
'name': file_name,
|
||||||
|
'path': str(relative_path),
|
||||||
|
'level': level,
|
||||||
|
'parent_path': parent_path,
|
||||||
|
'file_path': str(item)
|
||||||
|
})
|
||||||
|
|
||||||
|
# 按层级排序
|
||||||
|
structure['directories'].sort(key=lambda x: (x['level'], x['path']))
|
||||||
|
structure['files'].sort(key=lambda x: (x['level'], x['path']))
|
||||||
|
|
||||||
|
print(f"找到 {len(structure['directories'])} 个目录节点")
|
||||||
|
print(f"找到 {len(structure['files'])} 个文件节点")
|
||||||
|
|
||||||
|
return structure
|
||||||
|
|
||||||
|
|
||||||
|
def get_existing_templates(conn) -> Dict:
|
||||||
|
"""
|
||||||
|
获取数据库中现有的模板记录
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
字典,key为file_path(如果存在)或名称,value为模板信息列表
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for template in templates:
|
||||||
|
# 优先使用file_path作为key(更准确)
|
||||||
|
if template['file_path']:
|
||||||
|
key = template['file_path']
|
||||||
|
if key not in result:
|
||||||
|
result[key] = []
|
||||||
|
result[key].append({
|
||||||
|
'id': template['id'],
|
||||||
|
'name': template['name'],
|
||||||
|
'parent_id': template['parent_id'],
|
||||||
|
'file_path': template['file_path'],
|
||||||
|
'state': template['state']
|
||||||
|
})
|
||||||
|
# 同时使用名称作为key(用于目录节点和没有file_path的记录)
|
||||||
|
name_key = template['name']
|
||||||
|
if name_key not in result:
|
||||||
|
result[name_key] = []
|
||||||
|
result[name_key].append({
|
||||||
|
'id': template['id'],
|
||||||
|
'name': template['name'],
|
||||||
|
'parent_id': template['parent_id'],
|
||||||
|
'file_path': template['file_path'],
|
||||||
|
'state': template['state']
|
||||||
|
})
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def create_or_update_directory(conn, dir_name: str, parent_id: Optional[int], existing_templates: Dict) -> int:
|
||||||
|
"""
|
||||||
|
创建或更新目录节点
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
目录节点的ID
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 查找是否已存在(通过名称精确匹配,且file_path为None)
|
||||||
|
candidates = existing_templates.get(dir_name, [])
|
||||||
|
existing = None
|
||||||
|
for candidate in candidates:
|
||||||
|
if candidate.get('file_path') is None: # 目录节点
|
||||||
|
existing = candidate
|
||||||
|
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, %s)
|
||||||
|
"""
|
||||||
|
cursor.execute(insert_sql, (
|
||||||
|
template_id,
|
||||||
|
TENANT_ID,
|
||||||
|
parent_id,
|
||||||
|
dir_name,
|
||||||
|
None, # input_data
|
||||||
|
None, # file_path(目录节点没有文件路径)
|
||||||
|
CREATED_BY,
|
||||||
|
CREATED_BY,
|
||||||
|
1 # state: 1表示启用
|
||||||
|
))
|
||||||
|
conn.commit()
|
||||||
|
print(f" [CREATE] 创建目录: {dir_name} (ID: {template_id}, parent_id: {parent_id})")
|
||||||
|
# 更新existing_templates
|
||||||
|
if dir_name not in existing_templates:
|
||||||
|
existing_templates[dir_name] = []
|
||||||
|
existing_templates[dir_name].append({
|
||||||
|
'id': template_id,
|
||||||
|
'name': dir_name,
|
||||||
|
'parent_id': parent_id,
|
||||||
|
'file_path': None,
|
||||||
|
'state': 1
|
||||||
|
})
|
||||||
|
return template_id
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise Exception(f"创建或更新目录失败: {str(e)}")
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_name(name: str) -> str:
|
||||||
|
"""
|
||||||
|
标准化名称(去掉扩展名、括号内容、数字前缀等)
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
# 去掉扩展名
|
||||||
|
name = Path(name).stem if '.' in name else name
|
||||||
|
# 去掉括号内容
|
||||||
|
name = re.sub(r'[((].*?[))]', '', name)
|
||||||
|
name = name.strip()
|
||||||
|
# 去掉数字前缀和点号
|
||||||
|
name = re.sub(r'^\d+[\.\-]?\s*', '', name)
|
||||||
|
name = name.strip()
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def update_file_parent(conn, file_info: Dict, parent_id: Optional[int], existing_templates: Dict) -> Optional[int]:
|
||||||
|
"""
|
||||||
|
更新文件节点的parent_id
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_info: 文件信息,包含name和file_path
|
||||||
|
parent_id: 父节点ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
文件节点的ID,如果未找到则返回None
|
||||||
|
"""
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
file_name = file_info['name']
|
||||||
|
# 根据文件路径构建预期的MinIO路径
|
||||||
|
# 文件路径格式: template_finish/2-初核模版/1.初核请示/1.请示报告卡(XXX).docx
|
||||||
|
# MinIO路径格式: /615873064429507639/TEMPLATE/2025/12/1.请示报告卡(XXX).docx
|
||||||
|
expected_path = None
|
||||||
|
if 'file_path' in file_info:
|
||||||
|
# 从相对路径构建MinIO路径
|
||||||
|
from datetime import datetime
|
||||||
|
now = datetime.now()
|
||||||
|
relative_path = file_info.get('path', '')
|
||||||
|
file_name_only = Path(file_name).name
|
||||||
|
expected_path = f'/615873064429507639/TEMPLATE/{now.year}/{now.month:02d}/{file_name_only}'
|
||||||
|
|
||||||
|
# 优先通过file_path匹配(最准确)
|
||||||
|
existing = None
|
||||||
|
if expected_path and expected_path in existing_templates:
|
||||||
|
candidates = existing_templates[expected_path]
|
||||||
|
if candidates:
|
||||||
|
existing = candidates[0]
|
||||||
|
|
||||||
|
# 如果没找到,通过标准化名称匹配
|
||||||
|
if not existing:
|
||||||
|
normalized_file_name = normalize_name(file_name)
|
||||||
|
candidates = []
|
||||||
|
for key, templates in existing_templates.items():
|
||||||
|
for template in templates:
|
||||||
|
if template.get('file_path'): # 只匹配有file_path的(文件节点)
|
||||||
|
normalized_template_name = normalize_name(template['name'])
|
||||||
|
if normalized_template_name == normalized_file_name:
|
||||||
|
# 检查file_path是否匹配
|
||||||
|
if expected_path and template['file_path'] == expected_path:
|
||||||
|
existing = template
|
||||||
|
break
|
||||||
|
candidates.append(template)
|
||||||
|
if existing:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not existing and candidates:
|
||||||
|
# 如果有多个候选,选择parent_id匹配的
|
||||||
|
for candidate in candidates:
|
||||||
|
if candidate['parent_id'] == parent_id:
|
||||||
|
existing = candidate
|
||||||
|
break
|
||||||
|
# 如果还是没找到,选择第一个
|
||||||
|
if not existing:
|
||||||
|
existing = candidates[0]
|
||||||
|
|
||||||
|
if not existing:
|
||||||
|
print(f" [WARN] 未找到文件: {file_name} (标准化: {normalize_name(file_name)})")
|
||||||
|
return None
|
||||||
|
|
||||||
|
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_name} -> {existing['name']} (ID: {template_id}, parent_id: {parent_id})")
|
||||||
|
else:
|
||||||
|
print(f" [KEEP] 保持文件: {file_name} -> {existing['name']} (ID: {template_id})")
|
||||||
|
return template_id
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise Exception(f"更新文件parent_id失败: {str(e)}")
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
print("=" * 80)
|
||||||
|
print("更新模板层级结构")
|
||||||
|
print("=" * 80)
|
||||||
|
print()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 连接数据库
|
||||||
|
print("1. 连接数据库...")
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
print("[OK] 数据库连接成功\n")
|
||||||
|
|
||||||
|
# 扫描目录结构
|
||||||
|
print("2. 扫描目录结构...")
|
||||||
|
structure = scan_directory_structure(TEMPLATE_BASE_DIR)
|
||||||
|
if not structure:
|
||||||
|
print("错误: 未找到任何目录或文件")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 获取现有模板
|
||||||
|
print("\n3. 获取现有模板...")
|
||||||
|
existing_templates = get_existing_templates(conn)
|
||||||
|
print(f"[OK] 找到 {len(existing_templates)} 个现有模板\n")
|
||||||
|
|
||||||
|
# 构建路径到ID的映射(处理目录节点)
|
||||||
|
print("4. 创建/更新目录节点...")
|
||||||
|
print("=" * 80)
|
||||||
|
path_to_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'])
|
||||||
|
|
||||||
|
dir_id = create_or_update_directory(conn, dir_info['name'], parent_id, existing_templates)
|
||||||
|
path_to_id[dir_info['path']] = dir_id
|
||||||
|
|
||||||
|
print(f"\n[OK] 处理了 {len(path_to_id)} 个目录节点\n")
|
||||||
|
|
||||||
|
# 更新文件节点的parent_id
|
||||||
|
print("5. 更新文件节点的parent_id...")
|
||||||
|
print("=" * 80)
|
||||||
|
for file_info in structure['files']:
|
||||||
|
parent_id = None
|
||||||
|
if file_info['parent_path']:
|
||||||
|
parent_id = path_to_id.get(file_info['parent_path'])
|
||||||
|
|
||||||
|
update_file_parent(conn, file_info, parent_id, existing_templates)
|
||||||
|
|
||||||
|
print(f"\n[OK] 处理了 {len(structure['files'])} 个文件节点\n")
|
||||||
|
|
||||||
|
# 打印层级结构
|
||||||
|
print("6. 最终层级结构:")
|
||||||
|
print("=" * 80)
|
||||||
|
print_hierarchy(conn)
|
||||||
|
|
||||||
|
print("\n" + "=" * 80)
|
||||||
|
print("更新完成!")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n[ERROR] 发生错误: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.rollback()
|
||||||
|
finally:
|
||||||
|
if 'conn' in locals():
|
||||||
|
conn.close()
|
||||||
|
print("\n数据库连接已关闭")
|
||||||
|
|
||||||
|
|
||||||
|
def print_hierarchy(conn, parent_id=None, level=0, prefix=""):
|
||||||
|
"""打印层级结构"""
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if parent_id is None:
|
||||||
|
sql = """
|
||||||
|
SELECT id, name, parent_id, file_path
|
||||||
|
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, parent_id, file_path
|
||||||
|
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 = prefix + ("└── " if is_last else "├── ")
|
||||||
|
next_prefix = prefix + (" " if is_last else "│ ")
|
||||||
|
|
||||||
|
node_type = "📁" if item['file_path'] is None else "📄"
|
||||||
|
print(f"{current_prefix}{node_type} {item['name']} (ID: {item['id']})")
|
||||||
|
|
||||||
|
# 递归打印子节点
|
||||||
|
print_hierarchy(conn, item['id'], level + 1, next_prefix)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
56
verify_hierarchy_structure.py
Normal file
56
verify_hierarchy_structure.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
"""
|
||||||
|
验证层级结构是否正确
|
||||||
|
"""
|
||||||
|
import pymysql
|
||||||
|
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '152.136.177.240',
|
||||||
|
'port': 5012,
|
||||||
|
'user': 'finyx',
|
||||||
|
'password': '6QsGK6MpePZDE57Z',
|
||||||
|
'database': 'finyx',
|
||||||
|
'charset': 'utf8mb4'
|
||||||
|
}
|
||||||
|
|
||||||
|
TENANT_ID = 615873064429507639
|
||||||
|
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||||
|
|
||||||
|
# 检查"1请示报告卡"的记录
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT id, name, file_path, parent_id
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND name LIKE %s
|
||||||
|
ORDER BY id
|
||||||
|
""", (TENANT_ID, '%请示报告卡%'))
|
||||||
|
|
||||||
|
results = cursor.fetchall()
|
||||||
|
print(f"找到 {len(results)} 条'请示报告卡'相关记录:\n")
|
||||||
|
for r in results:
|
||||||
|
print(f"ID: {r['id']}")
|
||||||
|
print(f"名称: {r['name']}")
|
||||||
|
print(f"文件路径: {r['file_path']}")
|
||||||
|
print(f"父节点ID: {r['parent_id']}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 检查"XXX初核情况报告"的记录
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT id, name, file_path, parent_id
|
||||||
|
FROM f_polic_file_config
|
||||||
|
WHERE tenant_id = %s AND name LIKE %s
|
||||||
|
ORDER BY id
|
||||||
|
""", (TENANT_ID, '%初核情况报告%'))
|
||||||
|
|
||||||
|
results = cursor.fetchall()
|
||||||
|
print(f"\n找到 {len(results)} 条'初核情况报告'相关记录:\n")
|
||||||
|
for r in results:
|
||||||
|
print(f"ID: {r['id']}")
|
||||||
|
print(f"名称: {r['name']}")
|
||||||
|
print(f"文件路径: {r['file_path']}")
|
||||||
|
print(f"父节点ID: {r['parent_id']}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
113
修复模板名称总结.md
Normal file
113
修复模板名称总结.md
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
# 修复模板名称总结
|
||||||
|
|
||||||
|
## 任务完成情况
|
||||||
|
|
||||||
|
✅ **已完成所有任务**
|
||||||
|
|
||||||
|
根据 template_finish/ 目录下的模板文件,检查并修复了数据库中 f_polic_file_config 表的 name 字段,确保与模板文档名称(去掉扩展名)完全一致。
|
||||||
|
|
||||||
|
## 修复结果
|
||||||
|
|
||||||
|
### 统计信息
|
||||||
|
- **总文件数**: 21
|
||||||
|
- **匹配成功**: 21
|
||||||
|
- **更新数量**: 16
|
||||||
|
- **未找到**: 0
|
||||||
|
|
||||||
|
### 更新的模板名称
|
||||||
|
|
||||||
|
1. **1.请示报告卡(XXX).docx**
|
||||||
|
- 旧名称: `1请示报告卡`
|
||||||
|
- 新名称: `1.请示报告卡(XXX)`
|
||||||
|
|
||||||
|
2. **2.初步核实审批表(XXX).docx**
|
||||||
|
- 旧名称: `初步核实审批表`
|
||||||
|
- 新名称: `2.初步核实审批表(XXX)`
|
||||||
|
|
||||||
|
3. **3.附件初核方案(XXX).docx**
|
||||||
|
- 旧名称: `附件初核方案`
|
||||||
|
- 新名称: `3.附件初核方案(XXX)`
|
||||||
|
|
||||||
|
4. **1.请示报告卡(初核谈话).docx**
|
||||||
|
- 旧名称: `1请示报告卡`
|
||||||
|
- 新名称: `1.请示报告卡(初核谈话)`
|
||||||
|
|
||||||
|
5. **2谈话审批表.docx**
|
||||||
|
- 旧名称: `谈话审批表`
|
||||||
|
- 新名称: `2谈话审批表`
|
||||||
|
|
||||||
|
6. **3.谈话前安全风险评估表.docx**
|
||||||
|
- 旧名称: `谈话前安全风险评估表`
|
||||||
|
- 新名称: `3.谈话前安全风险评估表`
|
||||||
|
|
||||||
|
7. **4.谈话方案.docx**
|
||||||
|
- 旧名称: `谈话方案`
|
||||||
|
- 新名称: `4.谈话方案`
|
||||||
|
|
||||||
|
8. **5.谈话后安全风险评估表.docx**
|
||||||
|
- 旧名称: `谈话后安全风险评估表`
|
||||||
|
- 新名称: `5.谈话后安全风险评估表`
|
||||||
|
|
||||||
|
9. **1.谈话笔录.docx**
|
||||||
|
- 旧名称: `谈话笔录`
|
||||||
|
- 新名称: `1.谈话笔录`
|
||||||
|
|
||||||
|
10. **2.谈话询问对象情况摸底调查30问.docx**
|
||||||
|
- 旧名称: `谈话询问对象情况摸底调查30问`
|
||||||
|
- 新名称: `2.谈话询问对象情况摸底调查30问`
|
||||||
|
|
||||||
|
11. **3.被谈话人权利义务告知书.docx**
|
||||||
|
- 旧名称: `被谈话人权利义务告知书`
|
||||||
|
- 新名称: `3.被谈话人权利义务告知书`
|
||||||
|
|
||||||
|
12. **4.点对点交接单.docx**
|
||||||
|
- 旧名称: `点对点交接单`
|
||||||
|
- 新名称: `4.点对点交接单`
|
||||||
|
|
||||||
|
13. **5.陪送交接单(新).docx**
|
||||||
|
- 旧名称: `陪送交接单`
|
||||||
|
- 新名称: `5.陪送交接单(新)`
|
||||||
|
|
||||||
|
14. **6.2保密承诺书(谈话对象使用-中共党员用).docx**
|
||||||
|
- 旧名称: `2保密承诺书`
|
||||||
|
- 新名称: `6.2保密承诺书(谈话对象使用-中共党员用)`
|
||||||
|
|
||||||
|
15. **7.办案人员-办案安全保密承诺书.docx**
|
||||||
|
- 旧名称: `办案人员-办案安全保密承诺书`
|
||||||
|
- 新名称: `7.办案人员-办案安全保密承诺书`
|
||||||
|
|
||||||
|
16. **8.XXX初核情况报告.docx**
|
||||||
|
- 旧名称: `XXX初核情况报告`
|
||||||
|
- 新名称: `8.XXX初核情况报告`
|
||||||
|
|
||||||
|
### 名称已正确的模板(无需更新)
|
||||||
|
|
||||||
|
1. 谈话通知书第一联.docx
|
||||||
|
2. 谈话通知书第三联.docx
|
||||||
|
3. 谈话通知书第二联.docx
|
||||||
|
4. 6.1保密承诺书(谈话对象使用-非中共党员用).docx
|
||||||
|
5. 8-1请示报告卡(初核报告结论) .docx
|
||||||
|
|
||||||
|
## 修复规则
|
||||||
|
|
||||||
|
- **文件名**: `1.请示报告卡(XXX).docx`
|
||||||
|
- **数据库name字段**: `1.请示报告卡(XXX)`(去掉扩展名,保留所有其他内容)
|
||||||
|
|
||||||
|
确保:
|
||||||
|
- 保留数字前缀(如 "1."、"2.")
|
||||||
|
- 保留括号内容(如 "(XXX)"、"(初核谈话)")
|
||||||
|
- 保留所有标点符号和特殊字符
|
||||||
|
- 只去掉文件扩展名(.docx)
|
||||||
|
|
||||||
|
## 脚本文件
|
||||||
|
|
||||||
|
**fix_template_names.py** - 修复模板名称的脚本
|
||||||
|
- 扫描 template_finish 目录下的所有 .docx 文件
|
||||||
|
- 获取每个文件的完整名称(去掉扩展名)
|
||||||
|
- 检查数据库中对应的记录
|
||||||
|
- 如果 name 字段不匹配,更新为正确的名称
|
||||||
|
|
||||||
|
## 完成时间
|
||||||
|
|
||||||
|
2025年12月11日
|
||||||
|
|
||||||
75
处理保密承诺书模板总结.md
Normal file
75
处理保密承诺书模板总结.md
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# 处理保密承诺书(非中共党员用)模板总结
|
||||||
|
|
||||||
|
## 任务完成情况
|
||||||
|
|
||||||
|
✅ **已完成所有任务**
|
||||||
|
|
||||||
|
1. ✅ 解析文档中的占位符
|
||||||
|
2. ✅ 上传文档到MinIO
|
||||||
|
3. ✅ 更新数据库记录
|
||||||
|
|
||||||
|
## 处理结果
|
||||||
|
|
||||||
|
### 文件信息
|
||||||
|
- **文件路径**: `template_finish/2-初核模版/2.谈话审批/走读式谈话流程/6.1保密承诺书(谈话对象使用-非中共党员用).docx`
|
||||||
|
- **模板名称**: `6.1保密承诺书(谈话对象使用-非中共党员用)`
|
||||||
|
- **父节点ID**: `1765273962716807` (走读式谈话流程)
|
||||||
|
|
||||||
|
### 占位符提取结果
|
||||||
|
找到 **5个占位符**,全部成功匹配到数据库字段:
|
||||||
|
|
||||||
|
1. `{{ target_contact }}` - 被核查人联系方式
|
||||||
|
2. `{{ target_id_number }}` - 被核查人身份证号
|
||||||
|
3. `{{ target_name }}` - 被核查人姓名
|
||||||
|
4. `{{ target_organization_and_position }}` - 被核查人单位及职务
|
||||||
|
5. `{{ target_political_status }}` - 被核查人政治面貌
|
||||||
|
|
||||||
|
### MinIO上传结果
|
||||||
|
- **MinIO路径**: `/615873064429507639/TEMPLATE/2025/12/6.1保密承诺书(谈话对象使用-非中共党员用).docx`
|
||||||
|
- **上传状态**: ✅ 成功
|
||||||
|
|
||||||
|
### 数据库更新结果
|
||||||
|
- **模板ID**: `1765434655750341`
|
||||||
|
- **创建状态**: ✅ 新创建
|
||||||
|
- **字段关联**: ✅ 5个字段全部关联成功
|
||||||
|
|
||||||
|
## 数据库记录详情
|
||||||
|
|
||||||
|
### f_polic_file_config 表
|
||||||
|
- **id**: 1765434655750341
|
||||||
|
- **tenant_id**: 615873064429507639
|
||||||
|
- **parent_id**: 1765273962716807 (走读式谈话流程)
|
||||||
|
- **name**: 6.1保密承诺书(谈话对象使用-非中共党员用)
|
||||||
|
- **file_path**: /615873064429507639/TEMPLATE/2025/12/6.1保密承诺书(谈话对象使用-非中共党员用).docx
|
||||||
|
- **state**: 1 (启用)
|
||||||
|
|
||||||
|
### f_polic_file_field 表
|
||||||
|
已创建5条关联记录,关联的字段ID:
|
||||||
|
- target_contact
|
||||||
|
- target_id_number
|
||||||
|
- target_name
|
||||||
|
- target_organization_and_position
|
||||||
|
- target_political_status
|
||||||
|
|
||||||
|
## 层级结构
|
||||||
|
|
||||||
|
该模板现在位于:
|
||||||
|
```
|
||||||
|
📁 2-初核模版
|
||||||
|
└── 📁 2.谈话审批
|
||||||
|
└── 📁 走读式谈话流程
|
||||||
|
└── 📄 6.1保密承诺书(谈话对象使用-非中共党员用) (ID: 1765434655750341)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 脚本文件
|
||||||
|
|
||||||
|
**process_confidentiality_commitment_non_party.py** - 处理该模板的专用脚本
|
||||||
|
- 提取占位符
|
||||||
|
- 上传到MinIO
|
||||||
|
- 创建/更新数据库记录
|
||||||
|
- 建立字段关联关系
|
||||||
|
|
||||||
|
## 完成时间
|
||||||
|
|
||||||
|
2025年12月11日
|
||||||
|
|
||||||
124
更新模板层级结构总结.md
Normal file
124
更新模板层级结构总结.md
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
# 更新模板层级结构总结
|
||||||
|
|
||||||
|
## 任务完成情况
|
||||||
|
|
||||||
|
✅ **已完成主要任务**
|
||||||
|
|
||||||
|
1. ✅ 根据 template_finish/ 目录结构更新 f_polic_file_config 表的层级结构
|
||||||
|
2. ✅ 创建目录节点(一级、二级、三级)
|
||||||
|
3. ✅ 更新文件的 parent_id,建立正确的层级关系
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
根据 `template_finish/2-初核模版/` 目录,层级结构如下:
|
||||||
|
|
||||||
|
### 一级节点
|
||||||
|
- **2-初核模版** (ID: 1765273961277310)
|
||||||
|
|
||||||
|
### 二级节点
|
||||||
|
- **1.初核请示** (ID: 1765431558933731) - parent: 2-初核模版
|
||||||
|
- **2.谈话审批** (ID: 1765431558825578) - parent: 2-初核模版
|
||||||
|
- **3.初核结论** (ID: 1765431559135346) - parent: 2-初核模版
|
||||||
|
|
||||||
|
### 三级节点
|
||||||
|
- **谈话通知书** (ID: 1765273962774249) - parent: 2.谈话审批
|
||||||
|
- **走读式谈话审批** (ID: 1765273962700431) - parent: 2.谈话审批
|
||||||
|
- **走读式谈话流程** (ID: 1765273962716807) - parent: 2.谈话审批
|
||||||
|
|
||||||
|
## 文件节点分布
|
||||||
|
|
||||||
|
### 1.初核请示 下的文件
|
||||||
|
- 初步核实审批表 (ID: 1765425918287774)
|
||||||
|
- 附件初核方案 (ID: 1765425918483987)
|
||||||
|
- 1请示报告卡 (ID: 1765425930118581) - 注意:此文件在多个位置出现
|
||||||
|
|
||||||
|
### 2.谈话审批/谈话通知书 下的文件
|
||||||
|
- 谈话通知书第一联 (ID: 1765273963625524)
|
||||||
|
- 谈话通知书第二联 (ID: 1765273963825806)
|
||||||
|
- 谈话通知书第三联 (ID: 1765273963038891)
|
||||||
|
|
||||||
|
### 2.谈话审批/走读式谈话审批 下的文件
|
||||||
|
- 1请示报告卡 (ID: 1765425930118581) - 注意:与"1.初核请示"下的文件重复
|
||||||
|
- 谈话审批表 (ID: 1765425918217504)
|
||||||
|
- 谈话前安全风险评估表 (ID: 1765425918902422)
|
||||||
|
- 谈话方案 (ID: 1765425918745496)
|
||||||
|
- 谈话后安全风险评估表 (ID: 1765425919242549)
|
||||||
|
|
||||||
|
### 2.谈话审批/走读式谈话流程 下的文件
|
||||||
|
- 谈话笔录 (ID: 1765425918749247)
|
||||||
|
- 谈话询问对象情况摸底调查30问 (ID: 1765425918921697)
|
||||||
|
- 被谈话人权利义务告知书 (ID: 1765425930059797)
|
||||||
|
- 点对点交接单 (ID: 1765425919512780)
|
||||||
|
- 陪送交接单 (ID: 1765425919389484)
|
||||||
|
- 办案人员-办案安全保密承诺书 (ID: 1765425919629084)
|
||||||
|
- 8.XXX初核情况报告 (ID: 1765273963158289) - 注意:此文件位置可能不正确
|
||||||
|
|
||||||
|
### 3.初核结论 下的文件
|
||||||
|
- 8-1请示报告卡(初核报告结论) (ID: 1765273962631542)
|
||||||
|
- XXX初核情况报告 (ID: 1765425930460962)
|
||||||
|
|
||||||
|
## 已知问题
|
||||||
|
|
||||||
|
1. **缺失文件**: 以下文件在数据库中未找到:
|
||||||
|
- 6.1保密承诺书(谈话对象使用-非中共党员用).docx
|
||||||
|
|
||||||
|
这个文件可能在数据库中不存在,或者名称不同。数据库中只有"2保密承诺书"(ID: 1765425919729046),可能是"6.2保密承诺书(谈话对象使用-中共党员用)"的简化名称。
|
||||||
|
|
||||||
|
2. **重复文件已解决**: "1请示报告卡"现在正确地在两个位置:
|
||||||
|
- 1.初核请示 下 (ID: 1765425930118581) - 对应文件"1.请示报告卡(XXX).docx"
|
||||||
|
- 走读式谈话审批 下 (ID: 1765432134276990) - 对应文件"1.请示报告卡(初核谈话).docx"
|
||||||
|
|
||||||
|
这两个是不同的文件,现在已正确区分。
|
||||||
|
|
||||||
|
## 执行结果
|
||||||
|
|
||||||
|
- **创建的目录节点**: 3个(1.初核请示、2.谈话审批、3.初核结论)
|
||||||
|
- **更新的目录节点**: 4个(更新了parent_id)
|
||||||
|
- **更新的文件节点**: 20个(更新了parent_id)
|
||||||
|
- **未找到的文件**: 1个(6.1保密承诺书(谈话对象使用-非中共党员用).docx)
|
||||||
|
|
||||||
|
## 最终层级结构
|
||||||
|
|
||||||
|
```
|
||||||
|
📁 2-初核模版
|
||||||
|
├── 📁 1.初核请示
|
||||||
|
│ ├── 📄 1请示报告卡 (ID: 1765425930118581)
|
||||||
|
│ ├── 📄 初步核实审批表 (ID: 1765425918287774)
|
||||||
|
│ └── 📄 附件初核方案 (ID: 1765425918483987)
|
||||||
|
├── 📁 2.谈话审批
|
||||||
|
│ ├── 📁 谈话通知书
|
||||||
|
│ │ ├── 📄 谈话通知书第一联 (ID: 1765273963625524)
|
||||||
|
│ │ ├── 📄 谈话通知书第二联 (ID: 1765273963825806)
|
||||||
|
│ │ └── 📄 谈话通知书第三联 (ID: 1765273963038891)
|
||||||
|
│ ├── 📁 走读式谈话审批
|
||||||
|
│ │ ├── 📄 1请示报告卡 (ID: 1765432134276990)
|
||||||
|
│ │ ├── 📄 谈话审批表 (ID: 1765425918217504)
|
||||||
|
│ │ ├── 📄 谈话前安全风险评估表 (ID: 1765425918902422)
|
||||||
|
│ │ ├── 📄 谈话方案 (ID: 1765425918745496)
|
||||||
|
│ │ └── 📄 谈话后安全风险评估表 (ID: 1765425919242549)
|
||||||
|
│ └── 📁 走读式谈话流程
|
||||||
|
│ ├── 📄 2保密承诺书 (ID: 1765425919729046)
|
||||||
|
│ ├── 📄 办案人员-办案安全保密承诺书 (ID: 1765425919629084)
|
||||||
|
│ ├── 📄 点对点交接单 (ID: 1765425919512780)
|
||||||
|
│ ├── 📄 被谈话人权利义务告知书 (ID: 1765425930059797)
|
||||||
|
│ ├── 📄 谈话笔录 (ID: 1765425918749247)
|
||||||
|
│ ├── 📄 谈话询问对象情况摸底调查30问 (ID: 1765425918921697)
|
||||||
|
│ └── 📄 陪送交接单 (ID: 1765425919389484)
|
||||||
|
└── 📁 3.初核结论
|
||||||
|
├── 📄 8-1请示报告卡(初核报告结论) (ID: 1765273962631542)
|
||||||
|
└── 📄 XXX初核情况报告 (ID: 1765425930460962)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 脚本文件
|
||||||
|
|
||||||
|
**update_template_hierarchy_final.py** - 更新层级结构的最终版本脚本
|
||||||
|
- 扫描 template_finish 目录结构
|
||||||
|
- 使用 file_path(MinIO路径)作为唯一标识匹配文件
|
||||||
|
- 创建/更新目录节点
|
||||||
|
- 更新文件的 parent_id
|
||||||
|
- 正确处理同名但不同路径的文件(如"1请示报告卡"在不同目录下)
|
||||||
|
|
||||||
|
## 完成时间
|
||||||
|
|
||||||
|
2025年12月11日
|
||||||
|
|
||||||
139
清理重复模板总结.md
Normal file
139
清理重复模板总结.md
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
# 清理重复和无效模板总结
|
||||||
|
|
||||||
|
## 任务完成情况
|
||||||
|
|
||||||
|
✅ **已完成所有任务**
|
||||||
|
|
||||||
|
1. ✅ 删除 f_polic_file_config 表中的重复数据
|
||||||
|
2. ✅ 删除无效的模板数据
|
||||||
|
3. ✅ 确保文档模板结构和 template_finish/ 文件夹对应
|
||||||
|
4. ✅ 同步更新测试页面
|
||||||
|
|
||||||
|
## 执行结果
|
||||||
|
|
||||||
|
### 1. 清理前状态
|
||||||
|
- **数据库中的模板总数**: 53个
|
||||||
|
- **唯一模板名称**: 24个
|
||||||
|
- **重复模板**: 4组(共29个重复记录)
|
||||||
|
|
||||||
|
### 2. 清理操作
|
||||||
|
- **删除重复模板**: 29个
|
||||||
|
- **删除关联关系**: 136条
|
||||||
|
- **标记无效模板**: 6个
|
||||||
|
|
||||||
|
### 3. 清理后状态
|
||||||
|
- **数据库中的模板总数**: 24个
|
||||||
|
- **唯一模板名称**: 24个
|
||||||
|
- **启用的模板**: 18个(有filePath的)
|
||||||
|
- **禁用的模板**: 6个(无效模板)
|
||||||
|
|
||||||
|
## 清理的重复模板详情
|
||||||
|
|
||||||
|
### 1. 请示报告卡
|
||||||
|
- **保留**: ID 1765425930118581 (1请示报告卡)
|
||||||
|
- **删除**: ID 1765425917608094 (请示报告卡)
|
||||||
|
|
||||||
|
### 2. 保密承诺书
|
||||||
|
- **保留**: ID 1765425919729046 (2保密承诺书)
|
||||||
|
- **删除**: ID 1765425919731077 (1保密承诺书)
|
||||||
|
|
||||||
|
### 3. 谈话审批表
|
||||||
|
- **保留**: ID 1765425918217504 (谈话审批表)
|
||||||
|
- **删除**:
|
||||||
|
- ID 1765273961492987 (2谈话审批表)
|
||||||
|
- ID 1765273964441714 (2谈话审批表)
|
||||||
|
|
||||||
|
### 4. 其他重复模板(标准化后名称相同)
|
||||||
|
- **删除**: 26个重复模板,包括:
|
||||||
|
- 多个版本的"初步核实审批表"
|
||||||
|
- 多个版本的"请示报告卡"
|
||||||
|
- 多个版本的"谈话通知书"
|
||||||
|
- 多个版本的"谈话笔录"
|
||||||
|
- 多个版本的"保密承诺书"
|
||||||
|
- 等等
|
||||||
|
|
||||||
|
## 标记的无效模板
|
||||||
|
|
||||||
|
以下模板不在 template_finish 文件夹中,已标记为无效(state=0):
|
||||||
|
|
||||||
|
1. 2-初核模版 (ID: 1765273961277310)
|
||||||
|
2. 走读式谈话审批 (ID: 1765273962700431)
|
||||||
|
3. 走读式谈话流程 (ID: 1765273962716807)
|
||||||
|
4. 谈话通知书 (ID: 1765273962774249)
|
||||||
|
5. 8.XXX初核情况报告 (ID: 1765273963158289)
|
||||||
|
6. 2保密承诺书 (ID: 1765425919729046)
|
||||||
|
|
||||||
|
## 最终启用的模板列表(18个)
|
||||||
|
|
||||||
|
1. ✅ 1请示报告卡 (ID: 1765425930118581)
|
||||||
|
2. ✅ 8-1请示报告卡(初核报告结论) (ID: 1765273962631542)
|
||||||
|
3. ✅ XXX初核情况报告 (ID: 1765425930460962)
|
||||||
|
4. ✅ 初步核实审批表 (ID: 1765425918287774)
|
||||||
|
5. ✅ 办案人员-办案安全保密承诺书 (ID: 1765425919629084)
|
||||||
|
6. ✅ 点对点交接单 (ID: 1765425919512780)
|
||||||
|
7. ✅ 被谈话人权利义务告知书 (ID: 1765425930059797)
|
||||||
|
8. ✅ 谈话前安全风险评估表 (ID: 1765425918902422)
|
||||||
|
9. ✅ 谈话后安全风险评估表 (ID: 1765425919242549)
|
||||||
|
10. ✅ 谈话审批表 (ID: 1765425918217504)
|
||||||
|
11. ✅ 谈话方案 (ID: 1765425918745496)
|
||||||
|
12. ✅ 谈话笔录 (ID: 1765425918749247)
|
||||||
|
13. ✅ 谈话询问对象情况摸底调查30问 (ID: 1765425918921697)
|
||||||
|
14. ✅ 谈话通知书第一联 (ID: 1765273963625524)
|
||||||
|
15. ✅ 谈话通知书第三联 (ID: 1765273963038891)
|
||||||
|
16. ✅ 谈话通知书第二联 (ID: 1765273963825806)
|
||||||
|
17. ✅ 附件初核方案 (ID: 1765425918483987)
|
||||||
|
18. ✅ 陪送交接单 (ID: 1765425919389484)
|
||||||
|
|
||||||
|
## 测试页面更新
|
||||||
|
|
||||||
|
### 更新内容
|
||||||
|
1. ✅ 移除了硬编码的默认fileId(因为旧的ID已被删除)
|
||||||
|
2. ✅ 改进了文件列表加载逻辑
|
||||||
|
3. ✅ 自动加载所有可用的模板文件
|
||||||
|
|
||||||
|
### 功能说明
|
||||||
|
- **自动加载**: 页面加载时自动从 `/api/file-configs` 接口获取所有可用的模板
|
||||||
|
- **手动加载**: 点击"加载全部可用模板"按钮可以重新加载模板列表
|
||||||
|
- **过滤**: 只显示有filePath的模板(确保模板文件已上传到MinIO)
|
||||||
|
|
||||||
|
## 数据库表更新情况
|
||||||
|
|
||||||
|
### f_polic_file_config 表
|
||||||
|
- **删除**: 29条重复记录
|
||||||
|
- **更新**: 6条记录标记为无效(state=0)
|
||||||
|
- **最终状态**: 24条记录(18个启用,6个禁用)
|
||||||
|
|
||||||
|
### f_polic_file_field 表
|
||||||
|
- **删除**: 136条关联关系(随重复模板一起删除)
|
||||||
|
|
||||||
|
## 脚本文件
|
||||||
|
|
||||||
|
### 主要脚本
|
||||||
|
1. **cleanup_duplicate_templates.py** - 清理脚本
|
||||||
|
- 扫描 template_finish 文件夹
|
||||||
|
- 识别重复模板
|
||||||
|
- 删除重复记录
|
||||||
|
- 标记无效模板
|
||||||
|
|
||||||
|
2. **validate_and_update_templates.py** - 校验和更新脚本
|
||||||
|
- 重新校验模板和字段关联
|
||||||
|
- 上传模板到MinIO
|
||||||
|
- 更新数据库配置
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **重复判断标准**: 使用标准化后的模板名称进行判断(去掉括号、数字前缀等)
|
||||||
|
2. **保留策略**: 保留最新的、启用的模板记录
|
||||||
|
3. **无效模板**: 不在 template_finish 文件夹中的模板会被标记为无效,但不会删除(保留历史记录)
|
||||||
|
4. **关联关系**: 删除模板时会自动删除相关的字段关联关系
|
||||||
|
|
||||||
|
## 后续建议
|
||||||
|
|
||||||
|
1. **定期清理**: 建议定期运行 `cleanup_duplicate_templates.py` 脚本,确保没有重复数据
|
||||||
|
2. **新增模板**: 新增模板时,确保模板文件放在 `template_finish` 文件夹中,然后运行 `validate_and_update_templates.py`
|
||||||
|
3. **测试验证**: 每次清理后,建议测试文档生成功能,确保所有模板都能正常工作
|
||||||
|
|
||||||
|
## 完成时间
|
||||||
|
|
||||||
|
2025年12月11日
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user