from pathlib import Path from fastapi import APIRouter, Depends, HTTPException from api.medods import findPatientByPhone from api.n3health import resend, revoke, sendForSigning from db.schemas.documents import Document from db.schemas.patients import Patient from db.schemas.practitioners import Practitioner from db.schemas.settings import Settings from db.schemas.signings import Signing from db.schemas.statuses import Statuses from utils import requestDict, logger, convert_docs_to_pdfs from utils.attachment import pdfGet router = APIRouter() @router.get("/health", summary="Health") async def health(): return {"status": "healthy"} @router.post("/pre_singing", summary="pre_singing") async def pre_singing( reqData: dict = Depends(requestDict), ): # signature - type: eSignature / eAttorney logger.info(f"📥 Получен запрос на /pre_singing") practitioner = await Practitioner.getPractitionerByIdLpu( reqData.get("body", {}).get("userId", 0) ) signature = {} if practitioner.success: signature["type"] = ( "eSignature" if practitioner.data.get("esiaAuth", False) else "eAttorney" ) signature["expiration"] = practitioner.data.get("expired_at")[:10] daysRemainingControl = 0 deliveryType = None settings = await Settings.getSettings() if settings.success: daysRemainingControl = settings.data.get("expirationAlert", 0) deliveryData = settings.data.get("deliveryType", {}) deliveryType = [k for k, v in deliveryData.items() if v] multipleSending = settings.data.get("multipleSending", False) inProgress = [] complete = [] idPatientMis = reqData.get("body", {}).get("patientId", 0) if type(idPatientMis) != str: idPatientMis = str(idPatientMis) patient = await Patient.getPatientByIdPatientMis(idPatientMis, False, True) if patient.success: docNumbers = reqData.get("body", {}).get("docNumbers", []) for docNumber in docNumbers: if type(docNumber) != int: docNumber = int(docNumber) docs = await Document.getDocumentsByIdPatientMisAndNumber( idPatientMis, docNumber, True ) if docs.success: for doc in docs.data: docSingingId = doc.singingId docSinging = await Signing.getSigningById(docSingingId) if docSinging.success: if docSinging.data.get("storagePath"): complete.append(docNumber) else: if docSinging.data.get("trackingId"): inProgress.append(docNumber) else: logger.info(f"✅ {docNumber} - {docs.message}") exitData = { "signature": signature, "inProgress": inProgress, "complete": complete, "daysRemainingControl": daysRemainingControl, "deliveryType": deliveryType, "multipleSending": multipleSending, } return exitData @router.post("/singing", summary="singing") async def singing( reqData: dict = Depends(requestDict), ): def correctData(data): data["practitioner"]["userIdLpu"] = str(data["practitioner"]["userIdLpu"]) data["patient"]["idPatientMis"] = str(data["patient"]["idPatientMis"]) for recipient in data.get("recipients", []): recipient["idPatientMis"] = str(recipient["idPatientMis"]) return data logger.info(f"📥 Получен запрос на /singing") # Получаем данные из запроса body = reqData.get("body", {}) docs = body.pop("docs", []) # Проверяем наличие документов if not docs: logger.warning("Нет документов для обработки") return {"status": "ERROR", "message": "Нет документов для обработки"} practitioner = await Practitioner.getPractitionerByIdLpu( body.get("practitioner", {}).get("userIdLpu", 0) ) if not practitioner.success: logger.error( f"Сотрудник не найден, idLpu: {body.get('practitioner', {}).get('userIdLpu', 0)}" ) return {"status": "ERROR", "message": "Сотрудник не найден"} esiaAuth = practitioner.data.get("esiaAuth", False) userIdLpu = practitioner.data.get("userIdLpu", 0) body["practitioner"]["snils"] = practitioner.data.get("snils") if not esiaAuth: body["practitioner"]["mrProxyNumber"] = practitioner.data.get("attorney") patient = body.get("patient", None) if not patient: logger.error("Данные пациента не получены, пациент не найден") return { "status": "ERROR", "message": "Данные пациента не получены, пациент не найден", } idPatientMis = patient.get("idPatientMis", None) if not idPatientMis: logger.error("Не указан идентификатор пациента") return {"status": "ERROR", "message": "Не указан идентификатор пациента"} patientDB = await Patient.getPatientByIdPatientMis(idPatientMis, isCheck=True) if not patientDB.success: patientData = { "idPatientMis": idPatientMis, "familyName": patient.get("familyName", "N/A"), "givenName": patient.get("givenName", "N/A"), "middleName": patient.get("middleName", "N/A"), "birthDate": patient.get("birthDate", "N/A"), "sex": patient.get("sex", "N/A"), } newPatient = await Patient.addPatient(**patientData) if not newPatient.success: return { "status": "ERROR", "message": "Не удалось создать пациента", } patientData = newPatient.data.toDict() else: patientData = patientDB.data recipients = body.pop("recipients", []) recipientsData = [] recipientsIds = [] for recipient in recipients: idRecipientMis = recipient.get("idPatientMis", None) if not idRecipientMis: logger.error("Не указан идентификатор пациента") return { "status": "ERROR", "message": "Не указан идентификатор пациента", } recipientDB = await Patient.getPatientByIdPatientMis( idRecipientMis, isCheck=True ) if not recipientDB.success: recipientData = { "idPatientMis": idRecipientMis, "familyName": recipient.get("surname", "N/A"), "givenName": recipient.get("name", "N/A"), "middleName": recipient.get("secondName", "N/A"), "birthDate": recipient.get("birthDate", "N/A"), "sex": recipient.get("sex", "N/A"), } newRecipient = await Patient.addPatient(**recipientData) if not newRecipient.success: return { "status": "ERROR", "message": newRecipient.message, } recipientsData.append(newRecipient.data.toDict()) recipientsIds.append(newRecipient.data.idPatientMis) else: logger.info(recipientDB.data) recipientsData.append(recipientDB.data) recipientsIds.append(recipientDB.data.get("idPatientMis")) recipient["documentDto"] = [ { "docN": recipient.pop("snils"), "docS": "", "documentName": "СНИЛС", "idDocumentType": 223, "providerName": "ПФР", } ] recipient["telecom"] = [ { "system": "Telephone", "value": f'+{recipient.pop("phone")}', } ] body["recipients"] = recipients # Логируем информацию о документах logger.info(f"👨‍⚕️ Медработник: {practitioner.data.get("name")}") logger.info(f"👤 Пациент: {patientData.get('name')}") logger.info( f"👥 Получатели ({len(recipientsData)}): {', '.join([f'{p.get('name')}' for p in recipientsData])}" ) logger.info(f"📄 Документов для обработки: {len(docs)}") logger.info(f"🔐 ЕСИА: {esiaAuth}") try: # Конвертируем документы в PDF conversion_result = convert_docs_to_pdfs(docs, idPatientMis) if conversion_result.get("status", "") != "SUCCESS": return { "status": "ERROR", "message": "Не удалось сохранить документы", } newSinging = await Signing.addSigning( userIdLpu, idPatientMis, body["deliveryType"], recipientsIds ) if not newSinging: return { "status": "ERROR", "message": "Не удалось добавить подписание", } logger.info(f"🔐 Подписание добавлено: {newSinging.id}") pdfFiles = conversion_result.get("files", []) body["medDocument"] = { "esiaAuth": esiaAuth, "attachments": [], } body["files"] = [] for doc in pdfFiles: for idPatient in [idPatientMis, *recipientsIds]: newDocument = await Document.addDocument( singingId=newSinging.id, idPatientMis=str(idPatient), **doc ) if not newDocument: logger.error( f"Не удалось добавить документ для получателя {idPatient}" ) return { "status": "ERROR", "message": "Не удалось добавить документ", } logger.debug( f"📄 Документ добавлен: {newDocument.id} для получателя {idPatient}" ) partName = f'file{len(body["medDocument"]["attachments"])}' body["medDocument"]["attachments"].append( { "partName": partName, } ) body["files"].append( { "field": partName, "path": Path(doc["storagePath"]), "content_type": "application/pdf", } ) if esiaAuth: partName = f'file{len(body["medDocument"]["attachments"])}' body["medDocument"]["attachments"].append( { "partName": partName, } ) body["files"].append( { "field": partName, "path": Path( f"src/attachments/secure/{userIdLpu}/signature.p7s" ).resolve(), "content_type": "application/pkcs7-signature", } ) signingResult = await sendForSigning(correctData(body)) if not signingResult or signingResult.get("status", "") != "created": return { "status": "ERROR", "message": "Не удалось отправить документы на подписание", } newSinging.trackingId = signingResult.get("trackingId", "") await newSinging.save() logger.debug( f"🔐 Подписание обновлено: {newSinging.id}. Добавлен Трек-номер подписания" ) await Statuses.updateStatus(newSinging.id, True) logger.info(f"✅ Обработка завершена!") response = { "status": "SUCCESS", "message": "Документы успешно обработаны.", } return response except Exception as e: error_msg = f"Ошибка при обработке документов: {str(e)}" logger.error(error_msg) raise HTTPException(status_code=500, detail=error_msg) @router.post("/statuses", summary="statuses") async def statuses( reqData: dict = Depends(requestDict), ): logger.info(f"📥 Получен запрос на /statuses") # Получаем данные из запроса body = reqData.get("body", {}) userIdLpu = str(body.get("userIdLpu")) filters = body.get("filters", {}) if not userIdLpu or not filters: logger.error("Не отправлены userIdLpu или filters") return {"status": "ERROR", "message": "Не отправлены необходимые данные"} result = await Signing.getFilteredSingings(userIdLpu, filters) singingsData = result.data for singingData in singingsData: documents = singingData.pop("documents", []) uniqueDocs = [] uniquePaths = [] for doc in documents: if doc.get("storagePath") not in uniquePaths: uniquePaths.append(doc.get("storagePath")) uniqueDocs.append(doc) singingData["documents"] = uniqueDocs return singingsData @router.post("/documents", summary="documents") async def documents( reqData: dict = Depends(requestDict), ): logger.info(f"📥 Получен запрос на /documents") # Получаем данные из запроса body = reqData.get("body", {}) if "idPatientMis" not in body: logger.error("Не отправлен idPatientMis") return {"status": "ERROR", "message": "Не отправлен idPatientMis"} idPatientMis = str(body.get("idPatientMis")) singingData = await Signing.getSigningsByIdPatientMis(idPatientMis) if not singingData.success: logger.error("Подписания не найдены") return {"status": "SUCCESS", "data": [], "message": "Подписания не найдены"} userIdLpus = set([s.get("userIdLpu") for s in singingData.data]) practitioners = {} for userIdLpu in userIdLpus: practitioner = await Practitioner.getPractitionerByIdLpu(userIdLpu, False) practitioners[userIdLpu] = practitioner.data.name for s in singingData.data: s["practitioner"] = practitioners[s.get("userIdLpu")] for singing in singingData.data: statusesData = [ {d.get("idPatientMis"): d.get("patient", {}).get("name")} for d in singing.get("statuses", []) if d.get("idPatientMis") ] idMatchName = {} for statusData in statusesData: for idPatientMisStatus, name in statusData.items(): if idPatientMisStatus in idMatchName: continue idMatchName[idPatientMisStatus] = name documents = singing.pop("documents") uniqueDocs = [] for doc in documents: docOwner = doc.get("storagePath").split("/")[-2:-1][0] isOwn = docOwner == idPatientMis if str(doc.get("idPatientMis")) == idPatientMis: if not isOwn: ownerName = idMatchName[docOwner] doc["title"] = ( f"{doc.get('title')} (Пациент: {idMatchName[docOwner]})" ) uniqueDocs.append(doc) singing["documents"] = uniqueDocs settings = await Settings.getSettings(False) data = {"deliveryType": settings.data.deliveryType, "singings": singingData.data} responseData = {"status": "SUCCESS", "data": data} return responseData @router.post("/document", summary="document") async def document( reqData: dict = Depends(requestDict), ): logger.info(f"📥 Получен запрос на /document") documentPath = reqData.get("body", {}).get("documentPath", None) if not documentPath: return {"status": "ERROR", "message": "Документ не найден"} docBytes = pdfGet(documentPath) if not docBytes.success: return {"status": "ERROR", "message": docBytes.message} return {"status": "SUCCESS", "data": docBytes.data} @router.post("/revoke", summary="revoke") async def revoke_post( reqData: dict = Depends(requestDict), ): logger.info(f"📥 Получен запрос на /revoke") trackingId = reqData.get("body", {}).get("trackingId", None) if not trackingId: return {"status": "ERROR", "message": "Документ не найден"} revokeResult = await revoke(trackingId) if revokeResult.get("status", "") != "success": return { "status": "ERROR", "message": revokeResult.get("errorMessage", "Отозвать не удалось"), } import asyncio await asyncio.sleep(2) await Signing.updateStatuses() return {"status": "SUCCESS", "message": "Документ отозван"} @router.post("/resend", summary="resend") async def resend_post( reqData: dict = Depends(requestDict), ): logger.info(f"📥 Получен запрос на /resend") trackingId = reqData.get("body", {}).get("trackingId", None) idPatientMis = reqData.get("body", {}).get("idPatientMis", None) deliveryType = reqData.get("body", {}).get("deliveryType", None) errorResponse = {"status": "ERROR", "message": "Повторная отправка не удалось"} if not trackingId or not idPatientMis or not deliveryType: logger.error("Не отправлен trackingId или idPatientMis или deliveryType") logger.error( f"trackingId: {trackingId}, idPatientMis: {idPatientMis}, deliveryType: {deliveryType}" ) return errorResponse resendResult = await resend(trackingId, idPatientMis, deliveryType) if not resendResult or not resendResult.get("success", False): logger.error(resendResult) return errorResponse logger.info("✅ Повторная отправка успешна") return {"status": "SUCCESS", "message": resendResult.get("message", "")} @router.post("/advanced-search", summary="advanced-search") async def advanced_search_post( reqData: dict = Depends(requestDict), ): logger.info(f"📥 Получен запрос на /advanced-search") filters = reqData.get("body", {}).get("filters", {}) result = await Signing.getFilteredSingings("", filters) for singingData in result.data: documents = singingData.pop("documents", []) uniqueDocs = [] uniquePaths = [] for doc in documents: if doc.get("storagePath") not in uniquePaths: uniquePaths.append(doc.get("storagePath")) uniqueDocs.append(doc) singingData["documents"] = uniqueDocs return {"status": "SUCCESS", "data": result.data} @router.post("/search-recipients", summary="search-recipients") async def search_recipients_post( reqData: dict = Depends(requestDict), ): logger.info(f"📥 Получен запрос на /search-recipients") phoneNumber = "".join( filter(str.isdigit, reqData.get("body", {}).get("phone", "")) )[-10:] idPatientMis = str(reqData.get("body", {}).get("idPatientMis", "")) settings = await Settings.getSettings(False) if not settings.success: logger.error(settings.message) return {"status": "error", "message": settings.message} settings = settings.data try: patientsDB = await findPatientByPhone( settings.medodsApiHost, settings.medodsApiPort, settings.medodsApiIdentity, settings.medodsApiSecretKey, f"7{phoneNumber}", ) if not patientsDB.success or patientsDB.data.get("totalItems") == 0: logger.error("Пациент не найден") return {"status": "error", "data": []} patients = patientsDB.data.get("data", []) result = [ { "idPatientMis": str(patient.get("id")), "name": patient.get("name"), "surname": patient.get("surname"), "secondName": patient.get("secondName"), "birthDate": patient.get("birthdate"), "sex": patient.get("sex"), "snils": patient.get("snils"), "phone": patient.get("phone"), } for patient in patients if str(patient.get("id")) != idPatientMis ] return { "status": "ok", "data": result, } except Exception as e: logger.error(e) return { "status": "error", "data": [], }