se creo el endpoint para la vacuna y el smart contract

This commit is contained in:
luc662 2025-04-06 16:14:42 +00:00
parent 07d5d9e5f2
commit 44e3f4210d
5 changed files with 373 additions and 0 deletions

View file

@ -31,6 +31,22 @@ type Receta struct {
ExpectedSupplyDuration string `json:"expectedSupplyDuration"`
}
type Vacuna struct {
ID string `json:"id"` // identificador único para el ledger
Identificador string `json:"identificador"`
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
DniPaciente string `json:"dniPaciente"`
Reactions string `json:"reactions"` // puede ser un string o una estructura si querés después
}
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
recetas := []Receta{
{
@ -282,3 +298,86 @@ func (s *SmartContract) GetRecetasPorDniYEstado(ctx contractapi.TransactionConte
return recetasFiltradas, nil
}
func (s *SmartContract) CreateVacuna(ctx contractapi.TransactionContextInterface, vacuna Vacuna) error {
exists, err := s.VacunaExists(ctx, vacuna.ID)
if err != nil {
return err
}
if exists {
return fmt.Errorf("la vacuna %s ya existe", vacuna.ID)
}
vacunaJSON, err := json.Marshal(vacuna)
if err != nil {
return err
}
return ctx.GetStub().PutState(vacuna.ID, vacunaJSON)
}
func (s *SmartContract) VacunaExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
vacunaJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return false, err
}
return vacunaJSON != nil, nil
}
unc (s *SmartContract) ReadVacuna(ctx contractapi.TransactionContextInterface, id string) (*Vacuna, error) {
vacunaJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("error al leer del ledger: %v", err)
}
if vacunaJSON == nil {
return nil, fmt.Errorf("la vacuna %s no existe", id)
}
var vacuna Vacuna
err = json.Unmarshal(vacunaJSON, &vacuna)
if err != nil {
return nil, err
}
return &vacuna, nil
}
func (s *SmartContract) GetVacunasPorDniYEstado(ctx contractapi.TransactionContextInterface, dni string, estado string) ([]*Vacuna, error) {
if dni == "" {
return nil, fmt.Errorf("el dni es obligatorio")
}
resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
if err != nil {
return nil, fmt.Errorf("error al obtener datos del ledger: %v", err)
}
defer resultsIterator.Close()
var vacunasFiltradas []*Vacuna
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
var vacuna Vacuna
// Solo deserializamos si es posible (podría fallar si no es una vacuna)
if err := json.Unmarshal(queryResponse.Value, &vacuna); err != nil {
continue
}
// Validamos que tenga un DNI y coincida
if vacuna.DniPaciente != dni {
continue
}
// Si se pasó un estado, lo filtramos
if estado != "" && vacuna.Status != estado {
continue
}
vacunasFiltradas = append(vacunasFiltradas, &vacuna)
}
return vacunasFiltradas, nil
}

View file

