ソースを参照

implementação do crud para cpr monitoring e configuração para rodar via docker

Ranghetti 1 ヶ月 前
コミット
81eeb1fe72

+ 7 - 0
.dockerignore

@@ -0,0 +1,7 @@
+.env
+.env.*
+**/.env
+**/.env.*
+
+build
+*.deb

+ 27 - 0
Dockerfile

@@ -0,0 +1,27 @@
+FROM php:8.2-cli
+
+RUN apt-get update \
+    && apt-get install -y --no-install-recommends \
+        git \
+        unzip \
+        bash \
+        sed \
+        libpq-dev \
+        postgresql-client \
+    && docker-php-ext-install pdo_pgsql \
+    && rm -rf /var/lib/apt/lists/*
+
+COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
+
+WORKDIR /app
+
+COPY composer.json composer.lock* ./
+RUN composer install --no-interaction --no-progress
+
+COPY . .
+
+RUN rm -f /app/.env || true
+
+RUN chmod +x /app/bin/setup || true
+
+CMD ["bash", "-lc", "sed -i 's/\r$//' /app/bin/setup && chmod +x /app/bin/setup && /app/bin/setup && php /app/public/index.php"]

+ 61 - 0
controllers/CprMonitoringCreateController.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace Controllers;
+
+use Libs\ResponseLib;
+use Psr\Http\Message\ServerRequestInterface;
+use Respect\Validation\Exceptions\ValidationException;
+use Respect\Validation\Validator as val;
+use Services\CprMonitoringService;
+
+class CprMonitoringCreateController
+{
+    private CprMonitoringService $service;
+
+    public function __construct()
+    {
+        $this->service = new CprMonitoringService();
+    }
+
+    public function __invoke(ServerRequestInterface $request)
+    {
+        $userId = (int)($request->getAttribute('api_user_id') ?? 0);
+        $companyId = (int)($request->getAttribute('api_company_id') ?? 0);
+
+        if ($userId <= 0 || $companyId <= 0) {
+            return ResponseLib::sendFail('Unauthorized', [], 'E_VALIDATE')->withStatus(401);
+        }
+
+        $body = json_decode((string)$request->getBody(), true) ?? [];
+
+        try {
+            val::key('cpr_id', val::intType()->positive())
+                ->key('description', val::stringType()->notEmpty()->length(1, 5000))
+                ->key('link', val::stringType()->notEmpty()->length(1, 2048))
+                ->key('preview', val::boolType(), false)
+                ->assert($body);
+        } catch (ValidationException $e) {
+            return ResponseLib::sendFail('Validation failed: ' . $e->getFullMessage(), [], 'E_VALIDATE')->withStatus(400);
+        }
+
+        $cprId = (int)$body['cpr_id'];
+        $preview = isset($body['preview']) ? (bool)$body['preview'] : false;
+        $description = (string)$body['description'];
+        $link = (string)$body['link'];
+
+        try {
+            $created = $this->service->create($cprId, $preview, $description, $link);
+        } catch (\InvalidArgumentException $e) {
+            return ResponseLib::sendFail($e->getMessage(), [], 'E_VALIDATE')->withStatus(400);
+        } catch (\PDOException $e) {
+            if (($e->getCode() ?? '') === '23503') {
+                return ResponseLib::sendFail('CPR not found', ['cpr_id' => $cprId], 'E_NOT_FOUND')->withStatus(404);
+            }
+            return ResponseLib::sendFail('Failed to create cpr monitoring: ' . $e->getMessage(), [], 'E_DATABASE')->withStatus(500);
+        } catch (\Throwable $e) {
+            return ResponseLib::sendFail('Failed to create cpr monitoring: ' . $e->getMessage(), [], 'E_INTERNAL')->withStatus(500);
+        }
+
+        return ResponseLib::sendOk($created, 'S_CREATED');
+    }
+}

+ 53 - 0
controllers/CprMonitoringDeleteController.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace Controllers;
+
+use Libs\ResponseLib;
+use Psr\Http\Message\ServerRequestInterface;
+use Respect\Validation\Exceptions\ValidationException;
+use Respect\Validation\Validator as val;
+
+use Services\CprMonitoringService;
+
+class CprMonitoringDeleteController
+{
+    private CprMonitoringService $service;
+
+    public function __construct()
+    {
+        $this->service = new CprMonitoringService();
+    }
+
+    public function __invoke(ServerRequestInterface $request)
+    {
+        $userId = (int)($request->getAttribute('api_user_id') ?? 0);
+        $companyId = (int)($request->getAttribute('api_company_id') ?? 0);
+
+        if ($userId <= 0 || $companyId <= 0) {
+            return ResponseLib::sendFail('Unauthorized', [], 'E_VALIDATE')->withStatus(401);
+        }
+
+        $body = json_decode((string)$request->getBody(), true) ?? [];
+
+        try {
+            val::key('id', val::intType()->positive())
+                ->assert($body);
+        } catch (ValidationException $e) {
+            return ResponseLib::sendFail('Validation failed: ' . $e->getFullMessage(), [], 'E_VALIDATE')->withStatus(400);
+        }
+
+        $id = (int)$body['id'];
+
+        try {
+            $deleted = $this->service->delete($id);
+        } catch (\InvalidArgumentException $e) {
+            return ResponseLib::sendFail($e->getMessage(), [], 'E_VALIDATE')->withStatus(400);
+        } catch (\Throwable $e) {
+            return ResponseLib::sendFail('Failed to delete cpr monitoring: ' . $e->getMessage(), [], 'E_DATABASE')->withStatus(500);
+        }
+
+        return $deleted
+            ? ResponseLib::sendOk(['deleted' => true], 'S_DELETED')
+            : ResponseLib::sendFail('Cpr monitoring Not Found', [], 'E_DATABASE')->withStatus(204);
+    }
+}

+ 52 - 0
controllers/CprMonitoringGetController.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace Controllers;
+
+use Libs\ResponseLib;
+use Psr\Http\Message\ServerRequestInterface;
+use Respect\Validation\Exceptions\ValidationException;
+use Respect\Validation\Validator as val;
+use Services\CprMonitoringService;
+
+class CprMonitoringGetController
+{
+    private CprMonitoringService $service;
+
+    public function __construct()
+    {
+        $this->service = new CprMonitoringService();
+    }
+
+    public function __invoke(ServerRequestInterface $request)
+    {
+        $userId = (int)($request->getAttribute('api_user_id') ?? 0);
+        $companyId = (int)($request->getAttribute('api_company_id') ?? 0);
+
+        if ($userId <= 0 || $companyId <= 0) {
+            return ResponseLib::sendFail('Unauthorized', [], 'E_VALIDATE')->withStatus(401);
+        }
+
+        $body = json_decode((string)$request->getBody(), true) ?? [];
+
+        try {
+            val::key('id', val::intType()->positive())
+                ->assert($body);
+        } catch (ValidationException $e) {
+            return ResponseLib::sendFail('Validation failed: ' . $e->getFullMessage(), [], 'E_VALIDATE')->withStatus(400);
+        }
+
+        $id = (int)$body['id'];
+
+        try {
+            $row = $this->service->getById($id);
+        } catch (\InvalidArgumentException $e) {
+            return ResponseLib::sendFail($e->getMessage(), [], 'E_VALIDATE')->withStatus(400);
+        } catch (\Throwable $e) {
+            return ResponseLib::sendFail('Failed to fetch cpr monitoring: ' . $e->getMessage(), [], 'E_DATABASE')->withStatus(500);
+        }
+
+        return $row
+            ? ResponseLib::sendOk($row)
+            : ResponseLib::sendFail('Cpr monitoring not found', [], 'E_NOT_FOUND')->withStatus(404);
+    }
+}

+ 52 - 0
controllers/CprMonitoringListController.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace Controllers;
+
+use Libs\ResponseLib;
+use Psr\Http\Message\ServerRequestInterface;
+use Respect\Validation\Exceptions\ValidationException;
+use Respect\Validation\Validator as val;
+use Services\CprMonitoringService;
+
+class CprMonitoringListController
+{
+    private CprMonitoringService $service;
+
+    public function __construct()
+    {
+        $this->service = new CprMonitoringService();
+    }
+
+    public function __invoke(ServerRequestInterface $request)
+    {
+        $userId = (int)($request->getAttribute('api_user_id') ?? 0);
+        $companyId = (int)($request->getAttribute('api_company_id') ?? 0);
+
+        if ($userId <= 0 || $companyId <= 0) {
+            return ResponseLib::sendFail('Unauthorized', [], 'E_VALIDATE')->withStatus(401);
+        }
+
+        $body = json_decode((string)$request->getBody(), true) ?? [];
+
+        try {
+            val::key('cpr_id', val::intType()->positive())
+                ->assert($body);
+        } catch (ValidationException $e) {
+            return ResponseLib::sendFail('Validation failed: ' . $e->getFullMessage(), [], 'E_VALIDATE')->withStatus(400);
+        }
+
+        $cprId = (int)$body['cpr_id'];
+
+        try {
+            $rows = $this->service->listByCprId($cprId);
+        } catch (\InvalidArgumentException $e) {
+            return ResponseLib::sendFail($e->getMessage(), [], 'E_VALIDATE')->withStatus(400);
+        } catch (\Throwable $e) {
+            return ResponseLib::sendFail('Failed to list cpr monitoring: ' . $e->getMessage(), [], 'E_DATABASE')->withStatus(500);
+        }
+
+        return $rows
+            ? ResponseLib::sendOk($rows)
+            : ResponseLib::sendFail('Cpr monitoring not found', [], 'E_DATABASE')->withStatus(204);
+    }
+}

+ 62 - 0
controllers/CprMonitoringUpdateController.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace Controllers;
+
+use Libs\ResponseLib;
+use Psr\Http\Message\ServerRequestInterface;
+use Respect\Validation\Exceptions\ValidationException;
+use Respect\Validation\Validator as val;
+use Services\CprMonitoringService;
+
+class CprMonitoringUpdateController
+{
+    private CprMonitoringService $service;
+
+    public function __construct()
+    {
+        $this->service = new CprMonitoringService();
+    }
+
+    public function __invoke(ServerRequestInterface $request)
+    {
+        $userId = (int)($request->getAttribute('api_user_id') ?? 0);
+        $companyId = (int)($request->getAttribute('api_company_id') ?? 0);
+
+        if ($userId <= 0 || $companyId <= 0) {
+            return ResponseLib::sendFail('Unauthorized', [], 'E_VALIDATE')->withStatus(401);
+        }
+
+        $body = json_decode((string)$request->getBody(), true) ?? [];
+
+        try {
+            val::key('id', val::intType()->positive())
+                ->key('preview', val::optional(val::boolType()), false)
+                ->key('description', val::optional(val::stringType()->notEmpty()->length(1, 5000)), false)
+                ->key('link', val::optional(val::stringType()->notEmpty()->length(1, 2048)), false)
+                ->assert($body);
+        } catch (ValidationException $e) {
+            return ResponseLib::sendFail('Validation failed: ' . $e->getFullMessage(), [], 'E_VALIDATE')->withStatus(400);
+        }
+
+        $id = (int)$body['id'];
+        $preview = array_key_exists('preview', $body) ? (bool)$body['preview'] : null;
+        $description = array_key_exists('description', $body) ? (string)$body['description'] : null;
+        $link = array_key_exists('link', $body) ? (string)$body['link'] : null;
+
+        if ($preview === null && $description === null && $link === null) {
+            return ResponseLib::sendFail('Validation failed: nothing to update', [], 'E_VALIDATE')->withStatus(400);
+        }
+
+        try {
+            $updated = $this->service->update($id, $preview, $description, $link);
+        } catch (\InvalidArgumentException $e) {
+            return ResponseLib::sendFail($e->getMessage(), [], 'E_VALIDATE')->withStatus(400);
+        } catch (\Throwable $e) {
+            return ResponseLib::sendFail('Failed to update cpr monitoring: ' . $e->getMessage(), [], 'E_DATABASE')->withStatus(500);
+        }
+
+        return $updated
+            ? ResponseLib::sendOk($updated, 'S_UPDATED')
+            : ResponseLib::sendFail('Cpr monitoring Not Found or Not Updated', [], 'E_DATABASE')->withStatus(204);
+    }
+}

+ 38 - 0
docker-compose.yml

@@ -0,0 +1,38 @@
+services:
+  api:
+    build:
+      context: .
+    environment:
+      PORT: "8080"
+      X_LISTEN: "0.0.0.0:8080"
+      APP_PORT: "8000"
+      ROOT_DIR: "/home/uriri/Projetos/tooeasy/php_api"
+      DB_HOST: "host.docker.internal"
+      DB_PORT: "5432"
+      DB_NAME: "too_easy_db"
+      DB_USER: "postgres"
+      DB_PASSWORD: "postgres"
+      JWT_SECRET: "Chaiziquaiyae9wi5Zi4eegieritha0Ted9ocahv"
+      CORS: "true"
+      EASY_COIN_ADDR: "0x065FB961Df42F2fdE1705007AFA24b8697d5C899"
+      EASY_TOKEN_ADDR: "0x5F5776584b69d4C61590b9911EdcE2aBA0f0BA93"
+      EASY_ADMIM_PUBLIC_KEY: "0xf3bc81939d217cbadf1632009bf71c6e40f5943f"
+      EASY_ADMIM_PRIVATE_KEY: "77bd3a9eba21633b009732c181093e6d4afd6df5527643e167499d1b917bfdd0"
+      POLYGON_RPC_URL: "https://polygon-mainnet.g.alchemy.com/v2/Kh62-BdLFgC12ohpmM7jqC_l160Z6OpD"
+      CLIENT_ID: "1db37d27-0dcf-41e8-bb9e-917bd9979095"
+      CLIENT_SECRET: "3e98288c-3354-4dc9-8947-087e7252eb8a"
+      GRANT_TYPE: "client_credentials&client_id=1db37d27-0dcf-41e8-bb9e-917bd9979095&client_secret=3e98288c-3354-4dc9-8947-087e7252eb8a"
+      B3_URL: "https://api-balcao.b3.com.br/api/oauth/token"
+      CERT_PASS: "PVGZVZ"
+      B3_URL_CPR: "https://api-balcao.b3.com.br/pipeline/b3/v2/cpr/instruments"
+      TSHIELD_LOGIN: "TOOEASY"
+      TSHIELD_PASSWORD: "yjXWlYrnm6"
+      TSHIELD_CLIENT: "TOOEASY"
+      TSHIELD_VALIDATION_ID: "12283"
+      TSHIELD_VALIDATION_ID_CNPJ: "12284"
+      TSHIELD_BASE_URL: "https://api.tshield.com.br"
+      WOOVI_APPID: "Q2xpZW50X0lkX2U3MmU0YmJkLTMzYWQtNGYzNC05Y2M3LTg0ZmJkMGQzMDI1MzpDbGllbnRfU2VjcmV0X1J5WXJCSkZRSDlKK0V3NWJ2Z1dCM2xJWElIWjNuTUVrY0txcTBqNFZUdHc9"
+      WOOVI_BASE_URL: "https://api.woovi.com"
+      WOOVI_CLI_PATH: "bin/woovi"
+    ports:
+      - "8000:8080"

+ 27 - 0
migrations/20260328_add_cpr_monitoring_table.sql

@@ -0,0 +1,27 @@
+BEGIN;
+
+CREATE TABLE IF NOT EXISTS "cpr_monitoring" (
+  "id" SERIAL PRIMARY KEY,
+  "cpr_id" INTEGER NOT NULL,
+  "preview" BOOLEAN NOT NULL DEFAULT FALSE,
+  "description" TEXT NOT NULL,
+  "link" TEXT NOT NULL
+);
+
+DO $$
+BEGIN
+  IF NOT EXISTS (
+    SELECT 1
+    FROM pg_constraint
+    WHERE conname = 'cpr_monitoring_cpr_id_fkey'
+  ) THEN
+    ALTER TABLE "cpr_monitoring"
+      ADD CONSTRAINT cpr_monitoring_cpr_id_fkey
+      FOREIGN KEY ("cpr_id") REFERENCES "cpr"("cpr_id")
+      ON DELETE CASCADE;
+  END IF;
+END $$;
+
+CREATE INDEX IF NOT EXISTS idx_cpr_monitoring_cpr_id ON "cpr_monitoring"("cpr_id");
+
+COMMIT;

+ 105 - 0
models/CprMonitoringModel.php

@@ -0,0 +1,105 @@
+<?php
+
+namespace Models;
+
+class CprMonitoringModel
+{
+    private \PDO $pdo;
+
+    public function __construct()
+    {
+        if (!isset($GLOBALS['pdo']) || !$GLOBALS['pdo'] instanceof \PDO) {
+            throw new \RuntimeException('Global PDO connection not initialized');
+        }
+
+        $this->pdo = $GLOBALS['pdo'];
+    }
+
+    public function getById(int $id): ?array
+    {
+        $stmt = $this->pdo->prepare('SELECT id, cpr_id, preview, description, link FROM "cpr_monitoring" WHERE id = :id LIMIT 1');
+        $stmt->execute(['id' => $id]);
+        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+        return $row ?: null;
+    }
+
+    public function listByCprId(int $cprId): array
+    {
+        $stmt = $this->pdo->prepare(
+            'SELECT id, cpr_id, preview, description, link
+             FROM "cpr_monitoring"
+             WHERE cpr_id = :cpr_id
+             ORDER BY id DESC'
+        );
+        $stmt->execute(['cpr_id' => $cprId]);
+
+        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
+    }
+
+    public function create(int $cprId, bool $preview, string $description, string $link): array
+    {
+        $stmt = $this->pdo->prepare(
+            'INSERT INTO "cpr_monitoring" (cpr_id, preview, description, link)
+             VALUES (:cpr_id, :preview, :description, :link)
+             RETURNING id'
+        );
+
+        $stmt->execute([
+            'cpr_id' => $cprId,
+            'preview' => $preview,
+            'description' => $description,
+            'link' => $link,
+        ]);
+
+        $id = (int)$stmt->fetchColumn();
+
+        return [
+            'id' => $id,
+            'cpr_id' => $cprId,
+            'preview' => $preview,
+            'description' => $description,
+            'link' => $link,
+        ];
+    }
+
+    public function update(int $id, ?bool $preview = null, ?string $description = null, ?string $link = null): ?array
+    {
+        $fields = [];
+        $params = ['id' => $id];
+
+        if ($preview !== null) {
+            $fields[] = 'preview = :preview';
+            $params['preview'] = $preview;
+        }
+        if ($description !== null) {
+            $fields[] = 'description = :description';
+            $params['description'] = $description;
+        }
+        if ($link !== null) {
+            $fields[] = 'link = :link';
+            $params['link'] = $link;
+        }
+
+        if (!$fields) {
+            return null;
+        }
+
+        $sql = 'UPDATE "cpr_monitoring" SET ' . implode(', ', $fields) . ' WHERE id = :id';
+        $stmt = $this->pdo->prepare($sql);
+        $ok = $stmt->execute($params);
+        if (!$ok) {
+            return null;
+        }
+
+        return $this->getById($id);
+    }
+
+    public function delete(int $id): bool
+    {
+        $stmt = $this->pdo->prepare('DELETE FROM "cpr_monitoring" WHERE id = :id');
+        $stmt->execute(['id' => $id]);
+
+        return $stmt->rowCount() > 0;
+    }
+}

+ 7 - 0
public/index.php

@@ -105,4 +105,11 @@ $app->post('/b3/cpr/register', $authJwt, $onlyCompany1Or2, \Controllers\B3CprReg
 $app->post('/b3/payment/confirm', $authJwt, \Controllers\PaymentConfirmController::class);
 $app->post('/cpr/fast-track', \Controllers\CprFastTrackController::class);
 
+// CPR monitoring
+$app->post('/cpr/monitoring/create', $authJwt, \Controllers\CprMonitoringCreateController::class);
+$app->post('/cpr/monitoring/get', $authJwt, \Controllers\CprMonitoringGetController::class);
+$app->post('/cpr/monitoring/list', $authJwt, \Controllers\CprMonitoringListController::class);
+$app->post('/cpr/monitoring/update', $authJwt, \Controllers\CprMonitoringUpdateController::class);
+$app->post('/cpr/monitoring/delete', $authJwt, \Controllers\CprMonitoringDeleteController::class);
+
 $app->run();

+ 85 - 0
services/CprMonitoringService.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace Services;
+
+use Models\CprMonitoringModel;
+
+class CprMonitoringService
+{
+    private CprMonitoringModel $model;
+
+    public function __construct()
+    {
+        $this->model = new CprMonitoringModel();
+    }
+
+    public function create(int $cprId, bool $preview, string $description, string $link): array
+    {
+        if ($cprId <= 0) {
+            throw new \InvalidArgumentException('Invalid cpr_id');
+        }
+
+        $description = trim($description);
+        $link = trim($link);
+
+        if ($description === '') {
+            throw new \InvalidArgumentException('Invalid description');
+        }
+
+        if ($link === '') {
+            throw new \InvalidArgumentException('Invalid link');
+        }
+
+        return $this->model->create($cprId, $preview, $description, $link);
+    }
+
+    public function getById(int $id): ?array
+    {
+        if ($id <= 0) {
+            throw new \InvalidArgumentException('Invalid id');
+        }
+
+        return $this->model->getById($id);
+    }
+
+    public function listByCprId(int $cprId): array
+    {
+        if ($cprId <= 0) {
+            throw new \InvalidArgumentException('Invalid cpr_id');
+        }
+
+        return $this->model->listByCprId($cprId);
+    }
+
+    public function update(int $id, ?bool $preview = null, ?string $description = null, ?string $link = null): ?array
+    {
+        if ($id <= 0) {
+            throw new \InvalidArgumentException('Invalid id');
+        }
+
+        if ($description !== null) {
+            $description = trim($description);
+            if ($description === '') {
+                throw new \InvalidArgumentException('Invalid description');
+            }
+        }
+
+        if ($link !== null) {
+            $link = trim($link);
+            if ($link === '') {
+                throw new \InvalidArgumentException('Invalid link');
+            }
+        }
+
+        return $this->model->update($id, $preview, $description, $link);
+    }
+
+    public function delete(int $id): bool
+    {
+        if ($id <= 0) {
+            throw new \InvalidArgumentException('Invalid id');
+        }
+
+        return $this->model->delete($id);
+    }
+}