292 lines
10 KiB
PHP
292 lines
10 KiB
PHP
<?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] ?? '';
|
|
}
|
|
} |