clqms-be/app/Controllers/Rule/RuleController.php

345 lines
12 KiB
PHP
Raw Normal View History

<?php
namespace App\Controllers\Rule;
use App\Controllers\BaseController;
use App\Models\Rule\RuleActionModel;
use App\Models\Rule\RuleDefModel;
use App\Models\Test\TestDefSiteModel;
use App\Services\RuleExpressionService;
use App\Traits\ResponseTrait;
class RuleController extends BaseController
{
use ResponseTrait;
protected RuleDefModel $ruleDefModel;
protected RuleActionModel $ruleActionModel;
public function __construct()
{
$this->ruleDefModel = new RuleDefModel();
$this->ruleActionModel = new RuleActionModel();
}
public function index()
{
try {
$eventCode = $this->request->getGet('EventCode');
$active = $this->request->getGet('Active');
$scopeType = $this->request->getGet('ScopeType');
$testSiteID = $this->request->getGet('TestSiteID');
$search = $this->request->getGet('search');
$builder = $this->ruleDefModel->where('EndDate', null);
if ($eventCode !== null && $eventCode !== '') {
$builder->where('EventCode', $eventCode);
}
if ($active !== null && $active !== '') {
$builder->where('Active', (int) $active);
}
if ($scopeType !== null && $scopeType !== '') {
$builder->where('ScopeType', $scopeType);
}
if ($testSiteID !== null && $testSiteID !== '' && is_numeric($testSiteID)) {
$builder->where('TestSiteID', (int) $testSiteID);
}
if ($search !== null && $search !== '') {
$builder->like('Name', $search);
}
$rows = $builder
->orderBy('Priority', 'ASC')
->orderBy('RuleID', 'ASC')
->findAll();
return $this->respond([
'status' => 'success',
'message' => 'fetch success',
'data' => $rows,
], 200);
} catch (\Throwable $e) {
log_message('error', 'RuleController::index error: ' . $e->getMessage());
return $this->respond([
'status' => 'failed',
'message' => 'Failed to fetch rules',
'data' => [],
], 500);
}
}
public function show($id = null)
{
try {
if (!$id || !is_numeric($id)) {
return $this->failValidationErrors('RuleID is required');
}
$rule = $this->ruleDefModel->where('EndDate', null)->find((int) $id);
if (!$rule) {
return $this->respond([
'status' => 'failed',
'message' => 'Rule not found',
'data' => [],
], 404);
}
$actions = $this->ruleActionModel
->where('RuleID', (int) $id)
->where('EndDate', null)
->orderBy('Seq', 'ASC')
->orderBy('RuleActionID', 'ASC')
->findAll();
$rule['actions'] = $actions;
return $this->respond([
'status' => 'success',
'message' => 'fetch success',
'data' => $rule,
], 200);
} catch (\Throwable $e) {
log_message('error', 'RuleController::show error: ' . $e->getMessage());
return $this->respond([
'status' => 'failed',
'message' => 'Failed to fetch rule',
'data' => [],
], 500);
}
}
public function create()
{
$input = $this->request->getJSON(true) ?? [];
$validation = service('validation');
$validation->setRules([
'Name' => 'required|max_length[100]',
'EventCode' => 'required|max_length[50]',
'ScopeType' => 'required|in_list[GLOBAL,TESTSITE]',
'TestSiteID' => 'permit_empty|is_natural_no_zero',
'ConditionExpr' => 'permit_empty|max_length[1000]',
'Priority' => 'permit_empty|integer',
'Active' => 'permit_empty|in_list[0,1]',
]);
if (!$validation->run($input)) {
return $this->failValidationErrors($validation->getErrors());
}
if (($input['ScopeType'] ?? 'GLOBAL') === 'TESTSITE') {
if (empty($input['TestSiteID'])) {
return $this->failValidationErrors(['TestSiteID' => 'TestSiteID is required for TESTSITE scope']);
}
$testDef = new TestDefSiteModel();
$exists = $testDef->where('EndDate', null)->find((int) $input['TestSiteID']);
if (!$exists) {
return $this->failValidationErrors(['TestSiteID' => 'TestSiteID not found']);
}
} else {
$input['TestSiteID'] = null;
}
$db = \Config\Database::connect();
$db->transStart();
try {
$ruleData = [
'Name' => $input['Name'],
'Description' => $input['Description'] ?? null,
'EventCode' => $input['EventCode'],
'ScopeType' => $input['ScopeType'],
'TestSiteID' => $input['TestSiteID'] ?? null,
'ConditionExpr' => $input['ConditionExpr'] ?? null,
'Priority' => $input['Priority'] ?? 100,
'Active' => isset($input['Active']) ? (int) $input['Active'] : 1,
];
$ruleID = $this->ruleDefModel->insert($ruleData, true);
if (!$ruleID) {
throw new \Exception('Failed to create rule');
}
if (isset($input['actions']) && is_array($input['actions'])) {
foreach ($input['actions'] as $action) {
if (!is_array($action)) {
continue;
}
$actionType = $action['ActionType'] ?? null;
if (!$actionType) {
continue;
}
$params = $action['ActionParams'] ?? null;
if (is_array($params)) {
$params = json_encode($params);
}
$this->ruleActionModel->insert([
'RuleID' => $ruleID,
'Seq' => $action['Seq'] ?? 1,
'ActionType' => $actionType,
'ActionParams' => is_string($params) ? $params : null,
]);
}
}
$db->transComplete();
if ($db->transStatus() === false) {
throw new \Exception('Transaction failed');
}
return $this->respondCreated([
'status' => 'success',
'message' => 'Rule created successfully',
'data' => ['RuleID' => $ruleID],
], 201);
} catch (\Throwable $e) {
$db->transRollback();
log_message('error', 'RuleController::create error: ' . $e->getMessage());
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function update($id = null)
{
$input = $this->request->getJSON(true) ?? [];
if (!$id || !is_numeric($id)) {
$id = $input['RuleID'] ?? null;
}
if (!$id || !is_numeric($id)) {
return $this->failValidationErrors('RuleID is required');
}
$existing = $this->ruleDefModel->where('EndDate', null)->find((int) $id);
if (!$existing) {
return $this->respond([
'status' => 'failed',
'message' => 'Rule not found',
'data' => [],
], 404);
}
$validation = service('validation');
$validation->setRules([
'Name' => 'permit_empty|max_length[100]',
'EventCode' => 'permit_empty|max_length[50]',
'ScopeType' => 'permit_empty|in_list[GLOBAL,TESTSITE]',
'TestSiteID' => 'permit_empty|is_natural_no_zero',
'ConditionExpr' => 'permit_empty|max_length[1000]',
'Priority' => 'permit_empty|integer',
'Active' => 'permit_empty|in_list[0,1]',
]);
if (!$validation->run($input)) {
return $this->failValidationErrors($validation->getErrors());
}
$scopeType = $input['ScopeType'] ?? $existing['ScopeType'] ?? 'GLOBAL';
$testSiteID = array_key_exists('TestSiteID', $input) ? $input['TestSiteID'] : ($existing['TestSiteID'] ?? null);
if ($scopeType === 'TESTSITE') {
if (empty($testSiteID)) {
return $this->failValidationErrors(['TestSiteID' => 'TestSiteID is required for TESTSITE scope']);
}
$testDef = new TestDefSiteModel();
$exists = $testDef->where('EndDate', null)->find((int) $testSiteID);
if (!$exists) {
return $this->failValidationErrors(['TestSiteID' => 'TestSiteID not found']);
}
} else {
$testSiteID = null;
}
try {
$updateData = [];
foreach (['Name', 'Description', 'EventCode', 'ScopeType', 'ConditionExpr', 'Priority', 'Active'] as $field) {
if (array_key_exists($field, $input)) {
$updateData[$field] = $input[$field];
}
}
$updateData['TestSiteID'] = $testSiteID;
if (!empty($updateData)) {
$this->ruleDefModel->update((int) $id, $updateData);
}
return $this->respond([
'status' => 'success',
'message' => 'Rule updated successfully',
'data' => ['RuleID' => (int) $id],
], 200);
} catch (\Throwable $e) {
log_message('error', 'RuleController::update error: ' . $e->getMessage());
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function delete($id = null)
{
try {
if (!$id || !is_numeric($id)) {
return $this->failValidationErrors('RuleID is required');
}
$existing = $this->ruleDefModel->where('EndDate', null)->find((int) $id);
if (!$existing) {
return $this->respond([
'status' => 'failed',
'message' => 'Rule not found',
'data' => [],
], 404);
}
$this->ruleDefModel->delete((int) $id);
return $this->respondDeleted([
'status' => 'success',
'message' => 'Rule deleted successfully',
'data' => ['RuleID' => (int) $id],
]);
} catch (\Throwable $e) {
log_message('error', 'RuleController::delete error: ' . $e->getMessage());
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function validateExpr()
{
$input = $this->request->getJSON(true) ?? [];
$expr = $input['expr'] ?? '';
$context = $input['context'] ?? [];
if (!is_string($expr) || trim($expr) === '') {
return $this->failValidationErrors(['expr' => 'expr is required']);
}
if (!is_array($context)) {
return $this->failValidationErrors(['context' => 'context must be an object']);
}
try {
$svc = new RuleExpressionService();
$result = $svc->evaluate($expr, $context);
return $this->respond([
'status' => 'success',
'data' => [
'valid' => true,
'result' => $result,
],
], 200);
} catch (\Throwable $e) {
return $this->respond([
'status' => 'failed',
'data' => [
'valid' => false,
'error' => $e->getMessage(),
],
], 200);
}
}
}