From 9f8addf510f81db2730963c0d27994be30e89c55 Mon Sep 17 00:00:00 2001 From: luc662 Date: Thu, 22 May 2025 22:57:25 +0000 Subject: [PATCH] se agregaron las firmas y los campos del practicante para adaptarse al estandar FHIR --- .../chaincode-go/chaincode/smartcontract.go | 181 +++++++++++------- .../controllers/RecetaController.java | 74 +++++-- .../controllers/VacunaController.java | 2 + .../com/code/hyperledger/models/Receta.java | 3 + .../code/hyperledger/models/RecetaDto.java | 9 +- .../com/code/hyperledger/models/Vacuna.java | 2 + .../code/hyperledger/models/VacunaDto.java | 2 + .../hyperledger/services/RecetaService.java | 5 + 8 files changed, 186 insertions(+), 92 deletions(-) diff --git a/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go index d73de622..6d0490dd 100644 --- a/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go +++ b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go @@ -12,80 +12,90 @@ type SmartContract struct { } type Receta struct { - ID string `json:"id"` - Identifier string `json:"identifier"` - Owner string `json:"owner"` - PrescripcionAnteriorId string `json:"prescripcionAnteriorId"` - Status string `json:"status"` - StatusChange string `json:"statusChange"` - Prioridad string `json:"prioridad"` - Medicacion string `json:"medicacion"` - Razon string `json:"razon"` - Notas string `json:"notas"` - PeriodoDeTratamiento string `json:"periodoDeTratamiento"` - InstruccionesTratamiento string `json:"instruccionesTratamiento"` - PeriodoDeValidez string `json:"periodoDeValidez"` - PatientDocumentNumber string `json:"patientDocumentNumber"` - FechaDeAutorizacion string `json:"fechaDeAutorizacion"` - Cantidad string `json:"cantidad"` - ExpectedSupplyDuration string `json:"expectedSupplyDuration"` + ID string `json:"id"` + Identifier string `json:"identifier"` + Owner string `json:"owner"` + PrescripcionAnteriorId string `json:"prescripcionAnteriorId"` + Status string `json:"status"` + StatusChange string `json:"statusChange"` + Prioridad string `json:"prioridad"` + Medicacion string `json:"medicacion"` + Razon string `json:"razon"` + Notas string `json:"notas"` + PeriodoDeTratamiento string `json:"periodoDeTratamiento"` + InstruccionesTratamiento string `json:"instruccionesTratamiento"` + PeriodoDeValidez string `json:"periodoDeValidez"` + PatientDocumentNumber string `json:"patientDocumentNumber"` + FechaDeAutorizacion string `json:"fechaDeAutorizacion"` + Cantidad string `json:"cantidad"` + ExpectedSupplyDuration string `json:"expectedSupplyDuration"` + Practitioner string `json:"practitioner"` + PractitionerDocumentNumber string `json:"practitionerDocumentNumber"` + Signature string `json:"signature"` } type Vacuna struct { - ID string `json:"id"` // identificador único para el ledger - Identifier string `json:"identifier"` - Status string `json:"status"` // podés validarlo con enums si querés - StatusChange string `json:"statusChange"` // como string (ISO8601) - StatusReason string `json:"statusReason"` - VaccinateCode string `json:"vaccinateCode"` - AdministradedProduct string `json:"administradedProduct"` - Manufacturer string `json:"manufacturer"` - LotNumber string `json:"lotNumber"` - ExpirationDate string `json:"expirationDate"` // como string ISO8601 - PatientDocumentNumber string `json:"patientDocumentNumber"` - Reactions string `json:"reactions"` // puede ser un string o una estructura si querés después + ID string `json:"id"` // identificador único para el ledger + Identifier string `json:"identifier"` + Status string `json:"status"` // podés validarlo con enums si querés + StatusChange string `json:"statusChange"` // como string (ISO8601) + StatusReason string `json:"statusReason"` + VaccinateCode string `json:"vaccinateCode"` + AdministradedProduct string `json:"administradedProduct"` + Manufacturer string `json:"manufacturer"` + LotNumber string `json:"lotNumber"` + ExpirationDate string `json:"expirationDate"` // como string ISO8601 + PatientDocumentNumber string `json:"patientDocumentNumber"` + Reactions string `json:"reactions"` // puede ser un string o una estructura si querés después + Practitioner string `json:"practitioner"` + PractitionerDocumentNumber string `json:"practitionerDocumentNumber"` } - func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error { recetas := []Receta{ { - ID: "receta1", - Identifier: "rece1234", - Owner: "Tomoko", - PrescripcionAnteriorId: "presc123", - Status: "active", - StatusChange: "2024-01-15T10:00:00Z", - Prioridad: "high", - Medicacion: "medicacion1", - Razon: "razon1", - Notas: "algunas notas", - PeriodoDeTratamiento: "30 dias", - InstruccionesTratamiento: "una por dia", - PeriodoDeValidez: "1 anio", - PatientDocumentNumber: "12345678", - FechaDeAutorizacion: "2024-01-01T09:00:00Z", - Cantidad: "5", - ExpectedSupplyDuration: "2024-02-01T09:00:00Z", + ID: "receta1", + Identifier: "rece1234", + Owner: "Tomoko", + PrescripcionAnteriorId: "presc123", + Status: "active", + StatusChange: "2024-01-15T10:00:00Z", + Prioridad: "high", + Medicacion: "medicacion1", + Razon: "razon1", + Notas: "algunas notas", + PeriodoDeTratamiento: "30 dias", + InstruccionesTratamiento: "una por dia", + PeriodoDeValidez: "1 anio", + PatientDocumentNumber: "12345678", + FechaDeAutorizacion: "2024-01-01T09:00:00Z", + Cantidad: "5", + ExpectedSupplyDuration: "2024-02-01T09:00:00Z", + Practitioner: "practitioner", + PractitionerDocumentNumber: "123456789", + Signature: "signature", }, { - ID: "receta2", - Identifier: "rece1235", - Owner: "Alice", - PrescripcionAnteriorId: "presc456", - Status: "completed", - StatusChange: "2024-02-20T11:00:00Z", - Prioridad: "medium", - Medicacion: "medicacion2", - Razon: "razon2", - Notas: "otras notas", - PeriodoDeTratamiento: "60 dias", - InstruccionesTratamiento: "dos por dia", - PeriodoDeValidez: "2 anios", - PatientDocumentNumber: "87654321", - FechaDeAutorizacion: "2024-01-10T10:00:00Z", - Cantidad: "10", - ExpectedSupplyDuration: "2024-04-10T10:00:00Z", + ID: "receta2", + Identifier: "rece1235", + Owner: "Alice", + PrescripcionAnteriorId: "presc456", + Status: "completed", + StatusChange: "2024-02-20T11:00:00Z", + Prioridad: "medium", + Medicacion: "medicacion2", + Razon: "razon2", + Notas: "otras notas", + PeriodoDeTratamiento: "60 dias", + InstruccionesTratamiento: "dos por dia", + PeriodoDeValidez: "2 anios", + PatientDocumentNumber: "87654321", + FechaDeAutorizacion: "2024-01-10T10:00:00Z", + Cantidad: "10", + ExpectedSupplyDuration: "2024-04-10T10:00:00Z", + Practitioner: "practitioner", + PractitionerDocumentNumber: "123456789", + Signature: "signature", }, } @@ -121,6 +131,39 @@ func (s *SmartContract) CreateReceta(ctx contractapi.TransactionContextInterface return ctx.GetStub().PutState(receta.ID, recetaJSON) } +func (s *SmartContract) FirmarReceta(ctx contractapi.TransactionContextInterface, recetaID string, firma string) error { + exists, err := s.RecetaExists(ctx, recetaID) + if err != nil { + return err + } + if !exists { + return fmt.Errorf("la receta %s no existe", recetaID) + } + recetaJSON, err := ctx.GetStub().GetState(recetaID) + if err != nil { + return fmt.Errorf("error al obtener la receta: %v", err) + } + if recetaJSON == nil { + return fmt.Errorf("la receta %s no fue encontrada en el ledger", recetaID) + } + var receta Receta + if err := json.Unmarshal(recetaJSON, &receta); err != nil { + return fmt.Errorf("error al parsear la receta: %v", err) + } + if receta.Status != "draft" { + return fmt.Errorf("la receta %s no puede ser firmada porque no está en estado 'draft'", recetaID) + } + receta.Signature = firma + receta.Status = "active" + receta.StatusChange = "FIRMADA" + updatedRecetaJSON, err := json.Marshal(receta) + if err != nil { + return fmt.Errorf("error al serializar la receta firmada: %v", err) + } + + return ctx.GetStub().PutState(recetaID, updatedRecetaJSON) +} + func (s *SmartContract) EntregarReceta(ctx contractapi.TransactionContextInterface, recetaID string) error { exists, err := s.RecetaExists(ctx, recetaID) if err != nil { @@ -144,13 +187,12 @@ func (s *SmartContract) EntregarReceta(ctx contractapi.TransactionContextInterfa return fmt.Errorf("error al parsear la receta actual: %v", err) } - // Validar que el estado actual sea ACTIVO - if recetaActual.Status != "ACTIVO" { - return fmt.Errorf("solo se puede entregar la receta si está en estado 'ACTIVO'") + if recetaActual.Status != "active" { + return fmt.Errorf("solo se puede entregar la receta si está en estado 'active'") } // Cambiar el estado a ENTREGADO - recetaActual.Status = "ENTREGADO" + recetaActual.Status = "completed" // Guardar la receta modificada updatedRecetaJSON, err := json.Marshal(recetaActual) @@ -260,12 +302,13 @@ func (s *SmartContract) GetMultipleRecetas(ctx contractapi.TransactionContextInt var receta Receta err = json.Unmarshal(recetaJSON, &receta) if err != nil { - return nil, err + return nil, fmt.Errorf("error al parsear la receta con ID %s: %v", id, err) } recetas = append(recetas, &receta) } return recetas, nil } + // TODO: adaptar los campos para que se tengan un identificar de usuarios ademas del DNI func (s *SmartContract) GetRecetasPorDniYEstado(ctx contractapi.TransactionContextInterface, dni string, estado string) ([]*Receta, error) { if dni == "" || estado == "" { diff --git a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/controllers/RecetaController.java b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/controllers/RecetaController.java index 6592d168..2fd59cd3 100644 --- a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/controllers/RecetaController.java +++ b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/controllers/RecetaController.java @@ -35,38 +35,38 @@ public class RecetaController { @PostMapping("/crear") public ResponseEntity crear(@RequestBody Receta receta) { System.out.println("\n--> Submit Transaction: CrearReceta"); - + // Log para verificar los valores iniciales System.out.println("Receta recibida: " + receta); - + String now = LocalDateTime.now().toString(); String dni = receta.getPatientDocumentNumber(); - + // Log para ver el DNI y el timestamp System.out.println("DNI del paciente: " + dni); System.out.println("Timestamp actual: " + now); - + String id = dni + now; String assetId = Hashing.sha256(id); - + // Log para ver el ID generado System.out.println("ID generado para el asset: " + assetId); - + receta.setId(assetId); - + AssetIdDto assetIdDto = new AssetIdDto(); assetIdDto.setDni(dni); assetIdDto.setTimeStamp(now); - + try { // Log antes de intentar cargar la receta System.out.println("Intentando cargar la receta..."); - + recetaService.cargarReceta(receta); - + // Log después de que la receta fue cargada System.out.println("Receta cargada correctamente."); - + return new ResponseEntity<>(assetIdDto, HttpStatus.OK); } catch (CommitStatusException | EndorseException | CommitException | SubmitException e) { // Log de error @@ -74,31 +74,39 @@ public class RecetaController { e.printStackTrace(); return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } - } + } @PostMapping("/obtener") public ResponseEntity find(@RequestBody RecetaRequestDto requestBody) { - logger.info("Received request to obtain receta with ID: {}", requestBody.getId()); // Log de entrada - + logger.info("Received request to obtain receta with ID: {}", requestBody.getId()); // Log de entrada + try { String id = requestBody.getId(); - logger.debug("Searching for receta with ID: {}", id); // Log de búsqueda + logger.debug("Searching for receta with ID: {}", id); // Log de búsqueda Receta receta = recetaService.obtenerReceta(id); - logger.debug("Receta found: {}", receta); // Log cuando se encuentra la receta + logger.debug("Receta found: {}", receta); // Log cuando se encuentra la receta RecetaDto recetaDto = mapToDto(receta); - logger.info("Receta DTO created successfully for ID: {}", id); // Log de éxito + logger.info("Receta DTO created successfully for ID: {}", id); // Log de éxito return new ResponseEntity<>(recetaDto, HttpStatus.OK); } catch (IOException e) { - logger.error("IOException occurred while obtaining receta with ID: {}", requestBody.getId(), e); // Log de excepción específica + logger.error("IOException occurred while obtaining receta with ID: {}", requestBody.getId(), e); // Log de + // excepción + // específica return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE); } catch (GatewayException e) { - logger.error("GatewayException occurred while obtaining receta with ID: {}", requestBody.getId(), e); // Log de excepción Gateway + logger.error("GatewayException occurred while obtaining receta with ID: {}", requestBody.getId(), e); // Log + // de + // excepción + // Gateway return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE); } catch (Exception e) { - logger.error("Unexpected error occurred while obtaining receta with ID: {}", requestBody.getId(), e); // Log de error inesperado + logger.error("Unexpected error occurred while obtaining receta with ID: {}", requestBody.getId(), e); // Log + // de + // error + // inesperado return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } @@ -147,6 +155,29 @@ public class RecetaController { } } + @PutMapping("/firmar") + public ResponseEntity firmarReceta(@RequestBody Map requestBody) { + try { + String id = requestBody.get("id"); + String signature = requestBody.get("signature"); + + if (id == null || id.isEmpty()) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + System.out.println("\n--> Submit Transaction: FirmarReceta"); + + recetaService.firmarReceta(id, signature); + return new ResponseEntity<>(HttpStatus.OK); + } catch (EndorseException | SubmitException | CommitStatusException | CommitException e) { + e.printStackTrace(); // o algún log específico + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (GatewayException e) { + e.printStackTrace(); // este bloque rara vez se ejecutaría si ya atrapás las anteriores + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + private RecetaDto mapToDto(Receta receta) { RecetaDto dto = new RecetaDto(); dto.setIdentifier(receta.getIdentifier()); @@ -165,6 +196,9 @@ public class RecetaController { dto.setFechaDeAutorizacion(receta.getFechaDeAutorizacion()); dto.setCantidad(receta.getCantidad()); dto.setExpectedSupplyDuration(receta.getExpectedSupplyDuration()); + dto.setPractitioner(receta.getPractitioner()); + dto.setPractitionerDocumentNumber(receta.getPractitionerDocumentNumber()); + dto.setSignature(receta.setSignature()); return dto; } } diff --git a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/controllers/VacunaController.java b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/controllers/VacunaController.java index 343f1cf1..ce9baa6a 100644 --- a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/controllers/VacunaController.java +++ b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/controllers/VacunaController.java @@ -157,6 +157,8 @@ public class VacunaController { dto.setExpirationDate(vacuna.getExpirationDate()); dto.setPatientDocumentNumber(vacuna.getPatientDocumentNumber()); dto.setReactions(vacuna.getReactions()); + dto.setPractitioner(receta.getPractitioner()); + dto.setPractitionerDocumentNumber(receta.getPractitionerDocumentNumber()); return dto; } diff --git a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/Receta.java b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/Receta.java index e4babf36..683a4fc4 100644 --- a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/Receta.java +++ b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/Receta.java @@ -28,4 +28,7 @@ public class Receta { private String cantidad; //@JsonFormat(pattern = "yyyy-MM-dd") private String expectedSupplyDuration; + private String practitioner; + private String practitionerDocumentNumber; + private String signature; } \ No newline at end of file diff --git a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/RecetaDto.java b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/RecetaDto.java index 5beeea26..e6055129 100644 --- a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/RecetaDto.java +++ b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/RecetaDto.java @@ -12,7 +12,7 @@ public class RecetaDto { private String owner; private String prescripcionAnteriorId; private String status; - //@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + // @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") private String statusChange; private String prioridad; private String medicacion; @@ -22,9 +22,12 @@ public class RecetaDto { private String instruccionesTratamiento; private String periodoDeValidez; private String patientDocumentNumber; - //@JsonFormat(pattern = "yyyy-MM-dd") + // @JsonFormat(pattern = "yyyy-MM-dd") private String fechaDeAutorizacion; private String cantidad; - //@JsonFormat(pattern = "yyyy-MM-dd") + // @JsonFormat(pattern = "yyyy-MM-dd") private String expectedSupplyDuration; + private String practitioner; + private String practitionerDocumentNumber; + private String signature; } diff --git a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/Vacuna.java b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/Vacuna.java index 174b6f78..80e3cc02 100644 --- a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/Vacuna.java +++ b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/Vacuna.java @@ -20,5 +20,7 @@ public class Vacuna { private String expirationDate; private String patientDocumentNumber; private String reactions; + private String practitioner; + private String practitionerDocumentNumber; } diff --git a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/VacunaDto.java b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/VacunaDto.java index 6886fa30..ed195091 100644 --- a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/VacunaDto.java +++ b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/models/VacunaDto.java @@ -20,4 +20,6 @@ public class VacunaDto { private String expirationDate; private String patientDocumentNumber; private String reactions; + private String practitioner; + private String practitionerDocumentNumber; } diff --git a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/services/RecetaService.java b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/services/RecetaService.java index d9faba5e..f19282b3 100644 --- a/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/services/RecetaService.java +++ b/asset-transfer-basic/maven-API-SiMeDi/src/main/java/com/code/hyperledger/services/RecetaService.java @@ -149,6 +149,11 @@ public class RecetaService { contract.submitTransaction("EntregarReceta", recetaId); } + public void firmarReceta(String recetaId, String signature) + throws CommitStatusException, EndorseException, CommitException, SubmitException { + contract.submitTransaction("FirmarReceta", recetaId, signature); + } + public List obtenerRecetasPorDniYEstado(String dni, String estado) throws GatewayException, IOException { if (dni == null || dni.isBlank() || estado == null || estado.isBlank()) { throw new IllegalArgumentException("DNI y estado son obligatorios");