dbmsv4 init...5

This commit is contained in:
최준흠 2026-02-05 17:06:32 +09:00
parent fbc62329ae
commit 0c780611c9
13 changed files with 236 additions and 132 deletions

View File

@ -27,11 +27,11 @@ class Layout extends BaseConfig
],
'stylesheets' => [
'<link rel="icon" href="/favicon.ico">',
'<link href="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">',
'<link href="//cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">',
'<link rel="stylesheet" href="/css/common/style.css" />',
],
'javascripts' => [
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>',
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>',
],
'footerScripts' => []
],
@ -56,13 +56,13 @@ class Layout extends BaseConfig
'<link rel="icon" href="/favicon.ico">',
'<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">',
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">',
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">',
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">',
'<link rel="stylesheet" href="/css/common/style.css" />',
],
'javascripts' => [
'<script src="//cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>',
'<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>',
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>',
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>',
'<script src="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>',
],
'footerScripts' => []
@ -87,7 +87,7 @@ class Layout extends BaseConfig
'<link rel="icon" href="/favicon.ico">',
'<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">',
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">',
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">',
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">',
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">',
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />',
'<link rel="stylesheet" href="/assets/tagify/dist/tagify.css">',
@ -96,7 +96,7 @@ class Layout extends BaseConfig
'javascripts' => [
'<script src="//cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>',
'<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>',
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>',
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>',
'<script src="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>',
'<script src="/assets/tinymce/tinymce.min.js" referrerpolicy="origin"></script>',
'<script src="/assets/tagify/dist/tagify.js"></script>'

View File

@ -92,7 +92,7 @@ class ServiceController extends CustomerController
try {
$db->transException(true)->transStart();
//추가할 대체서버 정의
$serverinfo_uid = $this->request->getGet('serverinfo_uid');
$serverinfo_uid = $this->request->getPost('serverinfo_uid');
if (!$serverinfo_uid) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: 변경할 대체서버가 정의되지 않았습니다.");
}
@ -105,7 +105,7 @@ class ServiceController extends CustomerController
service('equipment_serverservice')->attatchToService($entity, $serverinfo_uid);
// 트랜잭션 완료 및 커밋
$db->transComplete();
return $this->action_redirect_process('info', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 대체서버 추가가 완료되었습니다.");
return static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 대체서버 추가가 완료되었습니다.";
} catch (\Throwable $e) {
$db->transRollback(); // 예외 발생 시 수동으로 롤백
return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 대체서버 추가 오류:" . $e->getMessage());

View File

@ -7,7 +7,6 @@ use Psr\Log\LoggerInterface;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use App\Entities\Equipment\ServerEntity;
class ServerController extends EquipmentController
{

View File

@ -12,7 +12,6 @@ class PaymentEntity extends CommonEntity
protected array $nullableFields = [
'serviceinfo_uid',
'serverpartinfo_uid',
'pay',
];
protected $attributes = [
'serviceinfo_uid' => null,
@ -22,7 +21,7 @@ class PaymentEntity extends CommonEntity
'billing' => "",
'billing_at' => "",
'billing_month' => 0,
'pay' => null,
'pay' => "",
'status' => '',
'content' => ''
];
@ -67,7 +66,7 @@ class PaymentEntity extends CommonEntity
{
return $this->billing_month ?? 0;
}
public function getPay(): int|null
public function getPay(): string|null
{
return $this->pay ?? null;
}

View File

@ -238,7 +238,6 @@ abstract class CommonService
try {
// INSERT 시 Entity의 PK는 0 또는 NULL이어야 함 (DB가 ID를 생성하도록)
$initialPK = $entity->getPK();
// dd($entity);
$result = $this->model->save($entity);
// 최종적으로 DB에 반영된 PK를 반환받습니다. (UPDATE이면 기존 PK, INSERT이면 새 PK)
$entity->{$this->getPKField()} = $this->handle_save_result($result, $initialPK);

View File

@ -79,16 +79,23 @@ class ServiceService extends CustomerService
final protected function updateAmount(ServiceEntity $entity): ServiceEntity
{
$serverUid = $entity->getServerInfoUid();
// ✅ 서버 미지정(해지/분리 상태) 방어: 계산 시도 자체를 막는다
if (empty($serverUid)) { // null, 0, '' 모두 방어
throw new RuntimeException(
static::class . '->' . __FUNCTION__
. " 오류: 서비스({$entity->getPK()})에 serverinfo_uid가 없습니다. (해지/분리 상태)"
);
}
$entity->amount = $this->getCalculatedAmount(
$entity->getRack(),
$entity->getLine(),
$entity->getSale(),
$entity->getServerInfoUid()
(int) $serverUid
);
// dd($entity);
if (!$this->model->save($entity)) {
$errors = $this->model->errors();
throw new RuntimeException("금액 업데이트 중 DB 저장 오류: " . implode(', ', $errors));
throw new RuntimeException("금액 업데이트 중 DB 저장 오류: " . implode(', ', $this->model->errors()));
}
return $entity;
}
@ -117,7 +124,7 @@ class ServiceService extends CustomerService
final public function recalcAmountAndSyncPaymentInternal(ServiceEntity $old, ServiceEntity $current): ServiceEntity
{
$current = $this->updateAmount($current);
service('paymentservice')->upsertByService($old, $current);
service('paymentservice')->setByService($old, $current);
return $current;
}
@ -163,15 +170,11 @@ class ServiceService extends CustomerService
protected function modify_process($entity, array $formDatas): ServiceEntity
{
if (empty($formDatas['site'])) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: 사이트가 지정되지 않았습니다.");
}
if (empty($formDatas['serverinfo_uid'])) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: 서버가 지정되지 않았습니다.");
}
$oldEntity = clone $entity;
$formDatas['code'] = $entity->getCode();
$formDatas['amount'] = 0;

View File

@ -50,14 +50,14 @@ class ServerPartService extends EquipmentService
return $entity;
}
private function getBillingAtByServiceEntity(ServiceEntity $serviceEntity, string $billing): string
private function getBillingAtByServiceEntity(ServiceEntity $serviceEntity, string $billing): ?string
{
if ($billing === PAYMENT['BILLING']['MONTH']) {
return $serviceEntity->getBillingAt();
} elseif ($billing === PAYMENT['BILLING']['ONETIME']) {
return date("Y-m-d");
}
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: {$billing}에 해당하는 청구방식을 찾을수 없습니다.");
return null;
}
private function setPartTitleByPartEntity(array $formDatas): string
@ -86,6 +86,8 @@ class ServerPartService extends EquipmentService
$formDatas['title'] = $this->setPartTitleByPartEntity($formDatas);
// ✅ ServerPart는 serverinfo_uid 기준으로 움직인다.
// serviceinfo_uid는 serverEntity의 "현재 상태"에서만 참고한다.
$serviceEntity = null;
if ($serverEntity->getServiceInfoUid()) {
$serviceEntity = service('customer_serviceservice')->getEntity($serverEntity->getServiceInfoUid());
@ -104,34 +106,53 @@ class ServerPartService extends EquipmentService
/**
* MONTH 파트 변경 :
* - "서비스가 유지중"이면: amount 재계산 + Payment upsert (internal)
* - "서버가 서비스에서 분리된 상태(server.serviceinfo_uid==null)"이면: amount 재계산만 + Payment는 TERMINATED 처리
* - "서비스가 유지중"이면: (serverinfo_uid 기준) 현재 서버가 붙은 서비스로 amount 재계산 + Payment upsert
* - "서버가 서비스에서 분리된 상태(server.serviceinfo_uid==null)"이면: 미납 결제 status=TERMINATED (amount 재계산 금지)
*
* 핵심: ServerPartService는 'serviceUid' 아니라 'serverUid' 기준으로 판단한다.
*/
private function syncMonthlyServiceAndPayment(?int $serviceUid, ?ServiceEntity $oldServiceSnapshot = null, bool $serverDetached = false): void
{
if (!$serviceUid)
return;
$svcService = service('customer_serviceservice');
$current = $svcService->getEntity($serviceUid);
if (!$current instanceof ServiceEntity) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: {$serviceUid}에 해당하는 서비스정보을 찾을수 없습니다.");
private function syncMonthlyServiceAndPaymentByServer(
int $serverUid,
?ServiceEntity $oldServiceSnapshot = null,
bool $serverDetached = false
): void {
$server = service('equipment_serverservice')->getEntity($serverUid);
if (!$server instanceof ServerEntity) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: {$serverUid} 서버정보를 찾을수 없습니다.");
}
$old = $oldServiceSnapshot instanceof ServiceEntity ? $oldServiceSnapshot : clone $current;
// ✅ 해지 케이스
if ($serverDetached) {
// ✅ 해지 케이스: payment upsert 금지(0원화 방지), 월 미납은 TERMINATED
$svcService->recalcAmountInternal($current);
service('paymentservice')->terminateUnpaidMonthlyByService($old);
if ($oldServiceSnapshot instanceof ServiceEntity) {
service('paymentservice')->terminateUnpaidMonthlyByService($oldServiceSnapshot);
}
return;
}
$serviceUid = $server->getServiceInfoUid();
if (!$serviceUid) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: 서비스가 정의되지 않은 서버정보입니다.");
}
$svcService = service('customer_serviceservice');
$current = $svcService->getEntity($serviceUid);
if (!$current instanceof ServiceEntity) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: {$serviceUid} 서비스정보를 찾을수 없습니다.");
}
// ✅ 핵심 추가: 메인서버만 amount/payment 동기화 허용
if ((int) $current->getServerInfoUid() !== (int) $serverUid) {
// 대체서버(alternative)에서는 서비스금액/결제 동기화 금지
return; // 또는 정책상 필요하면 예외로 변경 가능
// throw new RuntimeException("메인서버가 아닌 서버({$serverUid})에서 월동기화 금지");
}
// ✅ 일반 케이스: amount + payment upsert
$old = $oldServiceSnapshot instanceof ServiceEntity ? $oldServiceSnapshot : clone $current;
$svcService->recalcAmountAndSyncPaymentInternal($old, $current);
}
protected function create_process(array $formDatas): CommonEntity
{
$serverEntity = service('equipment_serverservice')->getEntity($formDatas['serverinfo_uid']);
@ -146,18 +167,18 @@ class ServerPartService extends EquipmentService
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: Return Type은 ServerPartEntity만 가능");
}
// dd($entity);
$this->getPartService($entity->getType())->attachToServerPart($entity);
// ✅ 서버가 서비스에 붙어 있을 때만 결제/동기화
if ($entity->getServiceInfoUid()) {
if ($entity->getBilling() == PAYMENT['BILLING']['MONTH']) {
$this->syncMonthlyServiceAndPayment($entity->getServiceInfoUid(), null, false);
$this->syncMonthlyServiceAndPaymentByServer((int) $entity->getServerInfoUid());
}
if ($entity->getBilling() == PAYMENT['BILLING']['ONETIME']) {
service('paymentservice')->createByServerPart($entity);
}
}
return $entity;
}
@ -179,17 +200,18 @@ class ServerPartService extends EquipmentService
$this->getPartService($entity->getType())->modifyServerPart($oldEntity, $entity);
$serviceUidsToSync = [];
// ✅ MONTH 동기화는 serviceinfo_uid가 아니라 serverinfo_uid 기준
$serverUidsToSync = [];
if ($oldEntity->getServiceInfoUid() && $oldEntity->getBilling() == PAYMENT['BILLING']['MONTH']) {
$serviceUidsToSync[(int) $oldEntity->getServiceInfoUid()] = true;
if ($oldEntity->getBilling() == PAYMENT['BILLING']['MONTH'] && $oldEntity->getServerInfoUid()) {
$serverUidsToSync[(int) $oldEntity->getServerInfoUid()] = true;
}
if ($entity->getServiceInfoUid() && $entity->getBilling() == PAYMENT['BILLING']['MONTH']) {
$serviceUidsToSync[(int) $entity->getServiceInfoUid()] = true;
if ($entity->getBilling() == PAYMENT['BILLING']['MONTH'] && $entity->getServerInfoUid()) {
$serverUidsToSync[(int) $entity->getServerInfoUid()] = true;
}
foreach (array_keys($serviceUidsToSync) as $svcUid) {
$this->syncMonthlyServiceAndPayment((int) $svcUid, null, false);
foreach (array_keys($serverUidsToSync) as $serverUid) {
$this->syncMonthlyServiceAndPaymentByServer((int) $serverUid);
}
if ($entity->getBilling() == PAYMENT['BILLING']['ONETIME']) {
@ -214,8 +236,9 @@ class ServerPartService extends EquipmentService
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: Return Type은 ServerPartEntity만 가능");
}
if ($old->getServiceInfoUid() !== null && $old->getBilling() == PAYMENT['BILLING']['MONTH']) {
$this->syncMonthlyServiceAndPayment($old->getServiceInfoUid(), null, false);
// ✅ MONTH면 서버 기준으로 서비스/결제 동기화
if ($old->getBilling() == PAYMENT['BILLING']['MONTH'] && $old->getServerInfoUid()) {
$this->syncMonthlyServiceAndPaymentByServer((int) $old->getServerInfoUid());
}
// ✅ ONETIME 미납 결제 삭제는 "보류" (여기서는 아무것도 안함)
@ -256,61 +279,37 @@ class ServerPartService extends EquipmentService
/**
* 서버 해지/분리 서버파트 회수 처리
* - MONTH 파트 삭제되면:
* - server.serviceinfo_uid == null (분리 완료) 경우에만:
* - 미납 payment는 삭제/0원수정 금지, status=TERMINATED
* - service.amount는 재계산만(결제 upsert 금지)
* - BASE 제외 전부 삭제(정책상 OK)
* - MONTH 삭제가 있었다면:
* - server.serviceinfo_uid == null 상태에서만 TERMINATED 처리 (amount 재계산 금지)
* - ONETIME 미납 삭제는 보류
*
* @param ServiceEntity|null $oldServiceEntity 분리 서비스 스냅샷(필수 권장)
* @param ServiceEntity|null $oldServiceEntity 분리 서비스 스냅샷(권장: TERMINATED 매칭용)
*/
public function detachFromServer(ServerEntity $serverEntity, ?ServiceEntity $oldServiceEntity = null): void
{
// 서버가 분리되기 전 serviceinfo_uid를 잃을 수 있으므로, oldServiceEntity로 serviceUid 확보
$serviceUid = $oldServiceEntity instanceof ServiceEntity ? $oldServiceEntity->getPK() : $serverEntity->getServiceInfoUid();
$monthlyChanged = false;
foreach ($this->getEntities([
'serverinfo_uid' => $serverEntity->getPK(),
"billing !=" => PAYMENT['BILLING']['BASE']
]) as $entity) {
foreach ($this->getEntities(['serverinfo_uid' => $serverEntity->getPK(), "billing !=" => PAYMENT['BILLING']['BASE']]) as $entity) {
if (!$entity instanceof ServerPartEntity)
continue;
$this->getPartService($entity->getType())->detachFromServerPart($entity);
if ($entity->getBilling() == PAYMENT['BILLING']['MONTH']) {
$monthlyChanged = true;
}
// ✅ 일괄 삭제(추가 동기화는 아래에서 한 번만)
parent::delete_process($entity);
}
if (!$monthlyChanged || !$serviceUid) {
if (!$monthlyChanged) {
return;
}
// ✅ "서버가 서비스에서 분리된 경우"에만 payment TERMINATED 처리
// ✅ 분리 완료 후(server.serviceinfo_uid == null)에서만 TERMINATED 처리
$serverDetached = ($serverEntity->getServiceInfoUid() === null);
if ($serverDetached) {
// oldServiceEntity가 없으면(호환 호출), 여기서라도 스냅샷 시도(가능하면)
if (!$oldServiceEntity instanceof ServiceEntity) {
$svc = service('customer_serviceservice')->getEntity($serviceUid);
if ($svc instanceof ServiceEntity) {
$oldServiceEntity = clone $svc;
}
}
if ($oldServiceEntity instanceof ServiceEntity) {
$this->syncMonthlyServiceAndPayment($serviceUid, $oldServiceEntity, true);
$this->syncMonthlyServiceAndPaymentByServer((int) $serverEntity->getPK(), $oldServiceEntity, true);
}
return;
}
// ✅ 서비스 유지중인 경우: amount + payment upsert
$this->syncMonthlyServiceAndPayment($serviceUid, null, false);
// ✅ (이 케이스는 정상 플로우에서는 거의 없지만) 서비스 유지중이면 정상 upsert
$this->syncMonthlyServiceAndPaymentByServer((int) $serverEntity->getPK());
}
}

View File

@ -36,6 +36,73 @@ class ServerService extends EquipmentService
return ServerEntity::class;
}
final public function getTotalServiceCount(array $where = []): array
{
$totalCounts = [
'chiba_summary' => 0,
'tokyo_summary' => 0,
'all_summary' => 0,
'normal' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
'defence' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
'dedicated' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
'alternative' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
'vpn' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
'event' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
'test' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
];
$builder = $this->model->select("serverinfo.type, COUNT(CASE WHEN serviceinfo.location = 'chiba' THEN 1 END) AS chiba, COUNT(CASE WHEN serviceinfo.location = 'tokyo' THEN 1 END) AS tokyo, COUNT(CASE WHEN serviceinfo.location IN ('chiba', 'tokyo') THEN 1 END) AS summary")
->join('serviceinfo', 'serviceinfo.uid = serverinfo.serviceinfo_uid')
->where($where)
->groupBy('serverinfo.type')
->builder();
// echo $builder->getCompiledSelect(false) . "<BR>";
//초기화 없이 SQL만 보고 싶을 때: getCompiledSelect(false) ← 꼭 false!
// dd($rows);
foreach ($builder->get()->getResult() as $row) {
$totalCounts[$row->type]['chiba'] = $row->chiba;
$totalCounts[$row->type]['tokyo'] = $row->tokyo;
$totalCounts[$row->type]['summary'] += $row->summary;
$totalCounts['chiba_summary'] += $row->chiba;
$totalCounts['tokyo_summary'] += $row->tokyo;
$totalCounts['all_summary'] = $totalCounts['chiba_summary'] + $totalCounts['tokyo_summary'];
}
// dd($totalCounts);
return $totalCounts;
}
//전체 검색어에 따른 서버정보를 검색 후 해당하는 서비스리스트를 가져온다.
final public function getSearchServices(string $keyword): array
{
$builder = $this->model->distinct()->select('serverinfo.serviceinfo_uid AS serviceinfo_uid')
->join('clientinfo', 'clientinfo.uid = serverinfo.clientinfo_uid')
->join('serverpartinfo', 'serverpartinfo.clientinfo_uid = clientinfo.uid', 'left')
->groupStart()
->like('clientinfo.name', $keyword, 'both', null, true) // escape=true
->orLike('serverinfo.code', $keyword, 'both', null, true)
->orLike('serverinfo.ip', $keyword, 'both', null, true)
->orLike('serverinfo.title', $keyword, 'both', null, true)
->orLike('serverpartinfo.title', $keyword, 'both', null, true)
->groupEnd()
->builder();
// echo $builder->getCompiledSelect(false); //초기화 없이 SQL만 보고 싶을 때: getCompiledSelect(false) ← 꼭 false!
$rows = $builder->get()->getResult();
if (!count($rows)) {
return [];
}
return $rows;
}
//서버 Title별 카운트수
final public function getStockCount(): array
{
$builder = $this->model->select('title,COUNT(*) AS cnt')->groupBy('title')->builder();
// echo $builder->getCompiledSelect(false); //초기화 없이 SQL만 보고 싶을 때: getCompiledSelect(false) ← 꼭 false!
// dd($builder->get()->getResult());
$rows = [];
foreach ($builder->get()->getResult() as $row) {
$rows[$row->title] = $row->cnt;
}
return $rows;
}
//총서버금액
final public function getCalculatedAmount(int $uid): int
{
@ -43,11 +110,10 @@ class ServerService extends EquipmentService
if (!$entity instanceof ServerEntity) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: {$uid} 서버 정보를 찾을수 없습니다.");
}
$serverPartService = service('equipment_serverpartservice');
$caculatedAmount = $entity->getPrice();
foreach ($serverPartService->getEntities(['serverinfo_uid' => $entity->getPK(), 'billing' => PAYMENT['BILLING']['MONTH']]) as $serverPartEntity) {
log_message('debug', $serverPartEntity->getCustomTitle() . '::' . $serverPartEntity->getCalculatedAmount());
$caculatedAmount += $serverPartEntity->getCalculatedAmount();
}
return $caculatedAmount;
@ -189,4 +255,33 @@ class ServerService extends EquipmentService
// - ONETIME 미납 삭제는 보류
service('equipment_serverpartservice')->detachFromServer($entity, $oldServiceEntity);
}
// Service 메인서버 교체 시 서버 정합성 유지용
public function modifyByService(ServiceEntity $oldServiceEntity, ServiceEntity $serviceEntity): void
{
// 기존 메인서버
$oldServer = $this->getEntity($oldServiceEntity->getServerInfoUid());
if (!$oldServer instanceof ServerEntity) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ .
"에서 오류발생: {$oldServiceEntity->getServerInfoUid()} 기존 메인 서버정보를 찾을수 없습니다.");
}
// 신규 메인서버
$newServer = $this->getEntity($serviceEntity->getServerInfoUid());
if (!$newServer instanceof ServerEntity) {
throw new RuntimeException(static::class . '->' . __FUNCTION__ .
"에서 오류발생: {$serviceEntity->getServerInfoUid()} 신규 메인 서버정보를 찾을수 없습니다.");
}
// 신규 서버를 서비스에 붙임(occupied 처리 + client/service uid 세팅)
// type은 기존 메인서버의 type을 승계 (normal/dedicated/defence 등)
$this->attatchToService($serviceEntity, $newServer->getPK(), [
'type' => $oldServer->getType()
]);
// 기존 메인서버는 대체 서버로 변경(서비스에 남겨둘지/분리할지는 정책에 따라 다름)
// 여기서는 기존 동작 유지: alternative로만 바꿈
parent::modify_process($oldServer, ['type' => 'alternative']);
}
}