@ -0,0 +1,92 @@
package com.code.hyperledger.controllers;
import com.code.hyperledger.Utils.Hashing;
import com.code.hyperledger.models.AssetIdDto;
import com.code.hyperledger.models.Vacuna;
import com.code.hyperledger.services.VacunaService;
import org.hyperledger.fabric.client.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/vacunas")
public class VacunaController {
@Autowired
private VacunaService vacunaService;
@PostMapping("/crear")
public ResponseEntity<AssetIdDto> crearVacuna(@RequestBody Vacuna vacuna) {
System.out.println("\n--> Submit Transaction: CrearVacuna");
String now = LocalDateTime.now().toString();
String dni = vacuna.getDniPaciente();
String id = dni + now;
String assetId = Hashing.sha256(id);
vacuna.setId(assetId);
AssetIdDto assetIdDto = new AssetIdDto();
assetIdDto.setDni(dni);
assetIdDto.setTimeStamp(now);
try {
vacunaService.registrarVacuna(vacuna);
return new ResponseEntity<>(assetIdDto, HttpStatus.OK);
} catch (CommitStatusException | EndorseException | CommitException | SubmitException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
}
@PostMapping("/obtener")
public ResponseEntity<Vacuna> obtenerVacuna(@RequestBody Map<String, String> requestBody) {
try {
String id = requestBody.get("id");
if (id == null || id.isEmpty()) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
Vacuna vacuna = vacunaService.obtenerVacuna(id);
return new ResponseEntity<>(vacuna, HttpStatus.OK);
} catch (IOException | GatewayException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
}
@GetMapping("/todas")
public ResponseEntity<List<Vacuna>> obtenerTodasLasVacunas() {
try {
List<Vacuna> vacunas = vacunaService.obtenerTodasLasVacunas();
return new ResponseEntity<>(vacunas, HttpStatus.OK);
} catch (IOException | GatewayException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/filtrar")
public ResponseEntity<List<Vacuna>> obtenerVacunasPorDniYEstado(@RequestBody Map<String, String> filtros) {
try {
String dni = filtros.get("dni");
String estado = filtros.get("estado"); // puede venir null o vacío
if (dni == null || dni.isEmpty()) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
List<Vacuna> vacunas = vacunaService.obtenerVacunasPorDniYEstado(dni, estado);
return new ResponseEntity<>(vacunas, HttpStatus.OK);
} catch (IOException | GatewayException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

View file

@ -0,0 +1,25 @@
package com.code.hyperledger.models;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Vacuna {
private String id;
private String identificador;
private String status;
private String statusChange;
private String statusReason;
private String vaccinateCode;
private String administradedProduct;
private String manufacturer;
private String lotNumber;
private String expirationDate;
private String dniPaciente;
private String reactions;
}

View file

@ -0,0 +1,23 @@
package com.code.hyperledger.models;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class VacunaDto {
private String identificador;
private String status;
//@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private String statusChange;
private String statusReason;
private String vaccinateCode;
private String administradedProduct;
private String manufacturer;
private String lotNumber;
private String expirationDate;
private String dniPaciente;
private String reactions;
}

View file

@ -0,0 +1,134 @@
package com.code.hyperledger.services;
import com.code.hyperledger.models.Vacuna;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.grpc.Grpc;
import io.grpc.ManagedChannel;
import io.grpc.TlsChannelCredentials;
import lombok.SneakyThrows;
import org.hyperledger.fabric.client.*;
import org.hyperledger.fabric.client.identity.*;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
public class VacunaService {
private static final String MSP_ID = System.getenv().getOrDefault("MSP_ID", "Org1MSP");
private static final String CHANNEL_NAME = System.getenv().getOrDefault("CHANNEL_NAME", "mychannel");
private static final String CHAINCODE_NAME = System.getenv().getOrDefault("CHAINCODE_NAME", "basic");
private static final Path CRYPTO_PATH = Paths
.get("../../test-network/organizations/peerOrganizations/org1.example.com");
private static final Path CERT_DIR_PATH = CRYPTO_PATH.resolve("users/User1@org1.example.com/msp/signcerts");
private static final Path KEY_DIR_PATH = CRYPTO_PATH.resolve("users/User1@org1.example.com/msp/keystore");
private static final Path TLS_CERT_PATH = CRYPTO_PATH.resolve("peers/peer0.org1.example.com/tls/ca.crt");
private static final String PEER_ENDPOINT = "localhost:7051";
private static final String OVERRIDE_AUTH = "peer0.org1.example.com";
private Contract contract;
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
private static Path getFirstFilePath(Path dirPath) throws IOException {
try (var keyFiles = Files.list(dirPath)) {
return keyFiles.findFirst().orElseThrow();
}
}
@SneakyThrows
@PostConstruct
public void init() {
var channel = newGrpcConnection();
var builder = Gateway.newInstance()
.identity(newIdentity())
.signer(newSigner())
.connection(channel)
.evaluateOptions(options -> options.withDeadlineAfter(5, TimeUnit.SECONDS))
.endorseOptions(options -> options.withDeadlineAfter(15, TimeUnit.SECONDS))
.submitOptions(options -> options.withDeadlineAfter(5, TimeUnit.SECONDS))
.commitStatusOptions(options -> options.withDeadlineAfter(1, TimeUnit.MINUTES));
try (var gateway = builder.connect()) {
this.setContract(gateway);
}
}
private static ManagedChannel newGrpcConnection() throws IOException {
var credentials = TlsChannelCredentials.newBuilder()
.trustManager(TLS_CERT_PATH.toFile())
.build();
return Grpc.newChannelBuilder(PEER_ENDPOINT, credentials)
.overrideAuthority(OVERRIDE_AUTH)
.build();
}
private Identity newIdentity() throws IOException, CertificateException {
try (var certReader = Files.newBufferedReader(getFirstFilePath(CERT_DIR_PATH))) {
var certificate = Identities.readX509Certificate(certReader);
return new X509Identity(MSP_ID, certificate);
}
}
private Signer newSigner() throws IOException, InvalidKeyException {
try (var keyReader = Files.newBufferedReader(getFirstFilePath(KEY_DIR_PATH))) {
var privateKey = Identities.readPrivateKey(keyReader);
return Signers.newPrivateKeySigner(privateKey);
}
}
private void setContract(final Gateway gateway) {
var network = gateway.getNetwork(CHANNEL_NAME);
contract = network.getContract(CHAINCODE_NAME);
}
public void registrarVacuna(Vacuna vacuna)
throws CommitStatusException, EndorseException, CommitException, SubmitException {
contract.submitTransaction(
"CreateVacuna",
vacuna.getId(),
vacuna.getNombre(),
vacuna.getLaboratorio(),
vacuna.getFechaAplicacion(),
vacuna.getDniPaciente(),
vacuna.getEstado()
);
}
public Vacuna obtenerVacuna(String vacunaId) throws GatewayException, IOException {
var evaluateResult = contract.evaluateTransaction("ReadVacuna", vacunaId);
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(evaluateResult, Vacuna.class);
}
public List<Vacuna> obtenerTodasLasVacunas() throws GatewayException, IOException {
var evaluateResult = contract.evaluateTransaction("GetAllVacunas");
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(evaluateResult,
objectMapper.getTypeFactory().constructCollectionType(List.class, Vacuna.class));
}
public List<Vacuna> obtenerVacunasPorDniYEstado(String dni, String estado) throws GatewayException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
byte[] evaluateResult;
if (estado == null || estado.isBlank()) {
evaluateResult = contract.evaluateTransaction("GetVacunasPorDni", dni);
} else {
evaluateResult = contract.evaluateTransaction("GetVacunasPorDniYEstado", dni, estado);
}
return objectMapper.readValue(evaluateResult,
objectMapper.getTypeFactory().constructCollectionType(List.class, Vacuna.class));
}
}