trafficmonitor init...1

This commit is contained in:
choi.jh 2025-11-14 17:04:12 +09:00
parent b05dfed46c
commit 102ba52c16
18 changed files with 352 additions and 203 deletions

View File

@ -4,7 +4,6 @@ namespace App\Controllers\Admin;
use App\Controllers\CommonController;
use App\DTOs\CommonDTO;
use App\Entities\CommonEntity;
use App\Traits\LogTrait;
use CodeIgniter\HTTP\RedirectResponse;
@ -27,7 +26,6 @@ abstract class AdminController extends CommonController
parent::initController($request, $response, $logger);
$this->addActionPaths(self::PATH);
}
abstract protected function createDTO(array $formDatas): CommonDTO;
final protected function getLayout(): string
{
return 'admin';
@ -47,7 +45,7 @@ abstract class AdminController extends CommonController
$this->addViewDatas('formRules', $this->service->getFormService()->getFormRules());
$this->addViewDatas('formFilters', $this->service->getFormService()->getFormFilters());
$this->addViewDatas('formOptions', $this->service->getFormService()->getFormOptions());
$this->addViewDatas('index_actionButtons', $this->service->getFormService()->getActionButtons());
$this->addViewDatas('index_actionButtons', $this->service->getFormService()->getactionButtons());
$this->addViewDatas('index_batchjobFields', $this->service->getFormService()->getBatchjobFilters());
$this->addViewDatas('index_batchjobButtons', $this->service->getFormService()->getBatchjobButtons());
parent::action_init_process($action);
@ -72,7 +70,7 @@ abstract class AdminController extends CommonController
protected function create_process(): CommonEntity
{
//요청 데이터를 DTO 객체로 변환
$dto = $this->createDTO($this->request->getPost());
$dto = $this->service->createDTO($this->request->getPost());
return $this->service->create($dto);
}
final public function create(): string|RedirectResponse
@ -113,7 +111,7 @@ abstract class AdminController extends CommonController
protected function modify_process($uid): CommonEntity
{
//요청 데이터를 DTO 객체로 변환
$dto = $this->createDTO($this->request->getPost());
$dto = $this->service->createDTO($this->request->getPost());
return $this->service->modify($uid, $dto);
}
final public function modify($uid): string|RedirectResponse
@ -128,46 +126,64 @@ abstract class AdminController extends CommonController
return $this->action_redirect_process('error', "{$this->getTitle()}에서 {$uid} 수정 오류:" . $e->getMessage());
}
}
protected function batchjob_process($uid, array $formDatas): CommonEntity
{
return $this->service->batchjob($uid, $formDatas);
}
final public function batchjob(): string|RedirectResponse
{
try {
$action = __FUNCTION__;
$this->action_init_process($action);
$selectedFields = [];
$postDatas = $this->request->getPost();
//1. postDatas에서 선택된 uids 정보 추출
$uids = [];
if (isset($postDatas['batchjob_uids'])) {
$uids = $postDatas['batchjob_uids'];
unset($postDatas['batchjob_uids']);
}
if (empty($uids)) {
throw new \Exception("적용할 리스트을 선택하셔야합니다.");
}
//2. postDatas에서 필요없는 정보 버림
unset($postDatas['batchjob_submit']);
//3. 나머지는 $formDatas로 사용할 데이터 추출
$formDatas = [];
foreach ($this->service->getFormService->getBatchjobFilters() as $field) {
$value = $this->request->getPost($field);
if ($value) {
$selectedFields[] = $field;
foreach ($postDatas as $field => $value) {
if ($value !== "") {
$formDatas[$field] = $value;
}
}
if (!count($selectedFields)) {
if (empty($formDatas)) {
throw new \Exception("변경할 조건항목을 선택하셔야합니다.");
}
//변경할 UIDS 정의
$uids = $this->request->getPost('batchjob_uids[]');
if (!is_array($uids) || !count($uids)) {
throw new \Exception("적용할 리스트을 선택하셔야합니다.");
}
//4. 데이터가 있는 필드 추출
$selectedFields = array_keys($formDatas);
//초기화
$this->service->getFormService()->setFormFields($selectedFields);
$this->service->getFormService()->setFormRules($action, $selectedFields);
$this->service->getFormService()->setFormRules(__FUNCTION__, $selectedFields);
$this->service->getFormService()->setFormFilters($selectedFields);
$this->service->getFormService()->setFormOptions($selectedFields);
$entities = [];
$errors = [];
$error = 0;
foreach ($uids as $uid) {
try {
$entities[] = $this->modify_process($uid);
$entities[] = $this->batchjob_process($uid, $formDatas);
} catch (ValidationException $e) {
$errors[] = "{$this->getTitle()}에서 {$uid} 수정 검증오류:" . $e->getMessage();
log_message('error', "{$this->getTitle()}에서 {$uid} 수정 검증오류:" . $e->getMessage());
$error++;
} catch (\Exception $e) {
$errors[] = "{$this->getTitle()}에서 {$uid} 수정 오류:" . $e->getMessage();
log_message('error', "{$this->getTitle()}에서 {$uid} 수정 오류:" . $e->getMessage());
$error++;
}
}
return $this->action_modal_process("{$this->getTitle()}에서 " . count($entities) . "개, 총:" . count($uids) . " 수정이 완료되었습니다.");
return $this->action_redirect_process('info', sprintf(
"%s에서 %s개 처리완료, %s개 오류, 총:%s개 수정이 완료되었습니다.",
$this->getTitle(),
count($entities),
$error,
count($uids)
));
} catch (\Exception $e) {
return $this->action_redirect_process('error', "{$this->getTitle()}에서 {$uid} 수정 오류:" . $e->getMessage());
return $this->action_redirect_process('error', "{$this->getTitle()}에서 일괄작업처리 오류:" . $e->getMessage());
}
}
protected function delete_process($uid): CommonEntity
@ -183,6 +199,44 @@ abstract class AdminController extends CommonController
return $this->action_redirect_process('error', "{$this->getTitle()}에서 {$uid} 삭제 오류:" . $e->getMessage());
}
}
protected function batchjob_delete_process($uid): CommonEntity
{
return $this->service->batchjob_delete($uid);
}
final public function batchjob_delete(): string|RedirectResponse
{
try {
$postDatas = $this->request->getPost();
//1. postDatas에서 선택된 uids 정보 추출
$uids = [];
if (isset($postDatas['batchjob_uids'])) {
$uids = $postDatas['batchjob_uids'];
unset($postDatas['batchjob_uids']);
}
if (empty($uids)) {
throw new \Exception("삭제할 리스트을 선택하셔야합니다.");
}
$entities = [];
$error = 0;
foreach ($uids as $uid) {
try {
$entities[] = $this->batchjob_delete_process($uid);
} catch (\Exception $e) {
log_message('error', "{$this->getTitle()}에서 {$uid} 삭제 오류:" . $e->getMessage());
$error++;
}
}
return $this->action_redirect_process('info', sprintf(
"%s에서 %s개 처리완료, %s개 오류, 총:%s개 일괄삭제가 완료되었습니다.",
$this->getTitle(),
count($entities),
$error,
count($uids)
));
} catch (\Exception $e) {
return $this->action_redirect_process('error', "{$this->getTitle()}에서 일괄삭제 오류:" . $e->getMessage());
}
}
protected function view_process($uid): CommonEntity
{
if (!$uid) {

View File

@ -2,7 +2,6 @@
namespace App\Controllers\Admin;
use App\DTOs\CollectorDTO;
use App\Entities\CollectorEntity;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
@ -19,14 +18,13 @@ class CollectorController extends AdminController
}
$this->addActionPaths(self::PATH);
}
protected function createDTO(array $formDatas): CollectorDTO
{
return new CollectorDTO($formDatas);
}
protected function action_init_process(string $action): void
{
$fields = ['trafficinfo_uid', 'in', 'out', 'raw_in', 'raw_out'];
$filters = ['trafficinfo_uid'];
$batchjobFilters = $filters;
$actionButtons = ['view' => ICONS['SEARCH']];
$batchjobButtons = [];
parent::action_init_process($action);
switch ($action) {
case 'create':
@ -49,7 +47,9 @@ class CollectorController extends AdminController
$this->service->getFormService()->setFormRules($action, $fields);
$this->service->getFormService()->setFormFilters($filters);
$this->service->getFormService()->setFormOptions($filters);
$this->service->getFormService()->setBatchjobFilters($filters);
$this->service->getFormService()->setBatchjobFilters($batchjobFilters);
$this->service->getFormService()->setActionButtons($actionButtons);
$this->service->getFormService()->setBatchjobButtons($batchjobButtons);
parent::action_init_process($action);
}
protected function create_process(): CollectorEntity
@ -72,4 +72,8 @@ class CollectorController extends AdminController
{
return parent::view_process($uid);
}
protected function batchjob_process($uid, array $formDatas): CollectorEntity
{
return parent::batchjob_process($uid, $formDatas);
}
}

View File

@ -2,7 +2,6 @@
namespace App\Controllers\Admin;
use App\DTOs\MylogDTO;
use App\Entities\MylogEntity;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
@ -19,14 +18,11 @@ class MylogController extends AdminController
}
$this->addActionPaths(self::PATH);
}
protected function createDTO(array $formDatas): MylogDTO
{
return new MylogDTO($formDatas);
}
protected function action_init_process(string $action): void
{
$fields = ['title', 'content'];
$filters = [];
$batchjobFilters = $filters;
parent::action_init_process($action);
switch ($action) {
case 'create':
@ -49,7 +45,7 @@ class MylogController extends AdminController
$this->service->getFormService()->setFormRules($action, $fields);
$this->service->getFormService()->setFormFilters($filters);
$this->service->getFormService()->setFormOptions($filters);
$this->service->getFormService()->setBatchjobFilters($filters);
$this->service->getFormService()->setBatchjobFilters($batchjobFilters);
parent::action_init_process($action);
}
protected function create_process(): MylogEntity
@ -72,4 +68,8 @@ class MylogController extends AdminController
{
return parent::view_process($uid);
}
protected function batchjob_process($uid, array $formDatas): MylogEntity
{
return parent::batchjob_process($uid, $formDatas);
}
}

