update result 2 3 LN patho sikarin
parent
25959140ae
commit
41ee69e49a
|
|
@ -18,7 +18,6 @@ class LisSendBackUpController extends Controller
|
|||
{
|
||||
error_log("=== Hanuman Result Sikarin START ===");
|
||||
|
||||
|
||||
$sendMapping = [];
|
||||
|
||||
// config
|
||||
|
|
|
|||
|
|
@ -0,0 +1,437 @@
|
|||
<?php
|
||||
|
||||
namespace console\controllers;
|
||||
|
||||
use console\models\ApiToken;
|
||||
use console\models\HisRequest;
|
||||
use console\models\LisResult;
|
||||
use console\models\PatientCaseApprove;
|
||||
use yii\console\Controller;
|
||||
use Yii;
|
||||
|
||||
class LisSendPathoAllLnController extends Controller
|
||||
{
|
||||
/**
|
||||
* Console action: รันกระบวนการส่งผลไปยัง external API
|
||||
* เรียกจาก CLI: php /path/to/yii lis-api-send/create
|
||||
*/
|
||||
public function actionCreate()
|
||||
{
|
||||
error_log("=== Hanuman Result Sikarin Patho START ===");
|
||||
|
||||
|
||||
$resultPatho = PatientCaseApprove::find()
|
||||
->joinWith(['approveBy'])
|
||||
->andWhere(['<>', 'ln', ''])
|
||||
->andWhere(['not like', 'ln', '-%', false])
|
||||
->andWhere(['not', ['report_at' => null]])
|
||||
->andWhere(['not', ['approve_at' => null]])
|
||||
->andWhere(['approve_status' => 3])
|
||||
->andWhere(['>', 'register_at', '2026-02-01 00:00:00'])
|
||||
->andWhere(['in', 'patient_case_approve.hospital_id', [703, 366, 367, 169]])
|
||||
->all();
|
||||
|
||||
foreach ($resultPatho as $case) {
|
||||
|
||||
$lnList = preg_split('/\s*,\s*/', (string)$case->ln, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
foreach ($lnList as $ln) {
|
||||
|
||||
// ต้องยาวมากกว่า 2 ถึงจะตัดได้
|
||||
if (strlen($ln) <= 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ✅ ตัด 2 ตัวท้าย (เช่น 76)
|
||||
$requestLn = substr($ln, 0, -2);
|
||||
|
||||
$hisRequestCheck = HisRequest::find()->where(['HN' => (string)$case['h_n']])->andFilterWhere(['like', 'LabNo', (string)$requestLn])->one();
|
||||
if ($hisRequestCheck && isset($hisRequestCheck->testCode)) {
|
||||
$lisResultCheck = LisResult::find()->where(['lab_no' => $hisRequestCheck->LabNo])->one();
|
||||
if (!$lisResultCheck) {
|
||||
|
||||
$hospital_code = Yii::$app->patho->createCommand("select code from const_hospital where id = " . $case['hospital_id'] . "")->queryOne();
|
||||
$filesData = [];
|
||||
$timestamp = date('dmYHis');
|
||||
|
||||
$LisResultUrl = "https://pathology.prolab.co.th/reports/H" . $hospital_code['code'] . "/" . $ln . "_" . $case['h_n'] . "_Prolab-" . $case['id_case'] . ".pdf";
|
||||
//echo $LisResultUrl . "<br>";
|
||||
|
||||
$path = Yii::getAlias('@runtime') . '/report.pdf';
|
||||
|
||||
/*if ($this->downloadFileStream($LisResultUrl, $path)) {
|
||||
echo 'Download completed';
|
||||
} else {
|
||||
echo 'Download failed';
|
||||
}*/
|
||||
|
||||
$pdfContent = $this->fetchUrl($LisResultUrl);
|
||||
if ($pdfContent !== false && strlen($pdfContent) > 0) {
|
||||
$filesData[] = [
|
||||
'FileName' => "{$hisRequestCheck->LabNo}_{$timestamp}.pdf",
|
||||
'Filebase64' => base64_encode($pdfContent),
|
||||
];
|
||||
|
||||
$testList = [
|
||||
'LabNo' => $hisRequestCheck->LabNo ?? '',
|
||||
'RequestRemark' => '',
|
||||
'ResultStatus' => '',
|
||||
'TestList' => [
|
||||
[
|
||||
'TestCode' => (string)($hisRequestCheck->testCode->TestCode ?? ''),
|
||||
'TestName' => (string)($hisRequestCheck->testCode->TestName ?? ''),
|
||||
'TestRemark' => '',
|
||||
'InformCriticalByCode' => '',
|
||||
'InformCriticalBy' => '',
|
||||
'InformCriticalTo' => '',
|
||||
'InformCriticalDateTime' => '',
|
||||
'CriticalFlag' => '',
|
||||
'ReportResultByCode' => '',
|
||||
'ReportResultBy' => (string)($case->pathologist ?? ''),
|
||||
'ReportResultDateTime' => $this->dateTimeFormat($case->report_at ?? ''),
|
||||
'ApproveResultByCode' => '',
|
||||
'ApproveResultBy' => (string)($case->approveBy->realname ?? ''),
|
||||
'ApproveResultDateTime' => $this->dateTimeFormat($case->approve_at ?? ''),
|
||||
'Filedata' => $filesData,
|
||||
'ResultList' => [
|
||||
[
|
||||
'ResultCode' => (string)($hisRequestCheck->testCode->resultCode->ResultCode ?? ''),
|
||||
'ResultName' => (string)($hisRequestCheck->testCode->resultCode->ResultName ?? ''),
|
||||
'ResultValue' => '',
|
||||
'ResultUnit' => '',
|
||||
'ResultFlag' => '',
|
||||
'ReferenceRange' => '',
|
||||
'ResultRemark' => '',
|
||||
]
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
var_dump($testList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error_log("=== Hanuman Result Sikarin Patho END ===");
|
||||
}
|
||||
|
||||
/** helper: ส่ง JSON ผ่าน cURL (คืนข้อมูล debug เต็ม) */
|
||||
protected function postJsonCurl($url, $payload, $headers = [])
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
$jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload);
|
||||
|
||||
// security: allow config to control SSL verify
|
||||
$verifySsl = Yii::$app->params['external_api']['verify_ssl'] ?? true;
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifySsl ? 2 : 0);
|
||||
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
||||
$curlHeaders = ['Content-Type: application/json', 'Accept: application/json', 'Expect:', 'User-Agent: MyLabSender/1.0'];
|
||||
foreach ($headers as $k => $v) {
|
||||
if (is_int($k)) {
|
||||
$curlHeaders[] = $v;
|
||||
} else {
|
||||
$curlHeaders[] = "{$k}: {$v}";
|
||||
}
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlErrno = curl_errno($ch);
|
||||
$curlError = $curlErrno ? curl_error($ch) : null;
|
||||
curl_close($ch);
|
||||
|
||||
return [
|
||||
'http_code' => $httpCode,
|
||||
'response' => $response,
|
||||
'curl_error' => $curlError,
|
||||
'curl_errno' => $curlErrno,
|
||||
'url' => $url,
|
||||
'payload_size' => strlen($jsonPayload),
|
||||
];
|
||||
}
|
||||
|
||||
protected function downloadFileStream($url, $savePath)
|
||||
{
|
||||
$context = stream_context_create([
|
||||
'http' => [
|
||||
'method' => 'GET',
|
||||
'timeout' => 30, // วินาที
|
||||
'header' => "User-Agent: FileFetcher/1.0\r\n",
|
||||
],
|
||||
'ssl' => [
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
],
|
||||
]);
|
||||
|
||||
$read = @fopen($url, 'rb', false, $context);
|
||||
if ($read === false) {
|
||||
error_log("Stream open failed: {$url}");
|
||||
return false;
|
||||
}
|
||||
|
||||
$write = fopen($savePath, 'w+b');
|
||||
if ($write === false) {
|
||||
fclose($read);
|
||||
error_log("Cannot open file for writing: {$savePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
stream_set_timeout($read, 30);
|
||||
|
||||
stream_copy_to_stream($read, $write);
|
||||
|
||||
$meta = stream_get_meta_data($read);
|
||||
|
||||
fclose($read);
|
||||
fclose($write);
|
||||
|
||||
if ($meta['timed_out']) {
|
||||
@unlink($savePath);
|
||||
error_log("Stream timeout while downloading: {$url}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return $savePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* helper: fetch URL via cURL (robust)
|
||||
*/
|
||||
protected function fetchUrl($url)
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
|
||||
CURLOPT_CONNECTTIMEOUT => 600, // 10 วินาที
|
||||
CURLOPT_TIMEOUT => 600, // รวมทั้งหมด 20 วินาที
|
||||
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; PDFFetcher/1.0)',
|
||||
CURLOPT_BUFFERSIZE => 128 * 1024,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/pdf',
|
||||
'Cache-Control: no-cache'
|
||||
],
|
||||
|
||||
// ลดปัญหา IPv6 hang
|
||||
CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
|
||||
]);
|
||||
|
||||
$data = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
$error = curl_error($ch);
|
||||
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
if ($data === false) {
|
||||
error_log("fetchUrl timeout or error: {$url} errno={$errno} err={$error}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($http >= 400) {
|
||||
error_log("fetchUrl http error: {$url} http={$http}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log helper — writes to STDOUT with timestamp and also to Yii log.
|
||||
* $level: 'info'|'warning'|'error'
|
||||
*/
|
||||
protected function logConsole($message, $level = 'info')
|
||||
{
|
||||
$time = date('Y-m-d H:i:s');
|
||||
$line = "[{$time}] {$message}" . PHP_EOL;
|
||||
// write to console
|
||||
$this->stdout($line);
|
||||
// write to Yii logger with appropriate level
|
||||
switch (strtolower($level)) {
|
||||
case 'error':
|
||||
Yii::error($message, __METHOD__);
|
||||
break;
|
||||
case 'warning':
|
||||
Yii::warning($message, __METHOD__);
|
||||
break;
|
||||
default:
|
||||
Yii::info($message, __METHOD__);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate long strings for logging. $maxBytes = null => no truncation.
|
||||
*/
|
||||
protected function truncateForLog($text, $maxBytes = 4096)
|
||||
{
|
||||
if ($text === null) return '';
|
||||
if ($maxBytes === null) return $text;
|
||||
$len = strlen($text);
|
||||
if ($len <= $maxBytes) return $text;
|
||||
$prefix = substr($text, 0, $maxBytes);
|
||||
return $prefix . "\n...TRUNCATED... (original_length={$len})";
|
||||
}
|
||||
|
||||
/** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */
|
||||
public function dateTimeFormat($value)
|
||||
{
|
||||
if (empty($value)) return '';
|
||||
try {
|
||||
$dt = new \DateTime($value);
|
||||
return $dt->format('d/m/Y H:i:s');
|
||||
} catch (\Throwable $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ขอ token จาก API (คืนค่า associative array แบบ {ok, token, expires_at, http_code, raw, error})
|
||||
*/
|
||||
protected function getApiToken($cfg)
|
||||
{
|
||||
$tokenUrl = $cfg['tokenUrl'] ?? Yii::$app->params['external_api']['tokenUrl'] ?? null;
|
||||
$username = $cfg['username'] ?? Yii::$app->params['external_api']['username'] ?? null;
|
||||
$password = $cfg['password'] ?? Yii::$app->params['external_api']['password'] ?? null;
|
||||
|
||||
$payload = ['username' => $username, 'password' => $password];
|
||||
|
||||
$ch = curl_init($tokenUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json']);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$raw = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlErr = curl_errno($ch) ? curl_error($ch) : null;
|
||||
curl_close($ch);
|
||||
|
||||
$result = ['ok' => false, 'token' => null, 'expires_at' => null, 'http_code' => $httpCode, 'raw' => $raw, 'error' => null];
|
||||
|
||||
if ($raw === false || $curlErr) {
|
||||
$result['error'] = 'cURL error: ' . ($curlErr ?? 'unknown');
|
||||
return $result;
|
||||
}
|
||||
|
||||
$decoded = json_decode($raw, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
$result['error'] = 'Invalid JSON response from token endpoint';
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (!empty($decoded['access_token'])) {
|
||||
$result['ok'] = true;
|
||||
$result['token'] = $decoded['access_token'];
|
||||
$expiresIn = intval($decoded['expires_in'] ?? 3600);
|
||||
$result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn);
|
||||
} elseif (!empty($decoded['token'])) {
|
||||
$result['ok'] = true;
|
||||
$result['token'] = $decoded['token'];
|
||||
$expiresIn = intval($decoded['expires_in'] ?? 3600);
|
||||
$result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn);
|
||||
} else {
|
||||
$result['error'] = $decoded['error'] ?? ($decoded['message'] ?? 'No token in response');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* ดึง token ที่ valid (คืน token string หรือ null และตั้ง $tokenError หากมีปัญหา)
|
||||
* พร้อม debug log วันที่หมดอายุของ Token
|
||||
*/
|
||||
protected function getValidApiToken($cfg, &$tokenError = null)
|
||||
{
|
||||
$apiName = $cfg['apiName'] ?? 'gems_api';
|
||||
$tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one();
|
||||
$token = null;
|
||||
$nowTs = time();
|
||||
$refreshMargin = $cfg['refreshMargin'] ?? 300; // 5 นาที
|
||||
|
||||
$needRequest = !$tokenModel || (strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin));
|
||||
|
||||
if ($tokenModel) {
|
||||
//$this->logConsole("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "...");
|
||||
error_log("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "...");
|
||||
} else {
|
||||
error_log("No token found in DB for {$apiName}, will request new token");
|
||||
//$this->logConsole("No token found in DB for {$apiName}, will request new token");
|
||||
}
|
||||
|
||||
if ($needRequest) {
|
||||
$tokenResp = $this->getApiToken($cfg);
|
||||
|
||||
if (empty($tokenResp) || empty($tokenResp['ok'])) {
|
||||
$tokenError = [
|
||||
'message' => 'Failed to get token from API',
|
||||
'tokenUrl' => $cfg['tokenUrl'] ?? null,
|
||||
'error' => $tokenResp['error'] ?? 'Unknown error',
|
||||
'http_code' => $tokenResp['http_code'] ?? 0,
|
||||
'raw' => $tokenResp['raw'] ?? null,
|
||||
];
|
||||
|
||||
if ($tokenModel && !empty($tokenModel->token)) {
|
||||
error_log("Using old token as fallback (expires_at={$tokenModel->expires_at})");
|
||||
//$this->logConsole("Using old token as fallback (expires_at={$tokenModel->expires_at})");
|
||||
return $tokenModel->token; // fallback
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$token = $tokenResp['token'];
|
||||
$expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600);
|
||||
|
||||
if (!$tokenModel) {
|
||||
$tokenModel = new ApiToken();
|
||||
$tokenModel->api_name = $apiName;
|
||||
}
|
||||
|
||||
$tokenModel->token = $token;
|
||||
$tokenModel->expires_at = $expiresAt;
|
||||
|
||||
try {
|
||||
$tokenModel->save(false);
|
||||
error_log("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token ?? '', 0, 15) . "...");
|
||||
//$this->logConsole("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token, 0, 15) . "...");
|
||||
} catch (\Throwable $e) {
|
||||
//$this->logConsole("Error saving new token: " . $e->getMessage(), 'error');
|
||||
error_log("Error saving new token: " . $e->getMessage(), 'error');
|
||||
if ($tokenModel && !empty($tokenModel->token)) {
|
||||
return $tokenModel->token; // fallback
|
||||
}
|
||||
$tokenError = $e->getMessage();
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$token = $tokenModel->token;
|
||||
//$this->logConsole("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "...");
|
||||
error_log("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "...");
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
}
|
||||
|
|
@ -36,7 +36,6 @@ class LisSendPathoController extends Controller
|
|||
|
||||
foreach ($resultPatho as $case) {
|
||||
|
||||
|
||||
$ln = (string)$case['ln'];
|
||||
$requestLn = substr($ln, 0, -2);
|
||||
|
||||
|
|
@ -107,7 +106,7 @@ class LisSendPathoController extends Controller
|
|||
]
|
||||
]
|
||||
];
|
||||
var_dump($testList);
|
||||
//var_dump($testList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue