init commit
All checks were successful
build-api-image / build (push) Successful in 51s

This commit is contained in:
Antonio Lopes dos Santos
2026-08-04 16:52:02 -03:00
commit afec302c3e
11594 changed files with 1636728 additions and 0 deletions

View File

@@ -0,0 +1,292 @@
<?php
namespace App\Services\Ambulatorio;
use App\Models\Ambulatorio\Agendamento;
use App\Models\Intranet\Cliente;
use Illuminate\Log\Logger;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Client\Response;
use Illuminate\Http\Response as HttpResponse;
use RuntimeException;
use Carbon\Carbon;
use Illuminate\Support\Str;
class RegistroPacienteAposAgendamento
{
private function log(): Logger
{
return logger()->channel('integracao-safecare');
}
public function integracaoPaciente(Agendamento $agendamento): void
{
$cliente = Cliente::where('link', config('database.default'))->firstOrFail();
$token = $this->autenticarSafecare($cliente);
$payload = $this->pacientePayload($agendamento, $cliente);
$this->log()->debug('SafeCare payload (final)', $payload);
$response = $this->postPaciente($payload, $agendamento, $token);
if ($response->failed()) {
$this->log()->error('Safecare: falha ao registrar paciente', [
'agendamento_id' => $agendamento->id,
'customer' => $cliente->link,
'status' => $response->status(),
'response_body' => $response->json() ?? $response->body(),
'payload' => $payload,
]);
throw new RuntimeException(sprintf(
'Erro API Safecare (status %s): %s',
$response->status(),
json_encode($response->json() ?? $response->body(), JSON_UNESCAPED_UNICODE)
));
}
$this->log()->info('Safecare: paciente registrado com sucesso', [
'agendamento_id' => $agendamento->id,
'customer' => $cliente->link,
'result' => $response->json('result'),
'patient_id' => $response->json('patientId'),
]);
}
protected function autenticarSafecare(Cliente $cliente): string
{
if (!$cliente->safecare_usuario || !$cliente->safecare_senha) {
$this->log()->error('Safecare: credenciais não configuradas', [
'customer' => $cliente->link,
]);
throw new RuntimeException(
'Credenciais Safecare não configuradas para o cliente: ' . $cliente->link
);
}
$cacheKey = 'safecare_token_' . $cliente->getKey();
$tokenCached = Cache::get($cacheKey);
if ($tokenCached && $this->tokenEstaAtivo($tokenCached)) {
return $tokenCached;
}
$this->log()->info('Safecare: solicitando novo token', ['customer' => $cliente->link]);
$token = $this->solicitarTokenSafecare($cliente);
Cache::put($cacheKey, $token, now()->addHours(23));
return $token;
}
private function solicitarTokenSafecare(Cliente $cliente): string
{
$response = Http::asJson()
->timeout(15)
->post(
rtrim(config('services.safecare.base_url'), '/') . '/api/partner/auth/login',
[
'identity' => $cliente->safecare_usuario,
'password' => $cliente->safecare_senha,
]
);
if ($response->failed()) {
$this->log()->error('Safecare: falha na autenticação', [
'customer' => $cliente->link,
'status' => $response->status(),
'response_body' => $response->json() ?? $response->body(),
]);
throw new RuntimeException(sprintf(
'Erro autenticação Safecare (status %s): %s',
$response->status(),
json_encode($response->json() ?? $response->body(), JSON_UNESCAPED_UNICODE)
));
}
$token = $response->json('token');
if (!$token) {
$this->log()->error('Safecare: token ausente na resposta de autenticação', [
'customer' => $cliente->link,
'response_body' => $response->json(),
]);
throw new RuntimeException('Token ausente na resposta de autenticação Safecare');
}
return $token;
}
private function tokenEstaAtivo(string $token): bool
{
$partes = explode('.', $token);
if (count($partes) !== 3) {
return false;
}
$payload = json_decode(base64_decode(strtr($partes[1], '-_', '+/')), true);
if (!isset($payload['exp'])) {
return false;
}
return now()->timestamp < ($payload['exp'] - 300);
}
protected function postPaciente(array $payload, Agendamento $agendamento, string $token): Response
{
$chaveIdempotencia = 'agendamento:' . $agendamento->id;
return Http::asJson()
->withHeaders([
'Authorization' => 'Bearer ' . $token,
'X-Idempotency-Key' => $chaveIdempotencia,
])
->timeout(15)
->retry(3, 200, fn($_, $response) => !$response || $response->status() !== HttpResponse::HTTP_UNAUTHORIZED)
->post(
rtrim(config('services.safecare.base_url'), '/') . '/api/partner/patients',
$payload
);
}
protected function pacientePayload(Agendamento $agendamento, Cliente $cliente): array
{
$dadosPaciente = DB::table('ambulatorio_agendamento')
->join('cadastro_paciente', 'cadastro_paciente.id', 'ambulatorio_agendamento.paciente_id')
->join('cadastro_cidadao', 'cadastro_cidadao.id', 'cadastro_paciente.cidadao_id')
->leftJoin('cadastro_cidadao_telefone', function ($join) {
$join->on('cadastro_cidadao_telefone.cidadao_id', '=', 'cadastro_cidadao.id')
->where('cadastro_cidadao_telefone.principal', 1);
})
->leftJoin('cadastro_sexo', 'cadastro_sexo.id', 'cadastro_cidadao.sexo_id')
->leftJoin('cadastro_nacionalidade', 'cadastro_nacionalidade.id', 'cadastro_cidadao.nacionalidade_id')
->leftJoin('cadastro_raca_cor', 'cadastro_raca_cor.id', 'cadastro_cidadao.raca_cor_id')
->leftJoin('cadastro_domicilio', 'cadastro_domicilio.id', 'cadastro_cidadao.domicilio_id')
->leftJoin('cadastro_endereco', 'cadastro_endereco.id', 'cadastro_domicilio.endereco_id')
->leftJoin('cadastro_logradouro', 'cadastro_logradouro.id', 'cadastro_endereco.logradouro_id')
->leftJoin('cadastro_logradouro_tipo', 'cadastro_logradouro_tipo.id', 'cadastro_logradouro.tipo_id')
->leftJoin('cadastro_bairro', 'cadastro_bairro.id', 'cadastro_logradouro.bairro_id')
->where('ambulatorio_agendamento.id', $agendamento->id)
->select(
[
'cadastro_cidadao.nome AS name',
'cadastro_cidadao.cns',
'cadastro_cidadao.cpf',
'cadastro_cidadao.data_nascimento AS birthdate',
DB::raw("COALESCE(cadastro_cidadao_telefone.numero, '') AS phone"),
'cadastro_sexo.descricao AS sex',
DB::raw("COALESCE(cadastro_cidadao.nome_mae, '') AS motherName"),
DB::raw("COALESCE(cadastro_nacionalidade.descricao, '') AS nationality"),
DB::raw("COALESCE(cadastro_raca_cor.descricao, '') AS race"),
'cadastro_logradouro_tipo.descricao as type',
'cadastro_logradouro.nome as street',
'cadastro_endereco.numero as number',
'cadastro_bairro.nome as neighborhood',
'cadastro_logradouro.cep as zipCode'
]
)
->distinct()
->orderBy('cadastro_cidadao.nome')
->first();
if (!$dadosPaciente) {
throw new RuntimeException(
'Dados do paciente não encontrados para o agendamento: ' . $agendamento->id
);
}
$race = $this->mapearRaca($dadosPaciente->race ?? null);
$sex = $this->mapearSexo($dadosPaciente->sex ?? null);
return [
'address' => [
'city' => '',
'complement' => '',
'neighborhood' => $dadosPaciente->neighborhood ?? '',
'number' => isset($dadosPaciente->number) ? (string) $dadosPaciente->number : '',
'reference' => '',
'state' => '',
'street' => $dadosPaciente->street ?? '',
'zipCode' => $dadosPaciente->zipCode ?? '',
],
'birthdate' => $dadosPaciente->birthdate
? Carbon::parse($dadosPaciente->birthdate)->format('Y-m-d')
: '',
'cns' => $dadosPaciente->cns ?? '',
'cpf' => $dadosPaciente->cpf ?? '',
'ethnicity' => '',
'guardianName' => '',
'isHomeCare' => false,
'metaData' => $this->metaDadosSafeCare($agendamento, $cliente),
'motherName' => $dadosPaciente->motherName ?? '',
'name' => $dadosPaciente->name ?? '',
'phone' => $dadosPaciente->phone ?? '',
'race' => $race,
'sex' => $sex,
];
}
protected function metaDadosSafeCare(Agendamento $agendamento, Cliente $cliente): array
{
return [
'agendamento_id' => (string) $agendamento->id,
'jornada_id' => (string) $agendamento->jornada_id,
'paciente_id' => (string) $agendamento->paciente_id,
'customer' => $cliente->link ?? '',
];
}
private function normalizarEnum(?string $valor): string
{
if ($valor === null) {
return '';
}
return strtoupper(trim(Str::ascii($valor)));
}
private function mapearRaca(?string $raca): string
{
$normalizada = $this->normalizarEnum($raca);
$mapa = [
'BRANCA' => 'white',
'PRETA' => 'black',
'PARDA' => 'brown',
'AMARELA' => 'yellow',
'INDIGENA' => 'indigenous',
];
return $mapa[$normalizada] ?? '';
}
private function mapearSexo(?string $sexo): string
{
$normalizada = $this->normalizarEnum($sexo);
$mapa = [
'MASCULINO' => 'male',
'FEMININO' => 'female',
];
return $mapa[$normalizada] ?? '';
}
}