View File

@ -2,7 +2,6 @@
namespace App\Controllers\Admin;
use App\DTOs\TrafficDTO;
use App\Entities\TrafficEntity;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
@ -19,14 +18,12 @@ class TrafficController extends AdminController
}
$this->addActionPaths(self::PATH);
}
protected function createDTO(array $formDatas): TrafficDTO
{
return new TrafficDTO($formDatas);
}
protected function action_init_process(string $action): void
{
$fields = ['client', 'server', 'switch', 'server_ip', 'interface', 'ip', 'status'];
$filters = ['status'];
$batchjobFilters = $filters;
$actionButtons = ['view' => ICONS['SEARCH'], 'dashboard' => ICONS['CHART'], 'delete' => ICONS['DELETE']];
switch ($action) {
case 'create':
case 'create_form':
@ -48,7 +45,8 @@ class TrafficController extends AdminController
$this->service->getFormService()->setFormRules($action, $fields);
$this->service->getFormService()->setFormFilters($filters);
$this->service->getFormService()->setFormOptions($filters);
$this->service->getFormService()->setBatchjobFilters($filters);
$this->service->getFormService()->setBatchjobFilters($batchjobFilters);
$this->service->getFormService()->setActionButtons($actionButtons);
parent::action_init_process($action);
}
protected function create_process(): TrafficEntity
@ -71,6 +69,10 @@ class TrafficController extends AdminController
{
return parent::view_process($uid);
}
protected function batchjob_process($uid, array $formDatas): TrafficEntity
{
return parent::batchjob_process($uid, $formDatas);
}
public function dashboard($uid): string
{
$entity = $this->service->getEntity($uid);

View File

@ -2,7 +2,6 @@
namespace App\Controllers\Admin;
use App\DTOs\UserDTO;
use App\Entities\UserEntity;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
@ -19,15 +18,12 @@ class UserController extends AdminController
}
$this->addActionPaths(self::PATH);
}
protected function createDTO(array $formDatas): UserDTO
{
return new UserDTO($formDatas);
}
//Action작업관련
protected function action_init_process(string $action): void
{
$fields = ['id', 'passwd', 'confirmpassword', 'name', 'email', 'mobile', 'role'];
$filters = ['role', 'status'];
$batchjobFilters = ['status'];
switch ($action) {
case 'create':
case 'create_form':
@ -49,7 +45,7 @@ class UserController extends AdminController
$this->service->getFormService()->setFormRules($action, $fields);
$this->service->getFormService()->setFormFilters($filters);
$this->service->getFormService()->setFormOptions($filters);
$this->service->getFormService()->setBatchjobFilters($filters);
$this->service->getFormService()->setBatchjobFilters($batchjobFilters);
parent::action_init_process($action);
}
protected function create_form_process(array $formDatas = []): array
@ -77,4 +73,8 @@ class UserController extends AdminController
{
return parent::view_process($uid);
}
protected function batchjob_process($uid, array $formDatas): UserEntity
{
return parent::batchjob_process($uid, $formDatas);
}
}