View File

@ -123,22 +123,21 @@ class PaymentService extends CommonService
}
//서비스정보로 결제정보 생성 또는 수정 (일반 운영용: upsert 유지)
public function upsertByService(ServiceEntity $oldServiceEntity, ServiceEntity $serviceEntity): PaymentEntity
public function setByService(ServiceEntity $oldServiceEntity, ServiceEntity $serviceEntity): PaymentEntity
{
$formDatas = $this->getFormDatasFromService($serviceEntity);
$entity = $this->getEntity([
'serviceinfo_uid' => $oldServiceEntity->getPK(),
'billing' => PAYMENT['BILLING']['MONTH'],
'billing_at' => $oldServiceEntity->getBillingAt(),
'status' => STATUS['UNPAID']
]);
$formDatas = $this->getFormDatasFromService($serviceEntity);
if (!$entity instanceof PaymentEntity) {
return $this->create_process($formDatas);
}
//매칭되는게 있으면
if ($entity instanceof PaymentEntity) {
return $this->modify_process($entity, $formDatas);
}
return $this->create_process($formDatas);
}
//일회성,선결제,쿠폰,포인트 입력 관련
protected function create_process(array $formDatas): PaymentEntity

View File

@ -91,7 +91,7 @@ trait UtilTrait
final public function alertTrait(string $msg, $url = null): string
{
$safeMsg = json_encode($msg, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG);
$js = "alert({$safeMsg});";
$js = "console.log({$safeMsg}); alert({$safeMsg});";
switch ($url) {
case 'close':
$js .= "window.close();";
@ -110,7 +110,8 @@ trait UtilTrait
final public function getPasswordStringTrait($length = 8, $characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
{
return substr(str_shuffle($characters), 0, $length);;
return substr(str_shuffle($characters), 0, $length);
;
}
// byte값을 알아보기 쉽게 변환

View File

@ -1,3 +1,4 @@
<?= session('message') ? session('message') : ""; ?>
<div class="rounded border border-gray p-2 mt-3">
<div style="font-size:15px">미지급 1회성정보</div>
<table class="table table-bordered table-hover table-striped">
@ -17,16 +18,28 @@
<?php $paymentCellDatas['entity'] = $entity ?>
<tr class="text-left">
<td><?= $cnt ?></td>
<td class="text-center"><?= $paymentCellDatas['helper']->getFieldView('serviceinfo_uid', $entity->getServiceInfoUid(), $paymentCellDatas) ?></td>
<td class="text-center">
<?= $paymentCellDatas['helper']->getFieldView('serviceinfo_uid', $entity->getServiceInfoUid(), $paymentCellDatas) ?>
</td>
<td class="text-center">
<?= $paymentCellDatas['helper']->getFieldView('title', $entity->getTitle(), $paymentCellDatas) ?>
<div><?= $paymentCellDatas['helper']->getFieldView('content', $entity->getContent(), $paymentCellDatas) ?></div>
</td>
<td class="text-end"><?= $paymentCellDatas['helper']->getFieldView('amount', $entity->getAmount(), $paymentCellDatas) ?></td>
<td class="text-center"><?= $paymentCellDatas['helper']->getFieldView('billing', $entity->getBilling(), $paymentCellDatas) ?></td>
<td class="text-center"><?= $paymentCellDatas['helper']->getFieldView('pay', $entity->getPay(), $paymentCellDatas) ?></td>
<td class="text-center"><?= $paymentCellDatas['helper']->getFieldView('create_at', $entity->getCreatedAt(), $paymentCellDatas) ?></td>
<td class="text-center"><?= $paymentCellDatas['helper']->getFieldView('user_uid', $entity->getUserUid(), $paymentCellDatas) ?></td>
<td class="text-end">
<?= $paymentCellDatas['helper']->getFieldView('amount', $entity->getAmount(), $paymentCellDatas) ?>
</td>
<td class="text-center">
<?= $paymentCellDatas['helper']->getFieldView('billing', $entity->getBilling(), $paymentCellDatas) ?>
</td>
<td class="text-center">
<?= $paymentCellDatas['helper']->getFieldView('pay', $entity->getPay(), $paymentCellDatas) ?>
</td>
<td class="text-center">
<?= $paymentCellDatas['helper']->getFieldView('create_at', $entity->getCreatedAt(), $paymentCellDatas) ?>
</td>
<td class="text-center">
<?= $paymentCellDatas['helper']->getFieldView('user_uid', $entity->getUserUid(), $paymentCellDatas) ?>
</td>
<td class="text-center"><?= $paymentCellDatas['helper']->getListButton('paid', "", $paymentCellDatas) ?></td>
</tr>
<?php $cnt-- ?>

View File

@ -2,26 +2,12 @@
<?php foreach ($serverCellDatas['entities'] as $entity): ?>
<?php $serverCellDatas['entity'] = $entity ?>
<tr class="text-center">
<th style="width: 250px">
<th style="width: 150px">
<?= $serverCellDatas['serviceEntity']->getServerInfoUid() == $entity->getPK() ? "📌" : "<a href=\"/admin/customer/service/changeServer/{$serverCellDatas['serviceEntity']->getPK()}?serverinfo_uid={$entity->getPK()}\">✔️</a>" ?>
<?= $serverCellDatas['serverPartHelper']->getFieldView('SERVER', "", ['serverEntity' => $entity]) ?>
<?= "<a href=\"/admin/customer/service/terminateServer/{$serverCellDatas['serviceEntity']->getPK()}?serverinfo_uid={$entity->getPK()}\">❌</a>" ?>
</th>
<th style=" width: 250px">
<?= $serverCellDatas['serverPartHelper']->getListButton('CPU', 'CPU', ['serverinfo_uid' => $entity->getPK()]) ?>
/ <?= $serverCellDatas['serverPartHelper']->getListButton('RAM', 'RAM', ['serverinfo_uid' => $entity->getPK()]) ?>
/
<?= $serverCellDatas['serverPartHelper']->getListButton('DISK', 'DISK', ['serverinfo_uid' => $entity->getPK()]) ?>
</th>
<th style="width: 200px">
<?= $serverCellDatas['serverPartHelper']->getListButton('IP', '추가IP', ['serverinfo_uid' => $entity->getPK()]) ?>
</th>
<th style="width: 200px">
<?= $serverCellDatas['serverPartHelper']->getListButton('CS', 'CS', ['serverinfo_uid' => $entity->getPK()]) ?>
</th>
<th style="width: 200px">
<?= $serverCellDatas['serverPartHelper']->getListButton('SOFTWARE', 'SOFTWARE', ['serverinfo_uid' => $entity->getPK()]) ?>
</th>
<th>파트정보</th>
</tr>
<tr class="text-center">
<td nowrap>
@ -38,11 +24,12 @@
</div>
<?php endif; ?>
</td>
<td>
<?= view_cell("\App\Cells\Equipment\ServerPartCell::parttable", [
'serverinfo_uid' => $entity->getPK(),
'types' => SERVERPART['ALL_PARTTYPES'],
'template' => 'detail'
'types' => SERVERPART['ALL_PARTTYPES']
]) ?>
</td>
</tr>
<?php endforeach; ?>
</table>

View File

@ -15,12 +15,22 @@
<table class="table table-bordered table-striped m-0 p-0">
<?php foreach ($serverPartCellDatas['types'] as $type): ?>
<tr class="m-0 p-0">
<th class="text-end m-0 p-0" width="15%"><?= $serverPartCellDatas['helper']->getListButton($type, '', $serverPartCellDatas) ?></th>
<td class="text-start m-0 p-0">
<th class="text-end m-0 p-0" width="10%">
<?= $serverPartCellDatas['helper']->getListButton($type, '', $serverPartCellDatas) ?>
</th>
<th>
<table class="table table-striped m-0 p-0">
<?php foreach ($htmls[$type] as $html): ?>
<?= $html['view'] ?>[<?= number_format($html['amount']) ?>원]<?= $html['entity']->getBilling() == PAYMENT['BILLING']['BASE'] ? "" : "<a href=\"/admin/equipment/serverpart/delete/{$html['entity']->getPK()}\">❌</a>" ?><BR>
<?php endforeach ?>
<tr>
<td class="text-start m-0 p-0"><?= $html['view'] ?></td>
<td class="text-end m-0 p-0 w-25"><?= number_format($html['amount']) ?>원</td>
<td class="text-center m-0 p-0 w-5">
<?= $html['entity']->getBilling() == PAYMENT['BILLING']['BASE'] ? "" : "<a href=\"/admin/equipment/serverpart/delete/{$html['entity']->getPK()}\">❌</a>" ?>
</td>
</tr>
<?php endforeach ?>
</table>
</th>
</tr>
<?php endforeach ?>
</table>