prolab-api/api/modules/v1/controllers/HisRequestController.php

374 lines
13 KiB
PHP

<?php
namespace api\modules\v1\controllers;
use api\components\Controller;
use yii\web\NotFoundHttpException;
use Yii;
use common\models\HisRequest;
use api\models\search\HisRequestSearch;
use common\models\HisObr;
use common\models\HisObx;
use OpenApi\Attributes as OA;
#[OA\Tag(name: "HisRequest", description: "Hospital import endpoints")]
class HisRequestController extends Controller
{
/*
#[OA\Get(
path: "/his-request",
summary: "Get list hospital import",
operationId: "hisRequestIndex",
tags: ["HisRequest"],
responses: [
new OA\Response(
response: 200,
description: "OK",
content: new OA\JsonContent(
type: "object",
properties: [
new OA\Property(property: "count", type: "integer", example: 10),
new OA\Property(
property: "dataModels",
type: "array",
items: new OA\Items(ref: "#/components/schemas/HisRequest")
),
new OA\Property(property: "total", type: "integer", example: 100),
]
)
),
new OA\Response(
response: 401,
description: "Unauthorized",
content: new OA\JsonContent(ref: "#/components/schemas/Unauthorized")
),
]
)]
public function actionIndex()
{
$search['HisRequestSearch'] = Yii::$app->request->queryParams;
$searchModel = new HisRequestSearch();
$dataProvider = $searchModel->search($search);
return $this->apiCollection([
'count' => $dataProvider->count,
'dataModels' => $dataProvider->models,
], $dataProvider->totalCount);
}*/
#[OA\Post(
path: "/his-request",
summary: "Create data hospital request API",
operationId: "hisRequestCreate",
tags: ["HisRequest"],
security: [["bearerAuth" => []]],
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(ref: "#/components/schemas/CreateHisRequest")
),
responses: [
new OA\Response(
response: 201,
description: "Created",
content: new OA\JsonContent(ref: "#/components/schemas/HisRequest")
),
new OA\Response(
response: 422,
description: "Validation error",
content: new OA\JsonContent(ref: "#/components/schemas/ErrorValidate")
),
]
)]
public function actionCreate()
{
// ---------- Helper: HL7 DTM converters ----------
$hl7Date = function (?string $s): ?string {
$s = trim((string)$s);
if ($s === '') return null;
$y = substr($s, 0, 4);
$m = substr($s, 4, 2);
$d = substr($s, 6, 2);
if (!checkdate((int)$m, (int)$d, (int)$y)) return null;
return sprintf('%04d-%02d-%02d', $y, $m, $d);
};
$hl7DateTime = function (?string $s): ?string {
$s = trim((string)$s);
if ($s === '') return null;
$y = substr($s, 0, 4);
$m = substr($s, 4, 2);
$d = substr($s, 6, 2);
$H = strlen($s) >= 10 ? substr($s, 8, 2) : '00';
$i = strlen($s) >= 12 ? substr($s, 10, 2) : '00';
$S = strlen($s) >= 14 ? substr($s, 12, 2) : '00';
if (!checkdate((int)$m, (int)$d, (int)$y)) return null;
return sprintf('%04d-%02d-%02d %02d:%02d:%02d', $y, $m, $d, $H, $i, $S);
};
// ---------- Read & normalize body ----------
$root = Yii::$app->request->getBodyParams();
// payload อาจมาเป็น {'HisRequest':{...}} หรือเป็น {...} ตรง ๆ
$reqData = $root['HisRequest'] ?? $root;
//error_log('[HisRequest] INCOMING: ' . print_r($reqData, true));
if (HisRequest::find()->where(['message_control_id' => $reqData['message_control_id'] ?? null])->exists()) {
return $this->apiConflict(
'DUPLICATE_MESSAGE',
'Duplicate message_control_id: ' . ($reqData['message_control_id'] ?? 'unknown'),
['message_control_id' => $reqData['message_control_id'] ?? null]
);
}
$tx = Yii::$app->db->beginTransaction();
try {
// =======================
// 1) SAVE his_request
// =======================
$req = new HisRequest();
//$req->import_at = date('Y-m-d H:i:s');
// แปลง DTM ก่อนเซฟ (ถ้า model ใช้ DATE/DATETIME)
$reqData['request_datetime'] = $hl7DateTime($reqData['request_datetime'] ?? null);
$reqData['date_time_of_message'] = $hl7DateTime($reqData['date_time_of_message'] ?? null);
$reqData['date_time_of_birth'] = $hl7Date($reqData['date_time_of_birth'] ?? null);
$reqData['admit_date_time'] = $hl7DateTime($reqData['admit_date_time'] ?? null);
$reqData['discharge_date_time'] = $hl7DateTime($reqData['discharge_date_time'] ?? null);
$reqData['date_time_of_transaction'] = $hl7DateTime($reqData['date_time_of_transaction'] ?? null);
// เผื่อ message_type แยกแล้ว ใส่ทั้งสาม field
if (!empty($reqData['message_type']) && empty($reqData['message_type_code'])) {
$parts = explode('^', $reqData['message_type']);
$reqData['message_type_code'] = $parts[0] ?? null;
$reqData['message_type_name'] = $parts[1] ?? null;
}
// map ลง model (ให้แน่ใจว่า HisRequest::rules() รับฟิลด์ทั้งหมดเป็น safe หรือกำหนดตามเหมาะสม)
$req->attributes = $reqData;
if (!$req->save()) {
error_log('[HisRequest] ERROR: ' . json_encode($req->errors, JSON_UNESCAPED_UNICODE));
throw new \Exception('Save his_request failed');
}
// =======================
// 2) SAVE his_obr (+ his_obx)
// =======================
$orders = $reqData['orders'] ?? [];
foreach ($orders as $oIdx => $o) {
if (empty($o['obr'])) continue; // ไม่มี OBR ข้าม
$obrIn = $o['obr'];
$obr = new HisObr();
$obr->his_request_id = $req->id;
// แปลง DTM OBR
$obrIn['requested_date_time'] = $hl7DateTime($obrIn['requested_date_time'] ?? null);
// map fields (ตาม schema ล่าสุด)
$mapFields = [
'obr_set_id',
'obr_placer_order_number',
'obr_filler_order_number',
// raw และ flatten ของ universal_service
'universal_service',
'universal_service_id',
'universal_service_text',
'universal_service_name_of_coding_system',
'universal_service_alternate_id',
'universal_service_alternate_text',
'name_of_alternate_coding_system',
'priority',
'requested_date_time',
'relevant_clinical_info',
// specimen
'specimen_source',
'specimen_source_code',
'specimen_source_name',
// ordering provider
'obr_ordering_provider_id',
'obr_ordering_provider_last_name',
'obr_ordering_provider_first_name',
'obr_ordering_provider_middle_name',
'obr_ordering_provider_prefix_name',
// etc.
'placer_field_1',
'ward_code_name',
'charge_to_practice',
];
foreach ($mapFields as $f) {
if (array_key_exists($f, $obrIn)) {
$obr->$f = $obrIn[$f];
}
}
// ถ้ามี ORC ระดับก้อน (ออปชั่น)
if (!empty($o['orc'])) {
$orc = $o['orc'];
$obr->orc_order_control = $orc['order_control'] ?? null;
$obr->orc_placer_no = $orc['orc_placer_order_number'] ?? null;
$obr->orc_filler_no = $orc['orc_filler_order_number'] ?? null;
$obr->orc_status = $orc['order_status'] ?? null;
$obr->orc_datetime_tx = $hl7DateTime($orc['date_time_of_transaction'] ?? null);
$obr->orc_ordering_provider_raw = $orc['orc_ordering_provider'] ?? null;
$obr->orc_ordering_provider_id = $orc['orc_ordering_provider_id'] ?? null;
$obr->orc_ordering_provider_lastname = $orc['orc_ordering_provider_last_name'] ?? null;
$obr->orc_ordering_provider_firstname = $orc['orc_ordering_provider_first_name'] ?? null;
$obr->orc_entering_org = $orc['entering_organization'] ?? null;
$obr->orc_entering_device = $orc['entering_device'] ?? null;
}
if (!$obr->save()) {
error_log('[HisObr] ERROR: ' . json_encode($obr->errors, JSON_UNESCAPED_UNICODE));
throw new \Exception('Save his_obr failed at index ' . $oIdx);
}
// ---------- OBX ----------
$obxArr = $o['obx'] ?? [];
foreach ($obxArr as $xIdx => $x) {
$obx = new HisObx();
$obx->his_obr_id = $obr->id;
// map ชื่อฟิลด์ให้ตรงกับ schema (เราใช้ obx_text, obx_values)
$obx->set_id = $x['set_id'] ?? null;
$obx->value_type = $x['value_type'] ?? null;
$obx->identifier = $x['id'] ?? null;
$obx->obx_text = $x['text'] ?? null;
$obx->coding_system = $x['system'] ?? null;
// values: อาจมาเป็น array หรือ string; ถ้า array ให้ join ด้วย "~"
$values = $x['values'] ?? null;
if (is_array($values)) {
$values = implode('~', $values);
}
$obx->obx_values = $values;
$obx->units = $x['units'] ?? null;
$obx->reference_range = $x['reference_range'] ?? null;
$obx->result_status = $x['result_status'] ?? null;
if (!$obx->save()) {
error_log('[HisObx] ERROR: ' . json_encode($obx->errors, JSON_UNESCAPED_UNICODE));
throw new \Exception('Save his_obx failed at req=' . $req->id . ' obr=' . $obr->id . ' idx=' . $xIdx);
}
}
}
$tx->commit();
// ตอบกลับข้อมูลที่สร้าง (จะส่งเฉพาะ his_request ตามเดิม)
return $this->apiCreated($req, 'CREATE_COMPLETED');
} catch (\Throwable $e) {
$tx->rollBack();
error_log('[HisRequest] ROLLBACK: ' . $e->getMessage());
return $this->apiValidate(['error' => [$e->getMessage()]]);
}
}
// ---------- End of actionCreate ----------
/*
#[OA\Put(
path: "/his-request/{id}",
summary: "Update data hospital import",
operationId: "hisRequestUpdate",
tags: ["HisRequest"],
parameters: [
new OA\Parameter(
name: "id",
in: "path",
required: true,
description: "HisRequest ID",
schema: new OA\Schema(type: "integer", example: 123)
),
],
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(ref: "#/components/schemas/UpdateHisRequest")
),
responses: [
new OA\Response(
response: 202,
description: "Accepted",
content: new OA\JsonContent(ref: "#/components/schemas/HisRequest")
),
new OA\Response(
response: 422,
description: "Validation error",
content: new OA\JsonContent(ref: "#/components/schemas/ErrorValidate")
),
new OA\Response(response: 404, description: "Not found"),
]
)]
public function actionUpdate($id)
{
$dataRequest['HisRequest'] = Yii::$app->request->getBodyParams();
$model = $this->findModel($id);
if ($model->load($dataRequest) && $model->save()) {
return $this->apiUpdated($model);
}
return $this->apiValidate($model->errors);
}
#[OA\Get(
path: "/his-request/{id}",
summary: "Get data hospital import",
operationId: "hisRequestView",
tags: ["HisRequest"],
parameters: [
new OA\Parameter(
name: "id",
in: "path",
required: true,
schema: new OA\Schema(type: "integer", example: 123)
),
],
responses: [
new OA\Response(
response: 200,
description: "OK",
content: new OA\JsonContent(ref: "#/components/schemas/HisRequest")
),
new OA\Response(response: 404, description: "Not found"),
]
)]
public function actionView($id)
{
return $this->apiItem($this->findModel($id));
}
#[OA\Delete(
path: "/his-request/{id}",
summary: "Delete data hospital import",
operationId: "hisRequestDelete",
tags: ["HisRequest"],
parameters: [
new OA\Parameter(
name: "id",
in: "path",
required: true,
schema: new OA\Schema(type: "integer", example: 123)
),
],
responses: [
new OA\Response(response: 202, description: "Accepted"),
new OA\Response(response: 404, description: "Not found"),
]
)]
public function actionDelete($id)
{
if ($this->findModel($id)->delete()) {
return $this->apiDeleted(true);
}
return $this->apiDeleted(false);
}
protected function findModel($id)
{
if (($model = HisRequest::findOne($id)) !== null) {
return $model;
}
throw new NotFoundHttpException('Resource not found');
}
*/
}