View File

@ -4,10 +4,11 @@ namespace App\DTOs\Auth;
use App\DTOs\CommonDTO;
class AuthDTO extends CommonDTO
abstract class AuthDTO extends CommonDTO
{
public function __construct()
{
parent::__construct();
}
abstract public function toArray();
}

View File

@ -9,7 +9,6 @@ class CollectorForm extends CommonForm
public function __construct()
{
parent::__construct();
$this->setActionButtons(['view' => ICONS['SEARCH']]);
}
public function getFormRule(string $action, string $field): string

View File

@ -36,39 +36,32 @@ abstract class CommonForm
//$fields 매치된것만 반환, []->전체
final public function getFormFields(array $fields = []): array
{
// 1. $fields 배열이 비어있는지 확인합니다.
// getFormFields('create') 와 같이 호출될 경우 $fields는 빈 배열[]이 됩니다.
if (empty($fields)) {
// 비어있다면, _formFields 전체를 반환합니다.
return $this->_formFields;
}
// $fields의 값(원하는 필드명)을 키로 변환합니다.
// 예: ['test1'] -> ['test1' => 0]
$fieldsAsKeys = array_flip($fields);
// _formFields와 $fieldsAsKeys 간의 키를 비교하여 교집합을 반환합니다.
// 즉, $fields에 지정된 필드 정의만 추출됩니다.
return array_intersect_key($this->_formFields, $fieldsAsKeys);
// _formFields와 키를 비교하여 교집합을 반환합니다. $fields에 지정된 필드 정의만 추출됩니다.
return array_intersect_key($this->_formFields, array_flip($fields));
}
final public function setFormRules(string $action, array $fields): void
final public function setFormRules(string $action, array $fields, $formRules = []): void
{
foreach ($fields as $field) {
$this->_formRules[$field] = $this->getFormRule($action, $field);
$formRules[$field] = $formRules[$field] ?? $this->getFormRule($action, $field);
}
$this->_formRules = $formRules;
}
//$fields 매치된것만 반환, []->전체
final public function getFormRules(array $fields = []): array
{
if (empty($fields)) {
return $this->_formRules;
}
$fieldsAsKeys = array_flip($fields);
return array_intersect_key($this->_formRules, $fieldsAsKeys);
return array_intersect_key($this->_formRules, array_flip($fields));
}
final public function setFormOptions(array $fields): void
final public function setFormOptions(array $fields, $formOptions = []): void
{
foreach ($fields as $field) {
$this->_formOptions[$field] = $this->getFormOption($field);
$formOptions[$field] = $formOptions[$field] ?? $this->getFormOption($field);
}
$this->_formOptions = $formOptions;
// dd($this->_formOptions[$field]);
}
//$fields 매치된것만 반환, []->전체
@ -77,8 +70,7 @@ abstract class CommonForm
if (empty($fields)) {
return $this->_formOptions;
}
$fieldsAsKeys = array_flip($fields);
return array_intersect_key($this->_formOptions, $fieldsAsKeys);
return array_intersect_key($this->_formOptions, array_flip($fields));
}
final public function setFormFilters(array $fields): void
{
@ -96,17 +88,17 @@ abstract class CommonForm
{
return $this->_batchjobFilters;
}
final public function setActionButtons(array $buttions): array
final public function setActionButtons(array $buttons): array
{
return $this->_actionButtons = $buttions;
return $this->_actionButtons = $buttons;
}
final public function getActionButtons(): array
final public function getactionButtons(): array
{
return $this->_actionButtons;
}
final public function setBatchjobButtons(array $buttions): array
final public function setBatchjobButtons(array $buttons): array
{
return $this->_batchjobButtons = $buttions;
return $this->_batchjobButtons = $buttons;
}
final public function getBatchjobButtons(): array
{

View File

@ -9,7 +9,6 @@ class TrafficForm extends CommonForm
public function __construct()
{
parent::__construct();
$this->setActionButtons(['view' => ICONS['SEARCH'], 'dashboard' => ICONS['CHART'], 'delete' => ICONS['DELETE']]);
}
public function getFormRule(string $action, string $field): string
{

View File

@ -0,0 +1,37 @@
<?php
namespace App\Libraries;
class SNMPLibrary
{
private $_ip = null;
private $_community = null;
private $_version = '2c';
public function __construct(string $ip, string $community, ?string $version = null)
{
$this->_ip = $ip;
$this->_community = $community;
}
public function get(string $fullOid): ?int
{
// snmp2_get을 사용하여 SNMP v2c로 요청
// 💡 snmp2_get() 함수가 존재하지 않는다는 LSP 오류를 무시하기 위해 @suppress 태그 사용
/** @phpstan-ignore-next-line */
$result = @snmp2_get($this->_ip, $this->_community, $fullOid, 100000, 3);
if ($result === false || $result === null) {
log_message('error', "SNMP 통신 실패: {$this->_ip} ({$fullOid}, Community: {$this->_community})");
return null;
}
// 💡 정규식 수정: /\d+$/ (문자열 끝의 숫자만 추출)
if (preg_match('/\d+$/', $result, $matches)) {
// SNMP Counter는 64비트 BigInt가 될 수 있으므로, 64비트 PHP 환경에서는
// (int) 캐스팅을 사용하여 안전하게 64비트 정수로 변환합니다.
// (BigInt 값을 DB에 저장할 때도 PHP의 정수형/문자열 처리 능력이 중요합니다.)
return (int)$matches[0];
}
return null;
}
}

View File

@ -3,6 +3,7 @@
namespace App\Services\Auth;
use App\DTOs\Auth\AuthDTO;
use App\Entities\CommonEntity;
use App\Entities\UserEntity;
use App\Helpers\AuthHelper;
use App\Libraries\AuthContext;
@ -56,10 +57,7 @@ abstract class AuthService
{
return $isArray ? $this->_classPaths : implode($delimeter, $this->_classPaths);
}
protected function getEntity_process(mixed $entity): mixed
{
return $entity;
}
abstract protected function getEntity_process(mixed $entity): CommonEntity;
final public function getEntity(string|int|array $where, ?string $message = null): mixed
{
try {
@ -100,7 +98,7 @@ abstract class AuthService
abstract protected function login_process(array $formDatas): UserEntity;
public function login(AuthDTO $dto): UserEntity
{
$formDatas = (array)$dto;
$formDatas = $dto->toArray();
// dd($formDatas);
//입력값 검증
if (!$this->getFormService()->validate($formDatas)) {

View File

@ -33,6 +33,10 @@ class GoogleService extends AuthService
}
return $this->_form;
}
protected function getEntity_process(mixed $entity): UserEntity
{
return $entity;
}
//기본기능
protected function login_process(array $formDatas): UserEntity
{

View File

@ -33,6 +33,10 @@ class LocalService extends AuthService
}
return $this->_form;
}
protected function getEntity_process(mixed $entity): UserEntity
{
return $entity;
}
protected function login_process(array $formDatas): UserEntity
{
$entity = $this->getEntity(['id' => $formDatas['id'], 'status' => 'AVAILABLE'], true);

View File

@ -7,30 +7,27 @@ use App\Entities\CollectorEntity;
use App\Entities\TrafficEntity;
use App\Forms\CollectorForm;
use App\Helpers\CollectorHelper;
use App\Libraries\SNMPLibrary;
use App\Models\CollectorModel;
use CodeIgniter\I18n\Time;
use RuntimeException;
class CollectorService extends CommonService
{
const OID_IF_IN_OCTETS = '1.3.6.1.2.1.31.1.1.1.6.';
const OID_IF_OUT_OCTETS = '1.3.6.1.2.1.31.1.1.1.10.';
private $_form = null;
private $_helper = null;
// 💡 64비트 카운터를 위해 High Capacity OID로 변경: ifHCInOctets
const OID_IF_IN_OCTETS = '1.3.6.1.2.1.31.1.1.1.6.';
// 💡 64비트 카운터를 위해 High Capacity OID로 변경: ifHCOutOctets
const OID_IF_OUT_OCTETS = '1.3.6.1.2.1.31.1.1.1.10.';
const SNMP_VERSION = '2c';
// 💡 32비트 롤오버 로직을 제거했으므로 해당 상수는 필요 없습니다.
// const MAX_COUNTER_32BIT = 4294967296;
public function __construct(CollectorModel $model)
{
parent::__construct($model);
$this->addClassPaths('Collector');
}
public function createDTO(array $formDatas): CollectorDTO
{
return new CollectorDTO($formDatas);
}
public function getFormService(): CollectorForm
{
if ($this->_form === null) {
@ -60,8 +57,15 @@ class CollectorService extends CommonService
}
return $this->_helper;
}
//기본 기능부분
protected function getEntity_process(mixed $entity): CollectorEntity
{
return $entity;
}
protected function create_process(array $formDatas): CollectorEntity
{
return new CollectorEntity($formDatas);
}
public function create(object $dto): CollectorEntity
{
if (!$dto instanceof CollectorDTO) {
@ -69,7 +73,10 @@ class CollectorService extends CommonService
}
return parent::create($dto);
}
protected function modify_process($uid, array $formDatas): CollectorEntity
{
return parent::modify_process($uid, $formDatas);
}
public function modify($uid, object $dto): CollectorEntity
{
if (!$dto instanceof CollectorDTO) {
@ -97,51 +104,23 @@ class CollectorService extends CommonService
return $entities;
}
//SNMP연결
private function getSNMPOctets(TrafficEntity $trafficEntity, string $oid): ?int
{
$fullOid = $oid . $trafficEntity->getInterface();
$community = $trafficEntity->getCommunity();
$ip = $trafficEntity->getIP();
// snmp2_get을 사용하여 SNMP v2c로 요청
// 💡 snmp2_get() 함수가 존재하지 않는다는 LSP 오류를 무시하기 위해 @suppress 태그 사용
/** @phpstan-ignore-next-line */
$result = @snmp2_get($ip, $community, $fullOid, 100000, 3);
if ($result === false || $result === null) {
log_message('error', "SNMP 통신 실패: {$ip} ({$fullOid}, Community: {$community})");
return null;
}
// 💡 정규식 수정: /\d+$/ (문자열 끝의 숫자만 추출)
if (preg_match('/\d+$/', $result, $matches)) {
// SNMP Counter는 64비트 BigInt가 될 수 있으므로, 64비트 PHP 환경에서는
// (int) 캐스팅을 사용하여 안전하게 64비트 정수로 변환합니다.
// (BigInt 값을 DB에 저장할 때도 PHP의 정수형/문자열 처리 능력이 중요합니다.)
return (int)$matches[0];
}
return null;
}
public function getCalculatedData(TrafficEntity $trafficEntity): array
{
$currentInOctets = $this->getSNMPOctets($trafficEntity, self::OID_IF_IN_OCTETS);
$currentOutOctets = $this->getSNMPOctets($trafficEntity, self::OID_IF_OUT_OCTETS);
$snmp = new SNMPLibrary($trafficEntity->getIP(), $trafficEntity->getCommunity());
$currentInOctets = $snmp->get(self::OID_IF_IN_OCTETS . $trafficEntity->getInterface());
$currentOutOctets = $snmp->get(self::OID_IF_OUT_OCTETS . $trafficEntity->getInterface());
if ($currentInOctets === null || $currentOutOctets === null) {
$message = "트래픽 수집 실패: {$trafficEntity->getIP()} - IF{$trafficEntity->getInterface()} (UID: {$trafficEntity->getPK()})";
log_message('warning', $message);
log_message('error', $message);
throw new \Exception($message);
}
// 이전 데이터를 조회하여 Rate 계산에 사용
// $this->model은 TrafficDataModel의 인스턴스라고 가정
$lastEntity = $this->model->getLastEntity($trafficEntity->getPK());
$inKbitsSec = 0.0;
$outKbitsSec = 0.0;
// 이전 데이터가 있어야만 Rate 계산 가능
if ($lastEntity !== null) {
$lastTime = Time::parse($lastEntity->getCreatedAt())->getTimestamp();
@ -153,28 +132,18 @@ class CollectorService extends CommonService
$lastIn = $lastEntity->getRawIn();
$lastOut = $lastEntity->getRawOut();
// 💡 1. 인바운드 Octets 차분 계산 (32비트 롤오버 로직 제거)
// 64비트 카운터(BIGINT)를 사용하기 때문에 단순 뺄셈으로 처리합니다.
// 64비트 카운터는 실질적으로 롤오버될 일이 없습니다.
// 💡 1. IN/OUT바운드 Octets 차분 계산 64비트 카운터(BIGINT)를 사용하기 때문에 단순 뺄셈으로 처리합니다.
$deltaInOctets = $currentInOctets - $lastIn;
// 💡 2. 아웃바운드 Octets 차분 계산 (32비트 롤오버 로직 제거)
// 64비트 카운터(BIGINT)를 사용하기 때문에 단순 뺄셈으로 처리합니다.
$deltaOutOctets = $currentOutOctets - $lastOut;
// Kbit/s 계산: (Delta_Octets * 8 bits) / Delta_Time_Seconds / 1000 (-> Kbit/s)
// 실수(float) 연산으로 정확도를 높입니다.
// Kbit/s 계산: (Delta_Octets * 8 bits) / Delta_Time_Seconds / 1000 (-> Kbit/s) 실수(float) 연산으로 정확도를 높입니다.
$inKbitsSec = ($deltaInOctets * 8.0) / $deltaTime / 1000.0;
$outKbitsSec = ($deltaOutOctets * 8.0) / $deltaTime / 1000.0;
} else {
log_message('error', "시간 차이 오류 발생: {$trafficEntity->getIP()} - {$deltaTime}초 (UID: {$trafficEntity->getPK()})");
}
}
// DB에 저장할 데이터를 배열로 반환
return [
// 'raw_in'과 'raw_out'은 이제 64비트 정수(BIGINT)를 담습니다.
// PHP에서 (int) 캐스팅은 64비트 환경에서 64비트 정수를 의미합니다.
'trafficinfo_uid' => (int)$trafficEntity->getPK(),
'in' => (int)$inKbitsSec, // 정수형으로 반환
'out' => (int)$outKbitsSec, // 정수형으로 반환

View File

@ -2,6 +2,7 @@
namespace App\Services;
use App\DTOs\CommonDTO;
use App\Entities\CommonEntity;
use App\Models\CommonModel;
use CodeIgniter\Database\Exceptions\DatabaseException;
@ -13,6 +14,7 @@ abstract class CommonService
private array $_classPaths = [];
protected $title = null;
protected function __construct(protected CommonModel $model) {}
abstract public function createDTO(array $formDatas): CommonDTO;
abstract public function getFormService(): mixed;
abstract public function getHelper(): mixed;
final protected function addClassPaths(string $path): void
@ -24,18 +26,22 @@ abstract class CommonService
return $isArray ? $this->_classPaths : implode($delimeter, $this->_classPaths);
}
final public function getEntity(string|int|array $where, ?string $message = null): mixed
/**
* 단일 엔티티를 조회합니다.
* @return CommonEntity|null CommonEntity 인스턴스 또는 찾지 못했을 경우 null
*/
final public function getEntity(string|int|array $where, ?string $message = null): ?CommonEntity
{
try {
$entity = is_array($where) ? $this->model->where($where)->first() : $this->model->find($where);
if (!$entity) {
return null;
}
if (is_array($entity)) {
throw new \Exception(__METHOD__ . "에서 결과값 Array 오류발생:\n" . var_export($entity, true));
if (!$entity instanceof CommonEntity) {
throw new \Exception(__METHOD__ . "에서 결과값 오류발생:\n" . var_export($entity, true));
}
return $this->getEntity_process($entity);
} catch (DatabaseException $e) { // 💡 DB 오류를 명시적으로 잡음
} catch (DatabaseException $e) {
$errorMessage = sprintf(
"\n------DB Query 오류 (%s)-----\nQuery: %s\nError: %s\n------------------------------\n",
__FUNCTION__,
@ -44,7 +50,7 @@ abstract class CommonService
);
log_message('error', $errorMessage);
throw new RuntimeException($errorMessage, $e->getCode(), $e);
} catch (\Exception $e) { // 기타 일반적인 예외 처리
} catch (\Exception $e) {
$errorMessage = sprintf(
"\n------일반 오류 (%s)-----\nError: %s\n------------------------------\n",
__FUNCTION__,
@ -59,7 +65,7 @@ abstract class CommonService
$entities = $this->getEntities_process($where, $columns);
log_message('debug', $this->model->getLastQuery());
return $entities;
} catch (DatabaseException $e) { // 💡 DB 오류를 명시적으로 잡음
} catch (DatabaseException $e) {
$errorMessage = sprintf(
"\n------DB Query 오류 (%s)-----\nQuery: %s\nError: %s\n------------------------------\n",
__FUNCTION__,
@ -68,7 +74,7 @@ abstract class CommonService
);
log_message('error', $errorMessage);
throw new RuntimeException($errorMessage, $e->getCode(), $e);
} catch (\Exception $e) { // 기타 일반적인 예외 처리
} catch (\Exception $e) {
$errorMessage = sprintf(
"\n------일반 오류 (%s)-----\nError: %s\n------------------------------\n",
__FUNCTION__,
@ -76,19 +82,17 @@ abstract class CommonService
);
throw new \Exception($errorMessage, $e->getCode(), $e);
}
} //
}
final public function getLatestPK(): int
final public function getNextPK(): int
{
$pkField = $this->model->getPKField();
// $this->model->selectMax($pkField)->get()->getRow() 대신 row() 사용 권장
$row = $this->model->selectMax($pkField)->get()->getRow();
// uid 대신 PK 필드명을 동적으로 사용
return isset($row->{$pkField}) ? ((int)$row->{$pkField} + 1) : 1;
}
//Entity관련
protected function getEntity_process(mixed $entity): mixed
protected function getEntity_process(CommonEntity $entity): CommonEntity
{
return $entity;
}
@ -99,11 +103,7 @@ abstract class CommonService
if ($where) {
$this->model->where($where);
}
//출력순서 정의
$this->setOrderBy();
$entities = [];
// findAll()이 DB 오류 없이 실행되었다면 문제 없음
foreach ($this->model->select(implode(',', $columns))->findAll() as $entity) {
$entities[$entity->getPK()] = $this->getEntity_process($entity);
}
@ -111,100 +111,148 @@ abstract class CommonService
}
//CURD 결과처리용
//DB 결과 처리 로직 통합 및 개선
protected function handle_save_result(mixed $result, ?int $uid = null): CommonEntity
protected function handle_save_result(mixed $result, int|string $uid): int|string
{
//최종 PK 값 결정 (insert/update 공통)
$pk = $this->model->useAutoIncrement() && is_numeric($result) && (int)$result > 0 ? (int)$result : $uid;
if (empty($pk)) {
// 모델의 insert/update가 실패했을 경우 에러 메시지를 포함하여 RuntimeException을 던집니다.
$errors = $this->model->errors();
$errorMsg = is_array($errors) && !empty($errors) ? implode(", ", $errors) : "DB 작업 성공 후 PK를 확인할 수 없거나 모델 오류 발생.";
throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg);
}
$entity = $this->getEntity($pk);
if (!$entity instanceof CommonEntity) {
throw new RuntimeException(__METHOD__ . "에서 오류발생: {$pk}에 해당하는 정보를 찾을수 없습니다.");
}
return $entity;
}
//생성용
protected function create_process(array $formDatas): CommonEntity
{
$result = $this->model->insert($formDatas, $this->model->useAutoIncrement());
log_message('debug', $this->model->getLastQuery());
if ($result === false) {
$errors = $this->model->errors();
$errorMsg = is_array($errors) ? implode(", ", $errors) : "삽입 작업이 실패했습니다.";
throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg);
}
return $this->handle_save_result($result);
// $pk는 auto-increment가 사용된 경우 새로 생성된 ID, 아니면 기존 $uid (업데이트의 경우)
$pk = $this->model->useAutoIncrement() && is_numeric($result) && (int)$result > 0 ? (int)$result : $uid;
if (empty($pk)) {
$errors = $this->model->errors();
$errorMsg = is_array($errors) && !empty($errors) ? implode(", ", $errors) : "DB 작업 성공 후 PK를 확인할 수 없거나 모델 오류 발생:{$pk}";
throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg);
}
return $pk;
}
protected function save_process(CommonEntity $entity): CommonEntity
{
$result = $this->model->save($entity);
// 최종적으로 DB에 반영된 PK를 반환받습니다. (UPDATE이면 기존 PK, INSERT이면 새 PK)
$finalPK = $this->handle_save_result($result, $entity->getPK());
return $this->getEntity($finalPK);
}
//생성용
abstract protected function create_process(array $formDatas): CommonEntity;
public function create(object $dto): CommonEntity
{
$formDatas = (array)$dto;
//입력값 검증
$formDatas = $dto->toArray();
if (!$this->getFormService()->validate($formDatas)) {
throw new ValidationException(implode("\n", service('validation')->getErrors()));
}
return $this->create_process($formDatas);
$entity = $this->create_process($formDatas);
return $this->save_process($entity);
}
//수정용
protected function modify_process($uid, array $formDatas): CommonEntity
protected function modify_process(string|int $uid, array $formDatas): CommonEntity
{
if (!$uid) {
throw new \Exception("수정에 필요한 PrimaryKey 가 정의 되지 않았습니다.");
}
$entity = $this->getEntity($uid);
if (!$entity instanceof CommonEntity) {
if (!$entity) {
throw new \Exception(__METHOD__ . "에서 오류발생: {$uid}에 해당하는 정보을 찾을수 없습니다.");
}
//PrimaryKey 필드는 수정에서 제외
unset($formDatas[$this->model->primaryKey]);
$result = $this->model->update($uid, $formDatas);
log_message('debug', $this->model->getLastQuery());
if ($result === false) {
$errors = $this->model->errors();
$errorMsg = is_array($errors) ? implode(", ", $errors) : "업데이트 작업이 실패했습니다.";
throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg);
$pkField = $this->model->getPKField();
// DTO에서 넘어온 데이터에 PK 필드가 포함되어 있으면 제거하여,
// 기존 엔티티의 PK를 덮어쓰지 못하도록 방어
if (isset($formDatas[$pkField])) {
unset($formDatas[$pkField]);
}
return $this->handle_save_result($result, $uid);
$entity->fill($formDatas);
// <<< FIX: fill() 후 PK가 유실되었는지 확인하고 강제로 재설정 (방어적 코딩) >>>
if (empty($entity->getPK())) {
log_message('warning', "modify_process에서 fill() 후 PK가 유실되어 uid {$uid}를 강제로 재설정합니다.");
// Entity의 PK 필드를 직접 설정하여 UPDATE가 실행되도록 보장
$entity->{$pkField} = $uid;
}
// save_process 진입 전 Entity의 PK 최종 확인 로그
log_message('debug', "save_process 진입 전 Entity PK: " . $entity->getPK() . " (기대값: {$uid})");
return $entity;
}
public function modify($uid, object $dto): CommonEntity
public function modify(string|int $uid, object $dto): CommonEntity
{
$formDatas = (array)$dto;
//입력값 검증
$formDatas = $dto->toArray();
if (!$this->getFormService()->validate($formDatas)) {
throw new ValidationException(implode("\n", service('validation')->getErrors()));
}
return $this->modify_process($uid, $formDatas);
$entity = $this->modify_process($uid, $formDatas);
return $this->save_process($entity);
}
protected function delete_process($uid): CommonEntity
//배치 작업용 수정
protected function batchjob_process(string|int $uid, array $formDatas): CommonEntity
{
// modify_process를 호출하여 로직 재사용 (PK 로드 및 PK 제거/방어 로직 포함)
$entity = $this->modify_process($uid, $formDatas);
return $entity;
}
public function batchjob(string|int $uid, array $formDatas): CommonEntity
{
if (!$this->getFormService()->validate($formDatas)) {
throw new ValidationException(implode("\n", service('validation')->getErrors()));
}
$entity = $this->batchjob_process($uid, $formDatas);
return $this->save_process($entity);
}
//삭제용 (일반)
protected function delete_process(string|int $uid): CommonEntity
{
if (!$uid) {
throw new \Exception("삭제에 필요한 PrimaryKey 가 정의 되지 않았습니다.");
}
$entity = $this->getEntity($uid);
if (!$entity instanceof CommonEntity) {
if (!$entity) {
throw new \Exception(__METHOD__ . "에서 오류발생: {$uid}에 해당하는 정보을 찾을수 없습니다.");
}
return $entity;
}
final public function delete($uid): CommonEntity
final public function delete(string|int $uid): CommonEntity
{
$entity = $this->delete_process($uid);
$result = $this->model->delete($entity->getPK());
log_message('debug', $this->model->getLastQuery());
if ($result === false) {
$errors = $this->model->errors();
$errorMsg = is_array($errors) ? implode(", ", $errors) : "모델 삭제 작업이 실패했습니다.";
$errorMsg = is_array($errors) ? implode(", ", $errors) : "삭제 작업이 실패했습니다.";
throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg);
}
return $entity;
}
//삭제용 (배치 작업)
protected function batchjob_delete_process(string|int $uid): CommonEntity
{
// delete_process를 호출하여 로직 재사용 (CommonEntity 로드 및 유효성 검사)
$entity = $this->delete_process($uid);
return $entity;
}
final public function batchjob_delete(string|int $uid): CommonEntity
{
$entity = $this->batchjob_delete_process($uid);
$result = $this->model->delete($entity->getPK());
log_message('debug', $this->model->getLastQuery());
if ($result === false) {
$errors = $this->model->errors();
$errorMsg = is_array($errors) ? implode(", ", $errors) : "삭제 작업이 실패했습니다.";
throw new RuntimeException(__METHOD__ . "에서 오류발생: " . $errorMsg);
}
return $entity;
}
//Index용
final public function getTotalCount(): int
{

View File

@ -18,6 +18,10 @@ class MylogService extends CommonService
parent::__construct($model);
$this->addClassPaths('Mylog');
}
public function createDTO(array $formDatas): MylogDTO
{
return new MylogDTO($formDatas);
}
public function getFormService(): MylogForm
{
if ($this->_form === null) {
@ -47,6 +51,14 @@ class MylogService extends CommonService
return $this->_helper;
}
//기본 기능부분
protected function getEntity_process(mixed $entity): MylogEntity
{
return $entity;
}
protected function create_process(array $formDatas): MylogEntity
{
return new MylogEntity($formDatas);
}
public function create(object $dto): MylogEntity
{
if (!$dto instanceof MylogDTO) {
@ -54,6 +66,10 @@ class MylogService extends CommonService
}
return parent::create($dto);
}
protected function modify_process($uid, array $formDatas): MyLogEntity
{
return parent::modify_process($uid, $formDatas);
}
public function modify($uid, object $dto): MyLogEntity
{
if (!$dto instanceof MylogDTO) {

View File

@ -18,6 +18,10 @@ class TrafficService extends CommonService
parent::__construct($model);
$this->addClassPaths('Traffic');
}
public function createDTO(array $formDatas): TrafficDTO
{
return new TrafficDTO($formDatas);
}
public function getFormService(): TrafficForm
{
if ($this->_form === null) {
@ -47,6 +51,14 @@ class TrafficService extends CommonService
return $this->_helper;
}
//기본 기능부분
protected function getEntity_process(mixed $entity): TrafficEntity
{
return $entity;
}
protected function create_process(array $formDatas): TrafficEntity
{
return new TrafficEntity($formDatas);
}
public function create(object $dto): TrafficEntity
{
if (!$dto instanceof TrafficDTO) {
@ -54,6 +66,10 @@ class TrafficService extends CommonService
}
return parent::create($dto);
}
protected function modify_process($uid, array $formDatas): TrafficEntity
{
return parent::modify_process($uid, $formDatas);
}
public function modify($uid, object $dto): TrafficEntity
{
if (!$dto instanceof TrafficDTO) {

View File

@ -18,6 +18,10 @@ class UserService extends CommonService
parent::__construct($model);
$this->addClassPaths('User');
}
public function createDTO(array $formDatas): UserDTO
{
return new UserDTO($formDatas);
}
public function getFormService(): UserForm
{
if ($this->_form === null) {
@ -47,14 +51,17 @@ class UserService extends CommonService
return $this->_helper;
}
//기본 기능부분
protected function getEntity_process(mixed $entity): UserEntity
{
return $entity;
}
protected function create_process(array $formDatas): UserEntity
{
//confirmpassword 필드는 Entity에 필요없으므로 제거
if (isset($formDatas['confirmpassword'])) {
unset($formDatas['confirmpassword']);
}
$entity = parent::create_process($formDatas);
return $entity;
return new UserEntity($formDatas);
}
public function create(object $dto): UserEntity
{
@ -69,8 +76,7 @@ class UserService extends CommonService
if (isset($formDatas['confirmpassword'])) {
unset($formDatas['confirmpassword']);
}
$entity = parent::modify_process($uid, $formDatas);
return $entity;
return parent::modify_process($uid, $formDatas);
}
public function modify($uid, object $dto): UserEntity
{