diff --git a/app.py b/app.py index 575add9..95dec91 100644 --- a/app.py +++ b/app.py @@ -21,6 +21,54 @@ from utils.response import success_response, error_response # 加载环境变量 load_dotenv() + +def clean_query_result(data): + """ + 清理查询结果,将 bytes 类型转换为字符串 + 用于处理数据库查询结果中的 BLOB 等字段 + 支持处理:bytes, datetime, Decimal, 以及其他不可序列化的类型 + """ + if isinstance(data, bytes): + # 如果是单个字节(如 TINYINT(1) 的 state 字段),转换为整数 + if len(data) == 1: + return int.from_bytes(data, byteorder='big') + # 如果是多个字节(如字符串),解码为 UTF-8 + try: + return data.decode('utf-8') + except UnicodeDecodeError: + return data.decode('utf-8', errors='ignore') + elif isinstance(data, dict): + return {key: clean_query_result(value) for key, value in data.items()} + elif isinstance(data, list): + return [clean_query_result(item) for item in data] + elif isinstance(data, datetime): + return data.isoformat() + elif isinstance(data, (int, float, str, bool, type(None))): + # 保持原始类型,但确保数字类型不会被意外转换 + return data + elif hasattr(data, '__int__'): + # 处理 Decimal 等数值类型,转换为 int 或 float + try: + if isinstance(data, float) or (hasattr(data, 'as_tuple') and data.as_tuple()[2] < 0): + return float(data) + else: + return int(data) + except: + return str(data) + else: + # 对于其他类型(如 Decimal, date, time 等),尝试转换为字符串或 JSON 兼容类型 + try: + # 尝试使用 JSON 默认处理 + import json + json.dumps(data, default=str) # 测试是否可以序列化 + return data + except (TypeError, ValueError): + # 如果无法序列化,转换为字符串 + try: + return str(data) + except: + return None + app = Flask(__name__) CORS(app) # 允许跨域请求 @@ -857,20 +905,27 @@ def get_tenant_ids(): """ 获取数据库中所有已存在的 tenant_id 用于模板字段关联管理页面选择租户 + 从三个表中查询所有不同的 tenant_id(字段表、模板表、关联表) """ try: conn = document_service.get_connection() cursor = conn.cursor(pymysql.cursors.DictCursor) try: - # 从 f_polic_file_config 表中获取所有不同的 tenant_id + # 从三个表中获取所有不同的 tenant_id(合并去重) cursor.execute(""" SELECT DISTINCT tenant_id - FROM f_polic_file_config - WHERE tenant_id IS NOT NULL + FROM ( + SELECT tenant_id FROM f_polic_field WHERE tenant_id IS NOT NULL + UNION + SELECT tenant_id FROM f_polic_file_config WHERE tenant_id IS NOT NULL + UNION + SELECT tenant_id FROM f_polic_file_field WHERE tenant_id IS NOT NULL + ) AS all_tenants ORDER BY tenant_id """) - tenant_ids = [row['tenant_id'] for row in cursor.fetchall()] + # 将 tenant_id 转换为字符串,避免 JavaScript 大整数精度问题 + tenant_ids = [str(row['tenant_id']) for row in cursor.fetchall()] return success_response({'tenant_ids': tenant_ids}) @@ -911,43 +966,81 @@ def get_template_field_relations(): WHERE tenant_id = %s AND state = 1 ORDER BY name """, (tenant_id,)) - templates = cursor.fetchall() + templates = cursor.fetchall() or [] + templates = [clean_query_result(t) for t in templates] # 获取指定 tenant_id 下所有启用的输入字段 + # 注意:这里查询的是 state=1 的字段,但为了字段管理页面能显示所有字段的状态,应该查询所有字段 cursor.execute(""" - SELECT id, name, filed_code, field_type + SELECT id, name, filed_code, field_type, state FROM f_polic_field - WHERE tenant_id = %s AND field_type = 1 AND state = 1 + WHERE tenant_id = %s AND field_type = 1 ORDER BY name """, (tenant_id,)) - input_fields = cursor.fetchall() + input_fields = cursor.fetchall() or [] + input_fields = [clean_query_result(f) for f in input_fields] + # 确保 state 字段是整数类型(虽然这里查询的是 state=1,但为了统一处理) + for field in input_fields: + if 'state' in field: + try: + field['state'] = int(field['state']) + except (ValueError, TypeError): + field['state'] = 1 # 获取指定 tenant_id 下所有启用的输出字段 + # 注意:这里查询的是 state=1 的字段,但为了字段管理页面能显示所有字段的状态,应该查询所有字段 cursor.execute(""" - SELECT id, name, filed_code, field_type + SELECT id, name, filed_code, field_type, state FROM f_polic_field - WHERE tenant_id = %s AND field_type = 2 AND state = 1 + WHERE tenant_id = %s AND field_type = 2 ORDER BY name """, (tenant_id,)) - output_fields = cursor.fetchall() + output_fields = cursor.fetchall() or [] + output_fields = [clean_query_result(f) for f in output_fields] + # 确保 state 字段是整数类型 + for field in output_fields: + if 'state' in field: + try: + field['state'] = int(field['state']) + except (ValueError, TypeError): + field['state'] = 1 # 获取指定 tenant_id 下现有的关联关系 + # 关联关系:f_polic_file_field.file_id -> f_polic_file_config.id + # f_polic_file_field.filed_id -> f_polic_field.id + # 注意:只查询关联关系表中 state=1 的记录,不检查模板的 state + # 因为模板可能被禁用,但关联关系仍然有效 cursor.execute(""" SELECT fff.file_id, fff.filed_id FROM f_polic_file_field fff - INNER JOIN f_polic_file_config fc ON fff.file_id = fc.id WHERE fff.tenant_id = %s AND fff.state = 1 """, (tenant_id,)) - relations = cursor.fetchall() + relations = cursor.fetchall() or [] + relations = [clean_query_result(r) for r in relations] # 构建关联关系映射 (file_id -> list of filed_id) + # 注意:JSON 序列化时,字典的整数 key 会变成字符串 + # 所以这里使用字符串 key,前端需要处理类型转换 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) + # 确保 ID 是整数类型 + try: + file_id = int(file_id) + filed_id = int(filed_id) + except (ValueError, TypeError): + continue # 跳过无效的关联关系 + + # 使用字符串 key,因为 JSON 序列化会将数字 key 转为字符串 + file_id_str = str(file_id) + if file_id_str not in relation_map: + relation_map[file_id_str] = [] + relation_map[file_id_str].append(filed_id) + + # 确保 relation_map 的 key 是整数类型(JSON 序列化时 key 会变成字符串) + # 但为了前端能正确匹配,我们保持 key 为整数类型 + # JSON 会自动将数字 key 转换为字符串,所以前端需要处理这种情况 return success_response({ 'tenant_id': tenant_id, @@ -1072,10 +1165,10 @@ def save_template_field_relations(): # 字段管理 API -@app.route('/api/fields', methods=['GET']) -def get_fields(): +@app.route('/api/field-management/fields', methods=['GET']) +def get_field_management_fields(): """ - 获取字段列表 + 获取字段列表(用于字段管理页面) 查询参数: tenant_id (必填), field_type (可选: 1=输入字段, 2=输出字段) """ try: @@ -1114,6 +1207,16 @@ def get_fields(): """, (tenant_id,)) fields = cursor.fetchall() + # 清理查询结果,将 bytes 类型转换为字符串 + fields = [clean_query_result(field) for field in fields] if fields else [] + # 确保 state 字段是整数类型(数据库可能返回 Decimal 或其他类型) + for field in fields: + if 'state' in field: + try: + field['state'] = int(field['state']) + except (ValueError, TypeError): + field['state'] = 1 # 默认启用 + # 即使没有数据也返回空数组,而不是错误 return success_response({'fields': fields}) finally: @@ -1124,7 +1227,7 @@ def get_fields(): return error_response(500, f"获取字段列表失败: {str(e)}") -@app.route('/api/fields', methods=['POST']) +@app.route('/api/field-management/fields', methods=['POST']) def create_field(): """ 创建新字段 @@ -1196,7 +1299,7 @@ def create_field(): return error_response(500, f"创建字段失败: {str(e)}") -@app.route('/api/fields/', methods=['PUT']) +@app.route('/api/field-management/fields/', methods=['PUT']) def update_field(field_id): """ 更新字段 @@ -1301,7 +1404,7 @@ def update_field(field_id): return error_response(500, f"更新字段失败: {str(e)}") -@app.route('/api/fields/', methods=['DELETE']) +@app.route('/api/field-management/fields/', methods=['DELETE']) def delete_field(field_id): """ 删除字段(软删除,将 state 设置为 0) @@ -1395,15 +1498,22 @@ def backup_database(): else: cursor.execute(f"SELECT * FROM {table}") - backup_data[table] = cursor.fetchall() + rows = cursor.fetchall() + # 清理查询结果,将 bytes 类型转换为字符串 + backup_data[table] = [clean_query_result(row) for row in rows] if rows else [] # 创建临时文件保存备份数据 + # 确保所有数据都已清理,可以 JSON 序列化 temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False, encoding='utf-8') - json.dump({ - 'backup_time': datetime.now().isoformat(), - 'tenant_id': tenant_id, - 'tables': backup_data - }, temp_file, ensure_ascii=False, indent=2, default=str) + try: + json.dump({ + 'backup_time': datetime.now().isoformat(), + 'tenant_id': tenant_id, + 'tables': backup_data + }, temp_file, ensure_ascii=False, indent=2, default=str) + except (TypeError, ValueError) as e: + temp_file.close() + return error_response(500, f"备份数据序列化失败: {str(e)}") temp_file.close() return send_file( diff --git a/check_database_tenant_data.py b/check_database_tenant_data.py new file mode 100644 index 0000000..81742ae --- /dev/null +++ b/check_database_tenant_data.py @@ -0,0 +1,140 @@ +""" +检查数据库中的实际数据,查看有哪些 tenant_id 以及对应的数据量 +""" +import pymysql +import os +from dotenv import load_dotenv + +load_dotenv() + +# 数据库连接配置 +DB_CONFIG = { + 'host': os.getenv('DB_HOST', '152.136.177.240'), + 'port': int(os.getenv('DB_PORT', 5012)), + 'user': os.getenv('DB_USER', 'finyx'), + 'password': os.getenv('DB_PASSWORD', '6QsGK6MpePZDE57Z'), + 'database': os.getenv('DB_NAME', 'finyx'), + 'charset': 'utf8mb4' +} + +def check_tenant_data(): + """检查各个表中的 tenant_id 数据""" + conn = pymysql.connect(**DB_CONFIG) + cursor = conn.cursor(pymysql.cursors.DictCursor) + + try: + print("=" * 80) + print("检查数据库中的 tenant_id 数据") + print("=" * 80) + + # 1. 检查 f_polic_field 表中的 tenant_id + print("\n1. f_polic_field 表中的 tenant_id 分布:") + cursor.execute(""" + SELECT tenant_id, + COUNT(*) as total_count, + SUM(CASE WHEN field_type = 1 THEN 1 ELSE 0 END) as input_count, + SUM(CASE WHEN field_type = 2 THEN 1 ELSE 0 END) as output_count, + SUM(CASE WHEN state = 1 THEN 1 ELSE 0 END) as enabled_count + FROM f_polic_field + GROUP BY tenant_id + ORDER BY tenant_id + """) + field_tenants = cursor.fetchall() + for row in field_tenants: + print(f" tenant_id: {row['tenant_id']}") + print(f" 总字段数: {row['total_count']}, 输入字段: {row['input_count']}, 输出字段: {row['output_count']}, 启用: {row['enabled_count']}") + + # 2. 检查 f_polic_file_config 表中的 tenant_id + print("\n2. f_polic_file_config 表中的 tenant_id 分布:") + cursor.execute(""" + SELECT tenant_id, + COUNT(*) as total_count, + SUM(CASE WHEN state = 1 THEN 1 ELSE 0 END) as enabled_count + FROM f_polic_file_config + GROUP BY tenant_id + ORDER BY tenant_id + """) + config_tenants = cursor.fetchall() + for row in config_tenants: + print(f" tenant_id: {row['tenant_id']}") + print(f" 总模板数: {row['total_count']}, 启用: {row['enabled_count']}") + + # 3. 检查 f_polic_file_field 表中的 tenant_id + print("\n3. f_polic_file_field 表中的 tenant_id 分布:") + cursor.execute(""" + SELECT tenant_id, + COUNT(*) as total_count, + SUM(CASE WHEN state = 1 THEN 1 ELSE 0 END) as enabled_count + FROM f_polic_file_field + GROUP BY tenant_id + ORDER BY tenant_id + """) + relation_tenants = cursor.fetchall() + for row in relation_tenants: + print(f" tenant_id: {row['tenant_id']}") + print(f" 总关联数: {row['total_count']}, 启用: {row['enabled_count']}") + + # 4. 检查特定 tenant_id 的详细数据 + test_tenant_id = 615873064429507600 + print(f"\n4. 检查 tenant_id = {test_tenant_id} 的详细数据:") + + # 字段数据 + cursor.execute(""" + SELECT COUNT(*) as count + FROM f_polic_field + WHERE tenant_id = %s + """, (test_tenant_id,)) + field_count = cursor.fetchone()['count'] + print(f" f_polic_field 表中的字段数: {field_count}") + + if field_count > 0: + cursor.execute(""" + SELECT id, name, filed_code, field_type, state + FROM f_polic_field + WHERE tenant_id = %s + LIMIT 10 + """, (test_tenant_id,)) + sample_fields = cursor.fetchall() + print(f" 示例字段(前10条):") + for field in sample_fields: + print(f" ID: {field['id']}, 名称: {field['name']}, 编码: {field['filed_code']}, 类型: {field['field_type']}, 状态: {field['state']}") + + # 模板数据 + cursor.execute(""" + SELECT COUNT(*) as count + FROM f_polic_file_config + WHERE tenant_id = %s + """, (test_tenant_id,)) + template_count = cursor.fetchone()['count'] + print(f" f_polic_file_config 表中的模板数: {template_count}") + + # 关联数据 + cursor.execute(""" + SELECT COUNT(*) as count + FROM f_polic_file_field + WHERE tenant_id = %s + """, (test_tenant_id,)) + relation_count = cursor.fetchone()['count'] + print(f" f_polic_file_field 表中的关联数: {relation_count}") + + # 5. 检查所有不同的 tenant_id + print("\n5. 所有表中出现的 tenant_id 汇总:") + cursor.execute(""" + SELECT DISTINCT tenant_id FROM f_polic_field + UNION + SELECT DISTINCT tenant_id FROM f_polic_file_config + UNION + SELECT DISTINCT tenant_id FROM f_polic_file_field + ORDER BY tenant_id + """) + all_tenants = cursor.fetchall() + print(" 所有 tenant_id 列表:") + for row in all_tenants: + print(f" {row['tenant_id']}") + + finally: + cursor.close() + conn.close() + +if __name__ == '__main__': + check_tenant_data() diff --git a/check_relations_query.py b/check_relations_query.py new file mode 100644 index 0000000..93b6800 --- /dev/null +++ b/check_relations_query.py @@ -0,0 +1,88 @@ +""" +检查关联关系查询逻辑 +""" +import pymysql +import os +from dotenv import load_dotenv + +load_dotenv() + +DB_CONFIG = { + 'host': os.getenv('DB_HOST', '152.136.177.240'), + 'port': int(os.getenv('DB_PORT', 5012)), + 'user': os.getenv('DB_USER', 'finyx'), + 'password': os.getenv('DB_PASSWORD', '6QsGK6MpePZDE57Z'), + 'database': os.getenv('DB_NAME', 'finyx'), + 'charset': 'utf8mb4' +} + +TENANT_ID = 615873064429507639 + +def check_relations(): + """检查关联关系查询""" + conn = pymysql.connect(**DB_CONFIG) + cursor = conn.cursor(pymysql.cursors.DictCursor) + + try: + # 检查一个具体模板的关联关系 + template_id = 1765273962716807 # 走读式谈话流程 + + print(f"检查模板 ID: {template_id}") + + # 方法1: 当前 API 使用的查询 + print("\n方法1: 当前 API 使用的查询(带 INNER JOIN 和 state=1):") + cursor.execute(""" + SELECT fff.file_id, fff.filed_id, fff.state as relation_state, fc.state as template_state + FROM f_polic_file_field fff + INNER 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 fff.state = 1 AND fff.file_id = %s + """, (TENANT_ID, template_id)) + results1 = cursor.fetchall() + print(f" 结果数: {len(results1)}") + for r in results1[:5]: + print(f" file_id: {r['file_id']}, filed_id: {r['filed_id']}, relation_state: {r['relation_state']}, template_state: {r['template_state']}") + + # 方法2: 只查询关联表,不检查模板状态 + print("\n方法2: 只查询关联表(不检查模板状态):") + cursor.execute(""" + SELECT fff.file_id, fff.filed_id, fff.state as relation_state + FROM f_polic_file_field fff + WHERE fff.tenant_id = %s AND fff.state = 1 AND fff.file_id = %s + """, (TENANT_ID, template_id)) + results2 = cursor.fetchall() + print(f" 结果数: {len(results2)}") + for r in results2[:5]: + print(f" file_id: {r['file_id']}, filed_id: {r['filed_id']}, relation_state: {r['relation_state']}") + + # 方法3: 检查模板是否存在且启用 + print("\n方法3: 检查模板状态:") + cursor.execute(""" + SELECT id, name, state + FROM f_polic_file_config + WHERE tenant_id = %s AND id = %s + """, (TENANT_ID, template_id)) + template = cursor.fetchone() + if template: + print(f" 模板存在: {template['name']}, state: {template['state']}") + else: + print(f" 模板不存在") + + # 检查所有关联关系(包括 state=0 的) + print("\n方法4: 检查所有关联关系(包括未启用的):") + cursor.execute(""" + SELECT fff.file_id, fff.filed_id, fff.state as relation_state + FROM f_polic_file_field fff + WHERE fff.tenant_id = %s AND fff.file_id = %s + """, (TENANT_ID, template_id)) + results4 = cursor.fetchall() + print(f" 结果数: {len(results4)}") + enabled = [r for r in results4 if r['relation_state'] == 1] + disabled = [r for r in results4 if r['relation_state'] == 0] + print(f" 启用: {len(enabled)}, 未启用: {len(disabled)}") + + finally: + cursor.close() + conn.close() + +if __name__ == '__main__': + check_relations() diff --git a/check_specific_template_relations.py b/check_specific_template_relations.py new file mode 100644 index 0000000..6839af1 --- /dev/null +++ b/check_specific_template_relations.py @@ -0,0 +1,198 @@ +""" +检查特定模板的关联关系 +""" +import pymysql +import os +import re +from pathlib import Path +from docx import Document +from dotenv import load_dotenv + +load_dotenv() + +DB_CONFIG = { + 'host': os.getenv('DB_HOST', '152.136.177.240'), + 'port': int(os.getenv('DB_PORT', 5012)), + 'user': os.getenv('DB_USER', 'finyx'), + 'password': os.getenv('DB_PASSWORD', '6QsGK6MpePZDE57Z'), + 'database': os.getenv('DB_NAME', 'finyx'), + 'charset': 'utf8mb4' +} + +TENANT_ID = 615873064429507639 +TEMPLATE_NAME = "1.请示报告卡(初核谈话)" +TEMPLATE_FILE = "template_finish/2-初核模版/2.谈话审批/走读式谈话审批/1.请示报告卡(初核谈话).docx" + +def extract_placeholders_from_docx(file_path: str): + """从docx文件中提取所有占位符""" + placeholders = set() + pattern = r'\{\{([^}]+)\}\}' + + try: + doc = Document(file_path) + + # 从段落中提取占位符 + for paragraph in doc.paragraphs: + text = paragraph.text + matches = re.findall(pattern, text) + for match in matches: + cleaned = match.strip() + if cleaned and '{' not in cleaned and '}' not in cleaned: + placeholders.add(cleaned) + + # 从表格中提取占位符 + 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: + cleaned = match.strip() + if cleaned and '{' not in cleaned and '}' not in cleaned: + placeholders.add(cleaned) + + except Exception as e: + print(f"错误: 读取文件失败 - {str(e)}") + return [] + + return sorted(list(placeholders)) + +def check_template(): + """检查模板的关联关系""" + conn = pymysql.connect(**DB_CONFIG) + cursor = conn.cursor(pymysql.cursors.DictCursor) + + try: + print(f"检查模板: {TEMPLATE_NAME}") + print("=" * 80) + + # 1. 从文档提取占位符 + print("\n1. 从文档提取占位符:") + if not Path(TEMPLATE_FILE).exists(): + print(f" 文件不存在: {TEMPLATE_FILE}") + return + + placeholders = extract_placeholders_from_docx(TEMPLATE_FILE) + print(f" 占位符数量: {len(placeholders)}") + print(f" 占位符列表: {placeholders}") + + # 2. 查询模板ID + print(f"\n2. 查询模板ID:") + cursor.execute(""" + SELECT id, name + FROM f_polic_file_config + WHERE tenant_id = %s AND name = %s + """, (TENANT_ID, TEMPLATE_NAME)) + template = cursor.fetchone() + if not template: + print(f" 模板不存在") + return + + template_id = template['id'] + print(f" 模板ID: {template_id}") + + # 3. 查询字段映射 + print(f"\n3. 查询字段映射:") + cursor.execute(""" + SELECT id, name, filed_code, field_type, state + FROM f_polic_field + WHERE tenant_id = %s + """, (TENANT_ID,)) + fields = cursor.fetchall() + + field_map = {} + for field in fields: + state = field['state'] + if isinstance(state, bytes): + state = int.from_bytes(state, byteorder='big') if len(state) == 1 else 1 + field_map[field['filed_code']] = { + 'id': field['id'], + 'name': field['name'], + 'field_type': field['field_type'], + 'state': state + } + + print(f" 字段总数: {len(field_map)}") + + # 4. 匹配占位符到字段 + print(f"\n4. 匹配占位符到字段:") + input_field_ids = [] + output_field_ids = [] + not_found = [] + + for placeholder in placeholders: + if placeholder in field_map: + field_info = field_map[placeholder] + if field_info['state'] == 1: + if field_info['field_type'] == 1: + input_field_ids.append(field_info['id']) + elif field_info['field_type'] == 2: + output_field_ids.append(field_info['id']) + else: + not_found.append(placeholder) + + # 添加必需的输入字段 + required_input_fields = ['clue_info', 'target_basic_info_clue'] + for req_field in required_input_fields: + if req_field in field_map: + field_info = field_map[req_field] + if field_info['state'] == 1 and field_info['id'] not in input_field_ids: + input_field_ids.append(field_info['id']) + + print(f" 输入字段ID: {input_field_ids}") + print(f" 输出字段ID: {output_field_ids}") + if not_found: + print(f" 未找到的占位符: {not_found}") + + # 5. 查询数据库中的关联关系 + print(f"\n5. 查询数据库中的关联关系:") + cursor.execute(""" + SELECT fff.filed_id, fff.state, f.name, f.field_type + 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.file_id = %s + """, (TENANT_ID, template_id)) + db_relations = cursor.fetchall() + + db_input_ids = [] + db_output_ids = [] + for rel in db_relations: + state = rel['state'] + if isinstance(state, bytes): + state = int.from_bytes(state, byteorder='big') if len(state) == 1 else 1 + + if state == 1: + if rel['field_type'] == 1: + db_input_ids.append(rel['filed_id']) + elif rel['field_type'] == 2: + db_output_ids.append(rel['filed_id']) + + print(f" 数据库中的输入字段ID: {sorted(db_input_ids)}") + print(f" 数据库中的输出字段ID: {sorted(db_output_ids)}") + + # 6. 对比 + print(f"\n6. 对比结果:") + expected_input = set(input_field_ids) + expected_output = set(output_field_ids) + actual_input = set(db_input_ids) + actual_output = set(db_output_ids) + + print(f" 输入字段 - 期望: {sorted(expected_input)}, 实际: {sorted(actual_input)}") + print(f" 输入字段匹配: {expected_input == actual_input}") + + print(f" 输出字段 - 期望: {sorted(expected_output)}, 实际: {sorted(actual_output)}") + print(f" 输出字段匹配: {expected_output == actual_output}") + + if expected_output != actual_output: + missing = expected_output - actual_output + extra = actual_output - expected_output + print(f" 缺少的输出字段: {sorted(missing)}") + print(f" 多余的输出字段: {sorted(extra)}") + + finally: + cursor.close() + conn.close() + +if __name__ == '__main__': + check_template() diff --git a/check_template_all_relations.py b/check_template_all_relations.py new file mode 100644 index 0000000..30b66f7 --- /dev/null +++ b/check_template_all_relations.py @@ -0,0 +1,98 @@ +""" +检查模板的所有关联关系(包括未启用的) +""" +import pymysql +import os +from dotenv import load_dotenv + +load_dotenv() + +DB_CONFIG = { + 'host': os.getenv('DB_HOST', '152.136.177.240'), + 'port': int(os.getenv('DB_PORT', 5012)), + 'user': os.getenv('DB_USER', 'finyx'), + 'password': os.getenv('DB_PASSWORD', '6QsGK6MpePZDE57Z'), + 'database': os.getenv('DB_NAME', 'finyx'), + 'charset': 'utf8mb4' +} + +TENANT_ID = 615873064429507639 +TEMPLATE_ID = 1765432134276990 # 1.请示报告卡(初核谈话) + +def check_all_relations(): + """检查模板的所有关联关系""" + conn = pymysql.connect(**DB_CONFIG) + cursor = conn.cursor(pymysql.cursors.DictCursor) + + try: + print(f"检查模板 ID: {TEMPLATE_ID}") + print("=" * 80) + + # 查询模板信息 + cursor.execute(""" + SELECT id, name, state + FROM f_polic_file_config + WHERE tenant_id = %s AND id = %s + """, (TENANT_ID, TEMPLATE_ID)) + template = cursor.fetchone() + if template: + print(f"模板名称: {template['name']}") + print(f"模板状态: {template['state']}") + else: + print("模板不存在") + return + + # 查询所有关联关系(包括 state=0 的) + cursor.execute(""" + SELECT + fff.file_id, + fff.filed_id, + fff.state as relation_state, + f.name as field_name, + f.field_type, + f.state as field_state + 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.file_id = %s + ORDER BY f.field_type, f.name + """, (TENANT_ID, TEMPLATE_ID)) + all_relations = cursor.fetchall() + + print(f"\n所有关联关系数: {len(all_relations)}") + + # 按状态分组 + enabled_relations = [r for r in all_relations if r['relation_state'] == 1 or (isinstance(r['relation_state'], bytes) and r['relation_state'] == b'\x01')] + disabled_relations = [r for r in all_relations if r not in enabled_relations] + + print(f"启用的关联关系: {len(enabled_relations)}") + print(f"未启用的关联关系: {len(disabled_relations)}") + + # 按字段类型分组 + input_fields = [r for r in enabled_relations if r['field_type'] == 1] + output_fields = [r for r in enabled_relations if r['field_type'] == 2] + + print(f"\n启用的输入字段关联: {len(input_fields)}") + for r in input_fields: + state_str = str(r['relation_state']) if not isinstance(r['relation_state'], bytes) else 'bytes' + print(f" - {r['field_name']} (ID: {r['filed_id']}, relation_state: {state_str}, field_state: {r['field_state']})") + + print(f"\n启用的输出字段关联: {len(output_fields)}") + for r in output_fields[:10]: + state_str = str(r['relation_state']) if not isinstance(r['relation_state'], bytes) else 'bytes' + print(f" - {r['field_name']} (ID: {r['filed_id']}, relation_state: {state_str}, field_state: {r['field_state']})") + if len(output_fields) > 10: + print(f" ... 还有 {len(output_fields) - 10} 个输出字段") + + # 检查未启用的关联关系 + if disabled_relations: + print(f"\n未启用的关联关系: {len(disabled_relations)}") + disabled_input = [r for r in disabled_relations if r['field_type'] == 1] + disabled_output = [r for r in disabled_relations if r['field_type'] == 2] + print(f" 输入字段: {len(disabled_input)}, 输出字段: {len(disabled_output)}") + + finally: + cursor.close() + conn.close() + +if __name__ == '__main__': + check_all_relations() diff --git a/check_template_with_output_fields.py b/check_template_with_output_fields.py new file mode 100644 index 0000000..6029ce2 --- /dev/null +++ b/check_template_with_output_fields.py @@ -0,0 +1,76 @@ +""" +检查哪些模板有输出字段关联 +""" +import pymysql +import os +from dotenv import load_dotenv + +load_dotenv() + +DB_CONFIG = { + 'host': os.getenv('DB_HOST', '152.136.177.240'), + 'port': int(os.getenv('DB_PORT', 5012)), + 'user': os.getenv('DB_USER', 'finyx'), + 'password': os.getenv('DB_PASSWORD', '6QsGK6MpePZDE57Z'), + 'database': os.getenv('DB_NAME', 'finyx'), + 'charset': 'utf8mb4' +} + +TENANT_ID = 615873064429507639 + +def check_templates_with_output_fields(): + """检查哪些模板有输出字段关联""" + conn = pymysql.connect(**DB_CONFIG) + cursor = conn.cursor(pymysql.cursors.DictCursor) + + try: + # 查询所有模板及其关联的输出字段 + cursor.execute(""" + SELECT + fc.id as template_id, + fc.name as template_name, + COUNT(CASE WHEN f.field_type = 2 THEN 1 END) as output_field_count, + COUNT(CASE WHEN f.field_type = 1 THEN 1 END) as input_field_count, + COUNT(*) as total_field_count + 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 fff.state = 1 + AND fc.state = 1 + GROUP BY fc.id, fc.name + HAVING output_field_count > 0 + ORDER BY output_field_count DESC + LIMIT 10 + """, (TENANT_ID,)) + + templates = cursor.fetchall() + + print(f"有输出字段关联的模板(前10个):") + print("=" * 80) + for t in templates: + print(f"\n模板: {t['template_name']} (ID: {t['template_id']})") + print(f" 输入字段: {t['input_field_count']}, 输出字段: {t['output_field_count']}, 总计: {t['total_field_count']}") + + # 查询该模板的具体输出字段 + cursor.execute(""" + SELECT f.id, f.name, f.filed_code + 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.file_id = %s + AND fff.state = 1 + AND f.field_type = 2 + LIMIT 5 + """, (TENANT_ID, t['template_id'])) + output_fields = cursor.fetchall() + print(f" 输出字段示例(前5个):") + for f in output_fields: + print(f" - {f['name']} (ID: {f['id']}, code: {f['filed_code']})") + + finally: + cursor.close() + conn.close() + +if __name__ == '__main__': + check_templates_with_output_fields() diff --git a/services/__pycache__/document_service.cpython-312.pyc b/services/__pycache__/document_service.cpython-312.pyc index b77a15a..222fd03 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/__pycache__/field_service.cpython-312.pyc b/services/__pycache__/field_service.cpython-312.pyc index adf73b9..58a92f1 100644 Binary files a/services/__pycache__/field_service.cpython-312.pyc and b/services/__pycache__/field_service.cpython-312.pyc differ diff --git a/static/template_field_manager.html b/static/template_field_manager.html index 2340636..3c796ca 100644 --- a/static/template_field_manager.html +++ b/static/template_field_manager.html @@ -562,7 +562,7 @@