diff --git a/scripts/repair_bug_318.py b/scripts/repair_bug_318.py new file mode 100644 index 00000000..f97afed1 --- /dev/null +++ b/scripts/repair_bug_318.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Bug #318 历史数据修复脚本 +为已存在但未生成手术医嘱的手术申请单补齐数据 +""" + +import psycopg2 +import json +import random +import string +from datetime import datetime + +# 数据库连接配置 +DB_CONFIG = { + "host": "47.116.196.11", + "port": 15432, + "database": "postgresql", + "user": "postgresql", + "password": "postgresql", # 请根据实际情况修改 +} + + +def generate_bus_no(): + """生成4位随机bus_no""" + return "".join(random.choices(string.digits, k=4)) + + +def check_repair_needed(conn): + """检查需要修复的记录数""" + cursor = conn.cursor() + sql = """ + SELECT COUNT(*) + FROM doc_request_form rf + LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no + AND sr.delete_flag = '0' + WHERE rf.type_code = 'PROCEDURE' + AND rf.delete_flag = '0' + AND sr.id IS NULL + """ + cursor.execute(sql) + count = cursor.fetchone()[0] + cursor.close() + return count + + +def get_repair_list(conn): + """获取需要修复的手术申请单列表""" + cursor = conn.cursor() + sql = """ + SELECT + rf.id, + rf.prescription_no, + rf.encounter_id, + rf.patient_id, + rf.requester_id, + rf.create_time, + rf.org_id, + rf.desc_json + FROM doc_request_form rf + LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no + AND sr.delete_flag = '0' + WHERE rf.type_code = 'PROCEDURE' + AND rf.delete_flag = '0' + AND sr.id IS NULL + ORDER BY rf.create_time DESC + """ + cursor.execute(sql) + results = cursor.fetchall() + cursor.close() + return results + + +def parse_surgery_info(desc_json): + """解析手术信息""" + if not desc_json: + return {} + try: + if isinstance(desc_json, str): + return json.loads(desc_json) + return desc_json + except: + return {} + + +def repair_service_request(conn, repair_list): + """修复手术医嘱(ServiceRequest)""" + cursor = conn.cursor() + + insert_sql = """ + INSERT INTO wor_service_request ( + bus_no, prescription_no, status_enum, generate_source_enum, + therapy_enum, quantity, unit_code, category_enum, + patient_id, requester_id, encounter_id, authored_time, + org_id, content_json, delete_flag, create_time, create_by, tenant_id + ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + RETURNING id + """ + + new_requests = [] + for record in repair_list: + ( + rf_id, + prescription_no, + encounter_id, + patient_id, + requester_id, + create_time, + org_id, + desc_json, + ) = record + + # 解析手术信息 + surgery_info = parse_surgery_info(desc_json) + content_json = ( + json.dumps(surgery_info, ensure_ascii=False) if surgery_info else None + ) + + # 生成bus_no + bus_no = generate_bus_no() + + # 插入ServiceRequest + cursor.execute( + insert_sql, + ( + bus_no, # bus_no + prescription_no, # prescription_no + 1, # status_enum: 1-待签发 + 1, # generate_source_enum: 1-医生处方 + 2, # therapy_enum: 2-临时医嘱 + 1, # quantity + "次", # unit_code + 4, # category_enum: 4-手术 + patient_id, # patient_id + requester_id, # requester_id + encounter_id, # encounter_id + create_time, # authored_time + org_id, # org_id + content_json, # content_json + "0", # delete_flag + create_time, # create_time + requester_id, # create_by + 1, # tenant_id + ), + ) + + service_request_id = cursor.fetchone()[0] + new_requests.append( + { + "service_request_id": service_request_id, + "bus_no": bus_no, + "prescription_no": prescription_no, + "patient_id": patient_id, + "encounter_id": encounter_id, + "requester_id": requester_id, + "create_time": create_time, + "org_id": org_id, + "surgery_info": surgery_info, + } + ) + + conn.commit() + cursor.close() + return new_requests + + +def repair_charge_items(conn, new_requests): + """修复收费项目(ChargeItem)""" + cursor = conn.cursor() + + insert_sql = """ + INSERT INTO adm_charge_item ( + bus_no, status_enum, generate_source_enum, patient_id, + context_enum, encounter_id, enterer_id, entered_date, + service_table, service_id, product_table, requesting_org_id, + quantity_value, quantity_unit, unit_price, total_price, + delete_flag, create_time, create_by, tenant_id + ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + """ + + for req in new_requests: + surgery_info = req["surgery_info"] + + # 手术费用 + surgery_fee = 0 + if surgery_info and "surgeryFee" in surgery_info: + try: + surgery_fee = float(surgery_info["surgeryFee"]) + except: + surgery_fee = 0 + + # 插入手术费用收费项目 + cursor.execute( + insert_sql, + ( + "CI" + req["bus_no"], # bus_no + 1, # status_enum: 1-草稿 + 1, # generate_source_enum: 1-医生处方 + req["patient_id"], # patient_id + 3, # context_enum: 3-诊疗 + req["encounter_id"], # encounter_id + req["requester_id"], # enterer_id + req["create_time"], # entered_date + "wor_service_request", # service_table + req["service_request_id"], # service_id + "wor_activity_definition", # product_table + req["org_id"], # requesting_org_id + 1, # quantity_value + "次", # quantity_unit + surgery_fee, # unit_price + surgery_fee, # total_price + "0", # delete_flag + req["create_time"], # create_time + req["requester_id"], # create_by + 1, # tenant_id + ), + ) + + # 麻醉费用(如果存在且大于0) + anesthesia_fee = 0 + if surgery_info and "anesthesiaFee" in surgery_info: + try: + anesthesia_fee = float(surgery_info["anesthesiaFee"]) + except: + anesthesia_fee = 0 + + if anesthesia_fee > 0: + cursor.execute( + insert_sql, + ( + "CI" + req["bus_no"] + "_A", # bus_no + 1, # status_enum + 1, # generate_source_enum + req["patient_id"], # patient_id + 3, # context_enum + req["encounter_id"], # encounter_id + req["requester_id"], # enterer_id + req["create_time"], # entered_date + "wor_service_request", # service_table + req["service_request_id"], # service_id + "wor_activity_definition", # product_table + req["org_id"], # requesting_org_id + 1, # quantity_value + "次", # quantity_unit + anesthesia_fee, # unit_price + anesthesia_fee, # total_price + "0", # delete_flag + req["create_time"], # create_time + req["requester_id"], # create_by + 1, # tenant_id + ), + ) + + conn.commit() + cursor.close() + + +def verify_repair(conn): + """验证修复结果""" + cursor = conn.cursor() + + # 统计手术医嘱数量 + cursor.execute(""" + SELECT COUNT(*) + FROM wor_service_request + WHERE category_enum = 4 AND delete_flag = '0' + """) + service_request_count = cursor.fetchone()[0] + + # 统计收费项目数量 + cursor.execute(""" + SELECT COUNT(*) + FROM adm_charge_item ci + WHERE ci.service_table = 'wor_service_request' + AND EXISTS ( + SELECT 1 FROM wor_service_request sr + WHERE sr.id = ci.service_id AND sr.category_enum = 4 + ) + """) + charge_item_count = cursor.fetchone()[0] + + cursor.close() + return service_request_count, charge_item_count + + +def main(): + """主函数""" + print("=" * 60) + print("Bug #318 历史数据修复脚本") + print("=" * 60) + print() + + try: + # 连接数据库 + print("正在连接数据库...") + conn = psycopg2.connect(**DB_CONFIG) + print("✓ 数据库连接成功") + print() + + # 步骤1:检查需要修复的记录数 + print("步骤1: 检查需要修复的记录...") + need_repair_count = check_repair_needed(conn) + print(f"✓ 发现 {need_repair_count} 条需要修复的手术申请单") + print() + + if need_repair_count == 0: + print("没有需要修复的数据,退出") + return + + # 步骤2:获取修复列表 + print("步骤2: 获取修复列表...") + repair_list = get_repair_list(conn) + print(f"✓ 获取到 {len(repair_list)} 条记录") + print() + + # 步骤3:修复ServiceRequest + print("步骤3: 生成手术医嘱(ServiceRequest)...") + new_requests = repair_service_request(conn, repair_list) + print(f"✓ 成功生成 {len(new_requests)} 条手术医嘱") + print() + + # 步骤4:修复ChargeItem + print("步骤4: 生成收费项目(ChargeItem)...") + repair_charge_items(conn, new_requests) + print(f"✓ 成功生成收费项目") + print() + + # 步骤5:验证修复结果 + print("步骤5: 验证修复结果...") + service_count, charge_count = verify_repair(conn) + print(f"✓ 当前手术医嘱总数: {service_count}") + print(f"✓ 当前手术收费项目总数: {charge_count}") + print() + + print("=" * 60) + print("✓ 修复完成!") + print("=" * 60) + + except Exception as e: + print(f"✗ 错误: {e}") + import traceback + + traceback.print_exc() + finally: + if "conn" in locals(): + conn.close() + print("\n数据库连接已关闭") + + +if __name__ == "__main__": + main() diff --git a/sql/bug_318_repair_execute.sql b/sql/bug_318_repair_execute.sql new file mode 100644 index 00000000..278590a5 --- /dev/null +++ b/sql/bug_318_repair_execute.sql @@ -0,0 +1,240 @@ +-- ============================================================ +-- Bug #318 历史数据修复 - 简化版SQL脚本 +-- 为已存在但未生成手术医嘱的手术申请单补齐数据 +-- ============================================================ +-- 执行说明: +-- 1. 在数据库管理工具中执行(如 DBeaver、pgAdmin) +-- 2. 建议先执行查询部分确认数据 +-- 3. 备份数据库后再执行修复 +-- ============================================================ + +-- 步骤1: 查看需要修复的记录数 +-- ============================================================ +SELECT + '需要修复的手术申请单数量' as 检查项, + COUNT(*) as 数量 +FROM doc_request_form rf +LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no + AND sr.delete_flag = '0' +WHERE rf.type_code = 'PROCEDURE' + AND rf.delete_flag = '0' + AND sr.id IS NULL; + +-- 步骤2: 查看详细列表(可选) +-- ============================================================ +/* +SELECT + rf.id as request_form_id, + rf.prescription_no as 手术单号, + rf.encounter_id as 就诊ID, + rf.patient_id as 患者ID, + rf.create_time as 申请时间 +FROM doc_request_form rf +LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no + AND sr.delete_flag = '0' +WHERE rf.type_code = 'PROCEDURE' + AND rf.delete_flag = '0' + AND sr.id IS NULL +ORDER BY rf.create_time DESC +LIMIT 10; +*/ + +-- 步骤3: 执行修复 - 生成手术医嘱(ServiceRequest) +-- ============================================================ +-- ⚠️ 请在确认备份数据库后执行以下语句 + +INSERT INTO wor_service_request ( + bus_no, + prescription_no, + status_enum, + generate_source_enum, + therapy_enum, + quantity, + unit_code, + category_enum, + patient_id, + requester_id, + encounter_id, + authored_time, + org_id, + content_json, + delete_flag, + create_time, + create_by, + tenant_id +) +SELECT + LPAD(FLOOR(RANDOM() * 10000)::TEXT, 4, '0') as bus_no, -- 生成4位随机数 + rf.prescription_no, + 1 as status_enum, -- 1-待签发 + 1 as generate_source_enum, -- 1-医生处方 + 2 as therapy_enum, -- 2-临时医嘱 + 1 as quantity, + '次' as unit_code, + 4 as category_enum, -- 4-手术 + rf.patient_id, + rf.requester_id, + rf.encounter_id, + rf.create_time as authored_time, + rf.org_id, + rf.desc_json as content_json, -- 存储手术详情 + '0' as delete_flag, + rf.create_time, + rf.requester_id as create_by, + 1 as tenant_id +FROM doc_request_form rf +LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no + AND sr.delete_flag = '0' +WHERE rf.type_code = 'PROCEDURE' + AND rf.delete_flag = '0' + AND sr.id IS NULL; + +-- 步骤4: 查看生成的手术医嘱数量 +-- ============================================================ +SELECT + '生成的手术医嘱数量' as 检查项, + COUNT(*) as 数量 +FROM wor_service_request +WHERE category_enum = 4 + AND delete_flag = '0' + AND create_time > CURRENT_TIMESTAMP - INTERVAL '1 hour'; -- 最近1小时生成的 + +-- 步骤5: 生成手术收费项目(ChargeItem) +-- ============================================================ +-- 先创建临时表存储新生成的ServiceRequest +CREATE TEMPORARY TABLE temp_new_service_requests AS +SELECT + sr.id as service_request_id, + sr.bus_no, + sr.prescription_no, + sr.patient_id, + sr.encounter_id, + sr.requester_id, + sr.create_time, + sr.org_id, + sr.content_json +FROM wor_service_request sr +WHERE sr.category_enum = 4 + AND sr.delete_flag = '0' + AND sr.create_time > CURRENT_TIMESTAMP - INTERVAL '1 hour'; -- 最近1小时生成的 + +-- 插入手术费用收费项目 +INSERT INTO adm_charge_item ( + bus_no, + status_enum, + generate_source_enum, + patient_id, + context_enum, + encounter_id, + enterer_id, + entered_date, + service_table, + service_id, + product_table, + requesting_org_id, + quantity_value, + quantity_unit, + unit_price, + total_price, + delete_flag, + create_time, + create_by, + tenant_id +) +SELECT + 'CI' || t.bus_no as bus_no, + 1 as status_enum, -- 1-草稿 + 1 as generate_source_enum, -- 1-医生处方 + t.patient_id, + 3 as context_enum, -- 3-诊疗 + t.encounter_id, + t.requester_id as enterer_id, + t.create_time as entered_date, + 'wor_service_request' as service_table, + t.service_request_id as service_id, + 'wor_activity_definition' as product_table, + t.org_id as requesting_org_id, + 1 as quantity_value, + '次' as quantity_unit, + COALESCE((t.content_json::jsonb->>'surgeryFee')::numeric, 0) as unit_price, + COALESCE((t.content_json::jsonb->>'surgeryFee')::numeric, 0) as total_price, + '0' as delete_flag, + t.create_time, + t.requester_id as create_by, + 1 as tenant_id +FROM temp_new_service_requests t +WHERE COALESCE((t.content_json::jsonb->>'surgeryFee')::numeric, 0) >= 0; + +-- 插入麻醉费用收费项目(如果有麻醉费用) +INSERT INTO adm_charge_item ( + bus_no, + status_enum, + generate_source_enum, + patient_id, + context_enum, + encounter_id, + enterer_id, + entered_date, + service_table, + service_id, + product_table, + requesting_org_id, + quantity_value, + quantity_unit, + unit_price, + total_price, + delete_flag, + create_time, + create_by, + tenant_id +) +SELECT + 'CI' || t.bus_no || '_A' as bus_no, + 1 as status_enum, + 1 as generate_source_enum, + t.patient_id, + 3 as context_enum, + t.encounter_id, + t.requester_id as enterer_id, + t.create_time as entered_date, + 'wor_service_request' as service_table, + t.service_request_id as service_id, + 'wor_activity_definition' as product_table, + t.org_id as requesting_org_id, + 1 as quantity_value, + '次' as quantity_unit, + (t.content_json::jsonb->>'anesthesiaFee')::numeric as unit_price, + (t.content_json::jsonb->>'anesthesiaFee')::numeric as total_price, + '0' as delete_flag, + t.create_time, + t.requester_id as create_by, + 1 as tenant_id +FROM temp_new_service_requests t +WHERE t.content_json::jsonb->>'anesthesiaFee' IS NOT NULL + AND (t.content_json::jsonb->>'anesthesiaFee')::numeric > 0; + +-- 步骤6: 验证修复结果 +-- ============================================================ +SELECT + '手术医嘱总数' as 检查项, + COUNT(*) as 数量 +FROM wor_service_request +WHERE category_enum = 4 + AND delete_flag = '0'; + +SELECT + '手术收费项目总数' as 检查项, + COUNT(*) as 数量 +FROM adm_charge_item ci +WHERE ci.service_table = 'wor_service_request' + AND EXISTS ( + SELECT 1 FROM wor_service_request sr + WHERE sr.id = ci.service_id AND sr.category_enum = 4 + ); + +-- 清理临时表 +DROP TABLE IF EXISTS temp_new_service_requests; + +-- ============================================================ +-- ✓ 修复完成! +-- ============================================================