diff --git a/app.py b/app.py index 7aab919..ad10173 100644 --- a/app.py +++ b/app.py @@ -835,6 +835,180 @@ def get_document_by_task(): return error_response(3001, f"文档生成失败: {str(e)}") +@app.route('/template-field-manager') +def template_field_manager(): + """返回模板字段关联管理页面""" + return send_from_directory('static', 'template_field_manager.html') + + +@app.route('/api/template-field-relations', methods=['GET']) +def get_template_field_relations(): + """ + 获取所有模板和字段的关联关系 + 用于模板字段关联管理页面 + """ + try: + conn = document_service.get_connection() + cursor = conn.cursor(pymysql.cursors.DictCursor) + + try: + # 获取所有启用的模板 + cursor.execute(""" + SELECT id, name, template_code + FROM f_polic_file_config + WHERE tenant_id = %s AND state = 1 + ORDER BY name + """, (document_service.tenant_id,)) + templates = cursor.fetchall() + + # 获取所有启用的输入字段 + cursor.execute(""" + SELECT id, name, filed_code, field_type + FROM f_polic_field + WHERE tenant_id = %s AND field_type = 1 AND state = 1 + ORDER BY name + """, (document_service.tenant_id,)) + input_fields = cursor.fetchall() + + # 获取所有启用的输出字段 + cursor.execute(""" + SELECT id, name, filed_code, field_type + FROM f_polic_field + WHERE tenant_id = %s AND field_type = 2 AND state = 1 + ORDER BY name + """, (document_service.tenant_id,)) + output_fields = cursor.fetchall() + + # 获取现有的关联关系 + cursor.execute(""" + SELECT file_id, filed_id + FROM f_polic_file_field + WHERE tenant_id = %s AND state = 1 + """, (document_service.tenant_id,)) + relations = cursor.fetchall() + + # 构建关联关系映射 (file_id -> list of filed_id) + # 注意:JSON不支持set,所以转换为list + relation_map = {} + for rel in relations: + file_id = rel['file_id'] + filed_id = rel['filed_id'] + if file_id not in relation_map: + relation_map[file_id] = [] + relation_map[file_id].append(filed_id) + + return success_response({ + 'templates': templates, + 'input_fields': input_fields, + 'output_fields': output_fields, + 'relations': relation_map + }) + + finally: + cursor.close() + conn.close() + + except Exception as e: + return error_response(500, f"获取关联关系失败: {str(e)}") + + +@app.route('/api/template-field-relations', methods=['POST']) +def save_template_field_relations(): + """ + 保存模板和字段的关联关系 + 请求体格式: { + "template_id": 123, + "input_field_ids": [1, 2, 3], + "output_field_ids": [4, 5, 6] + } + """ + try: + data = request.get_json() + + if not data: + return error_response(400, "请求参数不能为空") + + template_id = data.get('template_id') + input_field_ids = data.get('input_field_ids', []) + output_field_ids = data.get('output_field_ids', []) + + if not template_id: + return error_response(400, "template_id参数不能为空") + + conn = document_service.get_connection() + cursor = conn.cursor() + + try: + # 验证模板是否存在 + cursor.execute(""" + SELECT id FROM f_polic_file_config + WHERE id = %s AND tenant_id = %s AND state = 1 + """, (template_id, document_service.tenant_id)) + if not cursor.fetchone(): + return error_response(400, f"模板ID {template_id} 不存在或未启用") + + # 合并所有字段ID + all_field_ids = set(input_field_ids + output_field_ids) + + # 验证字段是否存在 + if all_field_ids: + placeholders = ','.join(['%s'] * len(all_field_ids)) + cursor.execute(f""" + SELECT id FROM f_polic_field + WHERE id IN ({placeholders}) AND tenant_id = %s AND state = 1 + """, list(all_field_ids) + [document_service.tenant_id]) + existing_field_ids = {row[0] for row in cursor.fetchall()} + invalid_field_ids = all_field_ids - existing_field_ids + if invalid_field_ids: + return error_response(400, f"字段ID {list(invalid_field_ids)} 不存在或未启用") + + # 删除该模板的所有现有关联关系 + cursor.execute(""" + DELETE FROM f_polic_file_field + WHERE file_id = %s AND tenant_id = %s + """, (template_id, document_service.tenant_id)) + + # 插入新的关联关系 + current_time = datetime.now() + created_by = 655162080928945152 # 默认创建者ID + + if all_field_ids: + insert_sql = """ + INSERT INTO f_polic_file_field + (tenant_id, file_id, filed_id, created_time, created_by, updated_time, updated_by, state) + VALUES (%s, %s, %s, %s, %s, %s, %s, 1) + """ + for field_id in all_field_ids: + cursor.execute(insert_sql, ( + document_service.tenant_id, + template_id, + field_id, + current_time, + created_by, + current_time, + created_by + )) + + conn.commit() + + return success_response({ + 'template_id': template_id, + 'input_field_count': len(input_field_ids), + 'output_field_count': len(output_field_ids), + 'total_field_count': len(all_field_ids) + }, "保存成功") + + except Exception as e: + conn.rollback() + raise e + finally: + cursor.close() + conn.close() + + except Exception as e: + return error_response(500, f"保存关联关系失败: {str(e)}") + + if __name__ == '__main__': # 确保static目录存在 os.makedirs('static', exist_ok=True) @@ -844,6 +1018,7 @@ if __name__ == '__main__': print(f"服务启动在 http://localhost:{port}") print(f"测试页面: http://localhost:{port}/") + print(f"模板字段管理页面: http://localhost:{port}/template-field-manager") print(f"Swagger API文档: http://localhost:{port}/api-docs") app.run(host='0.0.0.0', port=port, debug=debug) diff --git a/check_file_field_relations_comprehensive.py b/check_file_field_relations_comprehensive.py new file mode 100644 index 0000000..78d4c75 --- /dev/null +++ b/check_file_field_relations_comprehensive.py @@ -0,0 +1,496 @@ +""" +全面检查 f_polic_file_field 表的关联关系 +重点检查输入字段(field_type=1)和输出字段(field_type=2)与模板的关联情况 +""" +import pymysql +import os +import sys +from typing import Dict, List, Tuple +from collections import defaultdict + +# 设置输出编码为UTF-8 +if sys.platform == 'win32': + import io + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') + +# 数据库连接配置 +DB_CONFIG = { + 'host': os.getenv('DB_HOST', '152.136.177.240'), + 'port': int(os.getenv('DB_PORT', 5012)), + 'user': os.getenv('DB_USER', 'finyx'), + 'password': os.getenv('DB_PASSWORD', '6QsGK6MpePZDE57Z'), + 'database': os.getenv('DB_NAME', 'finyx'), + 'charset': 'utf8mb4' +} + +TENANT_ID = 615873064429507639 + + +def check_all_templates_field_relations(conn) -> Dict: + """检查所有模板的字段关联情况""" + cursor = conn.cursor(pymysql.cursors.DictCursor) + + print("\n" + "="*80) + print("1. 检查所有模板的字段关联情况") + print("="*80) + + # 获取所有启用的模板 + cursor.execute(""" + SELECT id, name, template_code + FROM f_polic_file_config + WHERE tenant_id = %s AND state = 1 + ORDER BY name + """, (TENANT_ID,)) + templates = cursor.fetchall() + + # 获取每个模板关联的字段(按类型分组) + cursor.execute(""" + SELECT + fc.id AS template_id, + fc.name AS template_name, + f.id AS field_id, + f.name AS field_name, + f.filed_code AS field_code, + f.field_type + FROM f_polic_file_config fc + INNER JOIN f_polic_file_field fff ON fc.id = fff.file_id AND fc.tenant_id = fff.tenant_id + INNER JOIN f_polic_field f ON fff.filed_id = f.id AND fff.tenant_id = f.tenant_id + WHERE fc.tenant_id = %s + AND fc.state = 1 + AND fff.state = 1 + AND f.state = 1 + ORDER BY fc.name, f.field_type, f.name + """, (TENANT_ID,)) + relations = cursor.fetchall() + + # 按模板分组统计 + template_stats = {} + for template in templates: + template_stats[template['id']] = { + 'template_id': template['id'], + 'template_name': template['name'], + 'template_code': template.get('template_code'), + 'input_fields': [], + 'output_fields': [], + 'input_count': 0, + 'output_count': 0 + } + + # 填充字段信息 + for rel in relations: + template_id = rel['template_id'] + if template_id in template_stats: + field_info = { + 'field_id': rel['field_id'], + 'field_name': rel['field_name'], + 'field_code': rel['field_code'], + 'field_type': rel['field_type'] + } + if rel['field_type'] == 1: + template_stats[template_id]['input_fields'].append(field_info) + template_stats[template_id]['input_count'] += 1 + elif rel['field_type'] == 2: + template_stats[template_id]['output_fields'].append(field_info) + template_stats[template_id]['output_count'] += 1 + + # 打印统计信息 + print(f"\n共找到 {len(templates)} 个启用的模板\n") + + templates_without_input = [] + templates_without_output = [] + templates_without_any = [] + + for template_id, stats in template_stats.items(): + status = [] + if stats['input_count'] == 0: + status.append("缺少输入字段") + templates_without_input.append(stats) + if stats['output_count'] == 0: + status.append("缺少输出字段") + templates_without_output.append(stats) + if stats['input_count'] == 0 and stats['output_count'] == 0: + templates_without_any.append(stats) + + status_str = " | ".join(status) if status else "[OK] 正常" + print(f" {stats['template_name']} (ID: {stats['template_id']})") + print(f" 输入字段: {stats['input_count']} 个 | 输出字段: {stats['output_count']} 个 | {status_str}") + + return { + 'template_stats': template_stats, + 'templates_without_input': templates_without_input, + 'templates_without_output': templates_without_output, + 'templates_without_any': templates_without_any + } + + +def check_input_field_relations_detail(conn) -> Dict: + """详细检查输入字段的关联情况""" + cursor = conn.cursor(pymysql.cursors.DictCursor) + + print("\n" + "="*80) + print("2. 详细检查输入字段的关联情况") + print("="*80) + + # 获取所有启用的输入字段 + cursor.execute(""" + SELECT id, name, filed_code + FROM f_polic_field + WHERE tenant_id = %s AND field_type = 1 AND state = 1 + ORDER BY name + """, (TENANT_ID,)) + input_fields = cursor.fetchall() + + # 获取每个输入字段关联的模板 + cursor.execute(""" + SELECT + f.id AS field_id, + f.name AS field_name, + f.filed_code AS field_code, + fc.id AS template_id, + fc.name AS template_name + FROM f_polic_field f + INNER JOIN f_polic_file_field fff ON f.id = fff.filed_id AND f.tenant_id = fff.tenant_id + INNER JOIN f_polic_file_config fc ON fff.file_id = fc.id AND fff.tenant_id = fc.tenant_id + WHERE f.tenant_id = %s + AND f.field_type = 1 + AND f.state = 1 + AND fff.state = 1 + AND fc.state = 1 + ORDER BY f.name, fc.name + """, (TENANT_ID,)) + input_field_relations = cursor.fetchall() + + # 按字段分组 + field_template_map = defaultdict(list) + for rel in input_field_relations: + field_template_map[rel['field_id']].append({ + 'template_id': rel['template_id'], + 'template_name': rel['template_name'] + }) + + print(f"\n共找到 {len(input_fields)} 个启用的输入字段\n") + + fields_without_relations = [] + fields_with_relations = [] + + for field in input_fields: + field_id = field['id'] + templates = field_template_map.get(field_id, []) + + if not templates: + fields_without_relations.append(field) + print(f" [WARN] {field['name']} ({field['filed_code']}) - 未关联任何模板") + else: + fields_with_relations.append({ + 'field': field, + 'templates': templates + }) + print(f" [OK] {field['name']} ({field['filed_code']}) - 关联了 {len(templates)} 个模板:") + for template in templates: + print(f" - {template['template_name']} (ID: {template['template_id']})") + + return { + 'input_fields': input_fields, + 'fields_without_relations': fields_without_relations, + 'fields_with_relations': fields_with_relations + } + + +def check_output_field_relations_detail(conn) -> Dict: + """详细检查输出字段的关联情况""" + cursor = conn.cursor(pymysql.cursors.DictCursor) + + print("\n" + "="*80) + print("3. 详细检查输出字段的关联情况") + print("="*80) + + # 获取所有启用的输出字段 + cursor.execute(""" + SELECT id, name, filed_code + FROM f_polic_field + WHERE tenant_id = %s AND field_type = 2 AND state = 1 + ORDER BY name + """, (TENANT_ID,)) + output_fields = cursor.fetchall() + + # 获取每个输出字段关联的模板 + cursor.execute(""" + SELECT + f.id AS field_id, + f.name AS field_name, + f.filed_code AS field_code, + fc.id AS template_id, + fc.name AS template_name + FROM f_polic_field f + INNER JOIN f_polic_file_field fff ON f.id = fff.filed_id AND f.tenant_id = fff.tenant_id + INNER JOIN f_polic_file_config fc ON fff.file_id = fc.id AND fff.tenant_id = fc.tenant_id + WHERE f.tenant_id = %s + AND f.field_type = 2 + AND f.state = 1 + AND fff.state = 1 + AND fc.state = 1 + ORDER BY f.name, fc.name + """, (TENANT_ID,)) + output_field_relations = cursor.fetchall() + + # 按字段分组 + field_template_map = defaultdict(list) + for rel in output_field_relations: + field_template_map[rel['field_id']].append({ + 'template_id': rel['template_id'], + 'template_name': rel['template_name'] + }) + + print(f"\n共找到 {len(output_fields)} 个启用的输出字段\n") + + fields_without_relations = [] + fields_with_relations = [] + + for field in output_fields: + field_id = field['id'] + templates = field_template_map.get(field_id, []) + + if not templates: + fields_without_relations.append(field) + print(f" [WARN] {field['name']} ({field['filed_code']}) - 未关联任何模板") + else: + fields_with_relations.append({ + 'field': field, + 'templates': templates + }) + if len(templates) <= 5: + print(f" [OK] {field['name']} ({field['filed_code']}) - 关联了 {len(templates)} 个模板:") + for template in templates: + print(f" - {template['template_name']} (ID: {template['template_id']})") + else: + print(f" [OK] {field['name']} ({field['filed_code']}) - 关联了 {len(templates)} 个模板") + for template in templates[:3]: + print(f" - {template['template_name']} (ID: {template['template_id']})") + print(f" ... 还有 {len(templates) - 3} 个模板") + + return { + 'output_fields': output_fields, + 'fields_without_relations': fields_without_relations, + 'fields_with_relations': fields_with_relations + } + + +def check_invalid_relations(conn) -> Dict: + """检查无效的关联关系""" + cursor = conn.cursor(pymysql.cursors.DictCursor) + + print("\n" + "="*80) + print("4. 检查无效的关联关系") + print("="*80) + + # 检查关联到不存在的 file_id + cursor.execute(""" + SELECT fff.id, fff.file_id, fff.filed_id, fff.tenant_id + FROM f_polic_file_field fff + LEFT JOIN f_polic_file_config fc ON fff.file_id = fc.id AND fff.tenant_id = fc.tenant_id + WHERE fff.tenant_id = %s AND fc.id IS NULL + """, (TENANT_ID,)) + invalid_file_relations = cursor.fetchall() + + # 检查关联到不存在的 filed_id + cursor.execute(""" + SELECT fff.id, fff.file_id, fff.filed_id, fff.tenant_id + FROM f_polic_file_field fff + LEFT JOIN f_polic_field f ON fff.filed_id = f.id AND fff.tenant_id = f.tenant_id + WHERE fff.tenant_id = %s AND f.id IS NULL + """, (TENANT_ID,)) + invalid_field_relations = cursor.fetchall() + + print(f"\n关联到不存在的 file_id: {len(invalid_file_relations)} 条") + if invalid_file_relations: + print(" 详情:") + for rel in invalid_file_relations[:10]: + print(f" - 关联ID: {rel['id']}, file_id: {rel['file_id']}, filed_id: {rel['filed_id']}") + if len(invalid_file_relations) > 10: + print(f" ... 还有 {len(invalid_file_relations) - 10} 条") + else: + print(" [OK] 没有无效的 file_id 关联") + + print(f"\n关联到不存在的 filed_id: {len(invalid_field_relations)} 条") + if invalid_field_relations: + print(" 详情:") + for rel in invalid_field_relations[:10]: + print(f" - 关联ID: {rel['id']}, file_id: {rel['file_id']}, filed_id: {rel['filed_id']}") + if len(invalid_field_relations) > 10: + print(f" ... 还有 {len(invalid_field_relations) - 10} 条") + else: + print(" [OK] 没有无效的 filed_id 关联") + + return { + 'invalid_file_relations': invalid_file_relations, + 'invalid_field_relations': invalid_field_relations + } + + +def get_summary_statistics(conn) -> Dict: + """获取汇总统计信息""" + cursor = conn.cursor(pymysql.cursors.DictCursor) + + print("\n" + "="*80) + print("5. 汇总统计信息") + print("="*80) + + # 总关联数 + cursor.execute(""" + SELECT COUNT(*) as total + FROM f_polic_file_field + WHERE tenant_id = %s AND state = 1 + """, (TENANT_ID,)) + total_relations = cursor.fetchone()['total'] + + # 输入字段关联数 + cursor.execute(""" + SELECT COUNT(*) as total + FROM f_polic_file_field fff + INNER JOIN f_polic_field f ON fff.filed_id = f.id AND fff.tenant_id = f.tenant_id + WHERE fff.tenant_id = %s + AND fff.state = 1 + AND f.state = 1 + AND f.field_type = 1 + """, (TENANT_ID,)) + input_relations = cursor.fetchone()['total'] + + # 输出字段关联数 + cursor.execute(""" + SELECT COUNT(*) as total + FROM f_polic_file_field fff + INNER JOIN f_polic_field f ON fff.filed_id = f.id AND fff.tenant_id = f.tenant_id + WHERE fff.tenant_id = %s + AND fff.state = 1 + AND f.state = 1 + AND f.field_type = 2 + """, (TENANT_ID,)) + output_relations = cursor.fetchone()['total'] + + # 关联的模板数 + cursor.execute(""" + SELECT COUNT(DISTINCT file_id) as total + FROM f_polic_file_field + WHERE tenant_id = %s AND state = 1 + """, (TENANT_ID,)) + related_templates = cursor.fetchone()['total'] + + # 关联的输入字段数 + cursor.execute(""" + SELECT COUNT(DISTINCT fff.filed_id) as total + FROM f_polic_file_field fff + INNER JOIN f_polic_field f ON fff.filed_id = f.id AND fff.tenant_id = f.tenant_id + WHERE fff.tenant_id = %s + AND fff.state = 1 + AND f.state = 1 + AND f.field_type = 1 + """, (TENANT_ID,)) + related_input_fields = cursor.fetchone()['total'] + + # 关联的输出字段数 + cursor.execute(""" + SELECT COUNT(DISTINCT fff.filed_id) as total + FROM f_polic_file_field fff + INNER JOIN f_polic_field f ON fff.filed_id = f.id AND fff.tenant_id = f.tenant_id + WHERE fff.tenant_id = %s + AND fff.state = 1 + AND f.state = 1 + AND f.field_type = 2 + """, (TENANT_ID,)) + related_output_fields = cursor.fetchone()['total'] + + print(f"\n总关联数: {total_relations}") + print(f" 输入字段关联数: {input_relations}") + print(f" 输出字段关联数: {output_relations}") + print(f"\n关联的模板数: {related_templates}") + print(f"关联的输入字段数: {related_input_fields}") + print(f"关联的输出字段数: {related_output_fields}") + + return { + 'total_relations': total_relations, + 'input_relations': input_relations, + 'output_relations': output_relations, + 'related_templates': related_templates, + 'related_input_fields': related_input_fields, + 'related_output_fields': related_output_fields + } + + +def main(): + """主函数""" + print("="*80) + print("全面检查 f_polic_file_field 表的关联关系") + print("="*80) + + try: + conn = pymysql.connect(**DB_CONFIG) + print("[OK] 数据库连接成功\n") + except Exception as e: + print(f"[ERROR] 数据库连接失败: {e}") + return + + try: + # 1. 检查所有模板的字段关联情况 + template_result = check_all_templates_field_relations(conn) + + # 2. 详细检查输入字段的关联情况 + input_result = check_input_field_relations_detail(conn) + + # 3. 详细检查输出字段的关联情况 + output_result = check_output_field_relations_detail(conn) + + # 4. 检查无效的关联关系 + invalid_result = check_invalid_relations(conn) + + # 5. 获取汇总统计信息 + stats = get_summary_statistics(conn) + + # 总结 + print("\n" + "="*80) + print("检查总结") + print("="*80) + + issues = [] + + if len(template_result['templates_without_input']) > 0: + issues.append(f"[WARN] {len(template_result['templates_without_input'])} 个模板缺少输入字段关联") + + if len(template_result['templates_without_output']) > 0: + issues.append(f"[WARN] {len(template_result['templates_without_output'])} 个模板缺少输出字段关联") + + if len(template_result['templates_without_any']) > 0: + issues.append(f"[WARN] {len(template_result['templates_without_any'])} 个模板没有任何字段关联") + + if len(input_result['fields_without_relations']) > 0: + issues.append(f"[WARN] {len(input_result['fields_without_relations'])} 个输入字段未关联任何模板") + + if len(output_result['fields_without_relations']) > 0: + issues.append(f"[WARN] {len(output_result['fields_without_relations'])} 个输出字段未关联任何模板") + + if len(invalid_result['invalid_file_relations']) > 0: + issues.append(f"[ERROR] {len(invalid_result['invalid_file_relations'])} 条无效的 file_id 关联") + + if len(invalid_result['invalid_field_relations']) > 0: + issues.append(f"[ERROR] {len(invalid_result['invalid_field_relations'])} 条无效的 filed_id 关联") + + if issues: + print("\n发现以下问题:\n") + for issue in issues: + print(f" {issue}") + else: + print("\n[OK] 未发现明显问题") + + print("\n" + "="*80) + + except Exception as e: + print(f"\n[ERROR] 检查过程中发生错误: {e}") + import traceback + traceback.print_exc() + finally: + conn.close() + print("\n数据库连接已关闭") + + +if __name__ == '__main__': + main() + diff --git a/link_all_templates_to_input_fields.py b/link_all_templates_to_input_fields.py new file mode 100644 index 0000000..8795d1b --- /dev/null +++ b/link_all_templates_to_input_fields.py @@ -0,0 +1,234 @@ +""" +将所有模板与两个输入字段关联 +- 线索信息 (clue_info) +- 被核查人员工作基本情况线索 (target_basic_info_clue) +""" +import pymysql +import os +import sys +import time +import random +from datetime import datetime + +# 设置输出编码为UTF-8 +if sys.platform == 'win32': + import io + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') + +# 数据库连接配置 +DB_CONFIG = { + 'host': os.getenv('DB_HOST', '152.136.177.240'), + 'port': int(os.getenv('DB_PORT', 5012)), + 'user': os.getenv('DB_USER', 'finyx'), + 'password': os.getenv('DB_PASSWORD', '6QsGK6MpePZDE57Z'), + 'database': os.getenv('DB_NAME', 'finyx'), + 'charset': 'utf8mb4' +} + +TENANT_ID = 615873064429507639 +CREATED_BY = 655162080928945152 +UPDATED_BY = 655162080928945152 + + +def generate_id(): + """生成ID""" + timestamp = int(time.time() * 1000) + random_part = random.randint(100000, 999999) + return timestamp * 1000 + random_part + + +def get_input_field_ids(conn): + """获取两个输入字段的ID""" + cursor = conn.cursor(pymysql.cursors.DictCursor) + + field_codes = ['clue_info', 'target_basic_info_clue'] + cursor.execute(""" + SELECT id, name, filed_code + FROM f_polic_field + WHERE tenant_id = %s + AND filed_code IN (%s, %s) + AND field_type = 1 + AND state = 1 + """, (TENANT_ID, field_codes[0], field_codes[1])) + + fields = cursor.fetchall() + field_map = {field['filed_code']: field for field in fields} + + result = {} + for code in field_codes: + if code in field_map: + result[code] = field_map[code] + else: + print(f"[WARN] 未找到字段: {code}") + + return result + + +def get_all_templates(conn): + """获取所有启用的模板""" + cursor = conn.cursor(pymysql.cursors.DictCursor) + + cursor.execute(""" + SELECT id, name + FROM f_polic_file_config + WHERE tenant_id = %s AND state = 1 + ORDER BY name + """, (TENANT_ID,)) + + return cursor.fetchall() + + +def get_existing_relations(conn, template_id, field_ids): + """获取模板与字段的现有关联关系""" + cursor = conn.cursor() + + if not field_ids: + return set() + + placeholders = ','.join(['%s'] * len(field_ids)) + cursor.execute(f""" + SELECT filed_id + FROM f_polic_file_field + WHERE tenant_id = %s + AND file_id = %s + AND filed_id IN ({placeholders}) + AND state = 1 + """, [TENANT_ID, template_id] + list(field_ids)) + + return {row[0] for row in cursor.fetchall()} + + +def create_relation(conn, template_id, field_id): + """创建模板与字段的关联关系""" + cursor = conn.cursor() + + current_time = datetime.now() + + # 检查是否已存在 + cursor.execute(""" + SELECT id FROM f_polic_file_field + WHERE tenant_id = %s + AND file_id = %s + AND filed_id = %s + """, (TENANT_ID, template_id, field_id)) + + if cursor.fetchone(): + return False # 已存在,不需要创建 + + # 创建新关联 + relation_id = generate_id() + cursor.execute(""" + INSERT INTO f_polic_file_field + (id, tenant_id, file_id, filed_id, created_time, created_by, updated_time, updated_by, state) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, 1) + """, ( + relation_id, + TENANT_ID, + template_id, + field_id, + current_time, + CREATED_BY, + current_time, + UPDATED_BY + )) + + return True # 创建成功 + + +def main(): + """主函数""" + print("="*80) + print("将所有模板与输入字段关联") + print("="*80) + + try: + conn = pymysql.connect(**DB_CONFIG) + print("[OK] 数据库连接成功\n") + except Exception as e: + print(f"[ERROR] 数据库连接失败: {e}") + return + + try: + # 1. 获取输入字段ID + print("1. 获取输入字段ID...") + input_fields = get_input_field_ids(conn) + + if len(input_fields) != 2: + print(f"[ERROR] 未找到所有输入字段,只找到: {list(input_fields.keys())}") + return + + field_ids = [field['id'] for field in input_fields.values()] + print(f" 找到字段:") + for code, field in input_fields.items(): + print(f" - {field['name']} ({code}): ID={field['id']}") + print() + + # 2. 获取所有模板 + print("2. 获取所有启用的模板...") + templates = get_all_templates(conn) + print(f" 找到 {len(templates)} 个模板\n") + + # 3. 为每个模板创建关联关系 + print("3. 创建关联关系...") + created_count = 0 + existing_count = 0 + error_count = 0 + + for template in templates: + template_id = template['id'] + template_name = template['name'] + + # 获取现有关联 + existing_relations = get_existing_relations(conn, template_id, field_ids) + + # 为每个字段创建关联(如果不存在) + for field_code, field_info in input_fields.items(): + field_id = field_info['id'] + + if field_id in existing_relations: + existing_count += 1 + continue + + try: + if create_relation(conn, template_id, field_id): + created_count += 1 + print(f" [OK] {template_name} <- {field_info['name']} ({field_code})") + else: + existing_count += 1 + except Exception as e: + error_count += 1 + print(f" [ERROR] {template_name} <- {field_info['name']}: {e}") + + # 提交事务 + conn.commit() + + # 4. 统计结果 + print("\n" + "="*80) + print("执行结果") + print("="*80) + print(f"模板总数: {len(templates)}") + print(f"字段总数: {len(input_fields)}") + print(f"预期关联数: {len(templates) * len(input_fields)}") + print(f"新创建关联: {created_count}") + print(f"已存在关联: {existing_count}") + print(f"错误数量: {error_count}") + print(f"实际关联数: {created_count + existing_count}") + + if error_count == 0: + print("\n[OK] 所有关联关系已成功创建或已存在") + else: + print(f"\n[WARN] 有 {error_count} 个关联关系创建失败") + + except Exception as e: + conn.rollback() + print(f"\n[ERROR] 执行过程中发生错误: {e}") + import traceback + traceback.print_exc() + finally: + conn.close() + print("\n数据库连接已关闭") + + +if __name__ == '__main__': + main() + diff --git a/services/__pycache__/document_service.cpython-312.pyc b/services/__pycache__/document_service.cpython-312.pyc index f875437..b77a15a 100644 Binary files a/services/__pycache__/document_service.cpython-312.pyc and b/services/__pycache__/document_service.cpython-312.pyc differ diff --git a/services/document_service.py b/services/document_service.py index 0f4111c..2234573 100644 --- a/services/document_service.py +++ b/services/document_service.py @@ -154,16 +154,40 @@ class DocumentService: # 扫描表格中的占位符 for table in doc.tables: - for row in table.rows: - for cell in row.cells: - if hasattr(cell, 'paragraphs'): - for paragraph in cell.paragraphs: - text = paragraph.text - matches = placeholder_pattern.findall(text) - for match in matches: - field_code = match.strip() - if field_code: - all_placeholders_in_template.add(field_code) + try: + if not table.rows: + continue + for row in table.rows: + try: + # 安全地访问 row.cells,避免 docx 库在处理异常表格结构时的 bug + if not hasattr(row, 'cells'): + continue + # 使用 try-except 包裹,防止 IndexError + try: + cells = row.cells + except (IndexError, AttributeError) as e: + print(f"[WARN] 无法访问表格行的单元格,跳过该行: {str(e)}") + continue + + for cell in cells: + try: + if hasattr(cell, 'paragraphs'): + for paragraph in cell.paragraphs: + text = paragraph.text + matches = placeholder_pattern.findall(text) + for match in matches: + field_code = match.strip() + if field_code: + all_placeholders_in_template.add(field_code) + except Exception as e: + print(f"[WARN] 处理表格单元格时出错,跳过: {str(e)}") + continue + except Exception as e: + print(f"[WARN] 处理表格行时出错,跳过: {str(e)}") + continue + except Exception as e: + print(f"[WARN] 处理表格时出错,跳过该表格: {str(e)}") + continue print(f"[DEBUG] 模板中发现 {len(all_placeholders_in_template)} 个不同的占位符: {sorted(all_placeholders_in_template)}") @@ -379,16 +403,40 @@ class DocumentService: # 检查表格中的占位符 for table in doc.tables: - for row in table.rows: - for cell in row.cells: - if hasattr(cell, 'paragraphs'): - for paragraph in cell.paragraphs: - text = paragraph.text - matches = placeholder_pattern.findall(text) - for match in matches: - field_code = match.strip() - if field_code: - remaining_placeholders.add(field_code) + try: + if not table.rows: + continue + for row in table.rows: + try: + # 安全地访问 row.cells,避免 docx 库在处理异常表格结构时的 bug + if not hasattr(row, 'cells'): + continue + # 使用 try-except 包裹,防止 IndexError + try: + cells = row.cells + except (IndexError, AttributeError) as e: + print(f"[WARN] 无法访问表格行的单元格,跳过该行: {str(e)}") + continue + + for cell in cells: + try: + if hasattr(cell, 'paragraphs'): + for paragraph in cell.paragraphs: + text = paragraph.text + matches = placeholder_pattern.findall(text) + for match in matches: + field_code = match.strip() + if field_code: + remaining_placeholders.add(field_code) + except Exception as e: + print(f"[WARN] 处理表格单元格时出错,跳过: {str(e)}") + continue + except Exception as e: + print(f"[WARN] 处理表格行时出错,跳过: {str(e)}") + continue + except Exception as e: + print(f"[WARN] 处理表格时出错,跳过该表格: {str(e)}") + continue # 输出统计信息 print(f"[DEBUG] 占位符替换统计:") diff --git a/static/template_field_manager.html b/static/template_field_manager.html new file mode 100644 index 0000000..7184248 --- /dev/null +++ b/static/template_field_manager.html @@ -0,0 +1,569 @@ + + +
+ + +维护模板与输入字段、输出字段的关联关系
+ + + +