automation = model(AutomationModel::class); $this->google = model(AdvGoogleManagerModel::class); } public function index() { return view('automation/automation'); } public function getList() { if($this->request->isAJAX() && strtolower($this->request->getMethod()) === 'get'){ $arg = $this->request->getGet(); $automations = $this->automation->getAutomationList($arg); $result = $this->automationList($automations); return $this->respond($result); }else{ return $this->fail("잘못된 요청"); } } private function automationList($lists) { // 현재 시간을 설정 $currentDate = new Time('now'); // 목록의 각 항목을 순회 foreach ($lists['data'] as &$data) { $closestTime = null; // 상태가 1이 아니면 예상 실행 시간을 빈 문자열로 설정하고 다음 항목으로 넘어감 if (!isset($data['aa_status']) || $data['aa_status'] != 1) { $data['expected_time'] = ''; continue; } // 스케줄 값을 변환 $schedule = $this->convertJsonToTimes($data['aas_schedule_value']); $currentDay = $currentDate->format('N'); // 현재 요일 (1: 월요일, 7: 일요일) $currentTime = $currentDate->format('H:i'); // 현재 시간 (HH:MM) // 스케줄 탐색을 위한 요일 배열 생성 및 정렬 $days = array_keys($schedule); sort($days); $found = false; // 이번 주 스케줄 탐색 foreach ($days as $day) { // 오늘 실행된 항목이 있으면 현재 요일 이후의 스케줄을 계속 탐색 if (!empty($data['aar_exec_timestamp_success'])) { $execTimestampSuccess = Time::parse($data['aar_exec_timestamp_success']); // 실행 시간 $execDay = $execTimestampSuccess->format('N'); // 실행 시간의 요일 if ($data['aas_exec_once'] == 1 && $execDay == $day && $execDay == $currentDay) { continue; } } // 현재 요일 이후의 스케줄을 탐색하여 가장 가까운 시간을 찾음 foreach ($schedule[$day] as $time) { $scheduledTime = Time::parse($time); $scheduledDateTime = $currentDate->setISODate($currentDate->year, $currentDate->weekOfYear, $day)->setTime($scheduledTime->hour, $scheduledTime->minute); // 현재 시간 이후의 스케줄 시간 중 가장 가까운 시간을 찾음 if ($scheduledDateTime->isAfter($currentDate)) { if (is_null($closestTime) || $scheduledDateTime->isBefore($closestTime)) { $closestTime = $scheduledDateTime; $found = true; } } else if ($scheduledDateTime->difference($currentDate)->getMinutes() <= 30) { // 현재 시간과 30분 이내의 차이가 나는 스케줄 시간 중 가장 가까운 시간을 찾음 $interval = 5; // 5분 단위 $nextPossibleTime = $currentDate->addMinutes($interval - ($currentDate->getMinute() % $interval)); if ($nextPossibleTime->isAfter($currentDate) && (is_null($closestTime) || $nextPossibleTime->isBefore($closestTime))) { $closestTime = $nextPossibleTime; $found = true; } } } // 가장 가까운 시간을 찾았으면 루프를 종료 if ($found) break; } // 이번 주에 실행할 스케줄이 없으면 다음 주 스케줄 탐색 if (!$found) { foreach ($days as $day) { foreach ($schedule[$day] as $time) { $scheduledTime = Time::parse($time); $scheduledDateTime = $currentDate->addDays(7 - $currentDay + $day)->setTime($scheduledTime->hour, $scheduledTime->minute); if (is_null($closestTime) || $scheduledDateTime->isBefore($closestTime)) { $closestTime = $scheduledDateTime; } } } } // 가장 가까운 시간을 예상 실행 시간으로 설정 if (!is_null($closestTime)) { $data['expected_time'] = $closestTime->toDateTimeString(); } } // 예상 실행 시간이 포함된 목록을 반환 return $lists; } public function getAutomation() { if($this->request->isAJAX() && strtolower($this->request->getMethod()) === 'get'){ $arg = $this->request->getGet(); $result = $this->automation->getAutomation($arg); $result['aas_schedule_value'] = json_decode($result['aas_schedule_value']); if(!empty($result['targets'])){ foreach ($result['targets'] as &$target) { switch ($target['media']) { case 'company': $target['media'] = '광고주'; break; case 'facebook': $target['media'] = '페이스북'; break; case 'google': $target['media'] = '구글'; break; case 'kakao': $target['media'] = '카카오'; break; default: break; } switch ($target['type']) { case 'advertiser': $target['type'] = '광고주'; break; case 'account': $target['type'] = '매체광고주'; break; case 'campaign': $target['type'] = '캠페인'; break; case 'adgroup': $target['type'] = '광고그룹'; break; case 'ad': $target['type'] = '광고'; break; default: break; } if($target['status'] == 1 || $target['status'] == 'ON' || $target['status'] == 'ENABLED' || $target['status'] == 'ACTIVE'){ $target['status'] = '활성'; }else{ $target['status'] = '비활성'; } } } foreach ($result['executions'] as &$execution) { switch ($execution['media']) { case 'facebook': $execution['media'] = '페이스북'; break; case 'google': $execution['media'] = '구글'; break; case 'kakao': $execution['media'] = '카카오'; break; default: break; } switch ($execution['type']) { case 'campaign': $execution['type'] = '캠페인'; break; case 'adgroup': $execution['type'] = '광고그룹'; break; case 'ad': $execution['type'] = '광고'; break; default: break; } if($execution['status'] == 1 || $execution['status'] == 'ON' || $execution['status'] == 'ENABLED' || $execution['status'] == 'ACTIVE'){ $execution['status'] = '활성'; }else{ $execution['status'] = '비활성'; } } return $this->respond($result); }else{ return $this->fail("잘못된 요청"); } } public function setStatus() { if($this->request->isAJAX() && strtolower($this->request->getMethod()) === 'put'){ $arg = $this->request->getRawInput(); $data = [ 'seq' => $arg['seq'], 'status' => $arg['status'], ]; $result = $this->automation->setAutomationStatus($data); return $this->respond($result); }else{ return $this->fail("잘못된 요청"); } } public function getAdv() { if($this->request->isAJAX() && strtolower($this->request->getMethod()) === 'get'){ $arg = $this->request->getGet(); switch ($arg['tab']) { case 'advertiser': if(!empty($arg['adv'])){ $result = $this->automation->getSearchCompaniesWithAdv($arg); }else{ $result = $this->automation->getSearchCompanies($arg); } break; case 'account': if(!empty($arg['adv'])){ $result = $this->automation->getSearchAccountsWithAdv($arg); }else{ $result = $this->automation->getSearchAccounts($arg); } break; case 'campaign': if(!empty($arg['adv'])){ $result = $this->automation->getSearchCampaignsWithAdv($arg); }else{ $result = $this->automation->getSearchCampaigns($arg); } break; case 'adgroup': if(!empty($arg['adv'])){ $result = $this->automation->getSearchAdsetsWithAdv($arg); }else{ $result = $this->automation->getSearchAdsets($arg); } break; case 'ad': if(!empty($arg['adv'])){ $result = $this->automation->getSearchAdsWithAdv($arg); }else{ $result = $this->automation->getSearchAds($arg); } break; default: return $this->fail("잘못된 요청"); break; } foreach ($result['data'] as &$row) { if(isset($row['status']) && $row['status'] == 1 || $row['status'] == 'ON' || $row['status'] == 'ENABLED' || $row['status'] == 'ACTIVE'){ $row['status'] = '활성'; }else{ $row['status'] = '비활성'; } } $result = [ 'data' => $result['data'], 'recordsTotal' => $result['allCount'], 'recordsFiltered' => $result['allCount'], 'draw' => intval($arg['draw']), ]; return $this->respond($result); }else{ return $this->fail("잘못된 요청"); } } public function getLogs() { if($this->request->isAJAX() && strtolower($this->request->getMethod()) === 'get'){ $arg = $this->request->getGet(); $result = $this->automation->getLogs($arg); foreach ($result['data'] as &$data) { switch ($data['result']) { case 'success': $data['result'] = '실행됨'; break; case 'failed': $data['result'] = '실패'; break; case 'not_execution': $data['result'] = '실행되지 않음'; break; default: $data['result'] = ''; } $descFields = ['schedule_desc', 'target_desc', 'conditions_desc', 'executions_desc']; foreach($descFields as $field) { if(!empty($data[$field])) { $decoded = json_decode($data[$field], true); if(!empty($decoded)){ if($field == 'executions_desc'){ $errorMsgs = []; if(is_array($decoded)){ foreach($decoded as $item) { if(is_array($item)){ if($item['result'] == false) { $errorMsgs[] = '실패 - '.'['.$item['data']['media'].']['.$item['data']['type'].']['.$item['data']['id'].'] '.$item['data']['exec_type'].' => '.$item['data']['exec_value']." ".$item['msg']; } }else{ $errorMsgs = '실패 - '.$decoded['msg']; continue; } } if(empty($errorMsgs)){ $data[$field] = '통과'; }else{ $data[$field] = $errorMsgs; } } }else{ if($decoded !== null) { if(isset($decoded['result']) && $decoded['result'] === true) { $data[$field] = '통과'; } else{ $data[$field] = '실패 - '.(isset($decoded['msg']) ? $decoded['msg'] : ''); } } } } } } } $result = [ 'data' => $result['data'], ]; return $this->respond($result); }else{ return $this->fail("잘못된 요청"); } } public function getLogByAdv() { if($this->request->isAJAX() && strtolower($this->request->getMethod()) === 'get'){ $arg = $this->request->getGet(); $sliceId = explode("_", $arg['id']); $id = $sliceId[1]; $automations = $this->automation->getAutomationByAdv($id); $result = $this->automation->getLogsByAdv($arg, $id); foreach ($result['data'] as &$data) { switch ($data['result']) { case 'success': $data['result'] = '실행됨'; break; case 'failed': $data['result'] = '실패'; break; case 'not_execution': $data['result'] = '실행되지 않음'; break; default: $data['result'] = ''; } $descFields = ['schedule_desc', 'target_desc', 'conditions_desc', 'executions_desc']; foreach($descFields as $field) { if(!empty($data[$field])) { $decoded = json_decode($data[$field], true); if(!empty($decoded)){ if($field == 'executions_desc'){ $errorMsgs = []; if(is_array($decoded)){ foreach($decoded as $item) { if(is_array($item)){ if($item['result'] == false) { $errorMsgs[] = '실패 - '.'['.$item['data']['media'].']['.$item['data']['type'].']['.$item['data']['id'].'] '.$item['data']['exec_type'].' => '.$item['data']['exec_value']." ".$item['msg']; } }else{ $errorMsgs = '실패 - '.$decoded['msg']; continue; } } if(empty($errorMsgs)){ $data[$field] = '통과'; }else{ $data[$field] = $errorMsgs; } } }else{ if($decoded !== null) { if(isset($decoded['result']) && $decoded['result'] === true) { $data[$field] = '통과'; } else{ $data[$field] = '실패 - '.(isset($decoded['msg']) ? $decoded['msg'] : ''); } } } } } } } $result = [ 'data' => $result['data'], 'automation' => $automations, 'recordsTotal' => $result['allCount'], 'recordsFiltered' => $result['allCount'], 'draw' => intval($arg['draw']), ]; return $this->respond($result); }else{ return $this->fail("잘못된 요청"); } } public function createAutomation() { if($this->request->isAJAX() && strtolower($this->request->getMethod()) === 'post'){ $data = $this->request->getRawInput(); $validationResult = $this->validationData($data); if($validationResult['result'] != true){ return $this->failValidationErrors($validationResult); } $mediaMapping = ['광고주' => 'company', '페이스북' => 'facebook', '구글' => 'google', '카카오' => 'kakao']; $typeMapping = ['광고주' => 'advertiser', '매체광고주' => 'account', '캠페인' => 'campaign', '광고그룹' => 'adgroup', '광고' => 'ad']; $execTypeMapping = ['상태' => 'status', '예산' => 'budget']; $execBudgetTypeMapping = ['원' => 'won', '%' => 'percent']; $data['schedule']['schedule_value'] = json_encode($data['schedule']['schedule_value']); $data['schedule']['exec_once'] = !empty($data['schedule']['exec_once']) ? 1 : 0; if(!empty($data['target'])) { foreach ($data['target'] as &$target) { $target['media'] = $mediaMapping[$target['media']] ?? $target['media']; $target['type'] = $typeMapping[$target['type']] ?? $target['type']; } } if(!empty($data['execution'])) { foreach ($data['execution'] as &$execution) { $execution['media'] = $mediaMapping[$execution['media']] ?? $execution['media']; $execution['type'] = $typeMapping[$execution['type']] ?? $execution['type']; $execution['exec_type'] = $execTypeMapping[$execution['exec_type']] ?? $execution['exec_type']; $execution['exec_budget_type'] = $execBudgetTypeMapping[$execution['exec_budget_type']] ?? $execution['exec_budget_type']; } } if(!empty($data['target_create_type']) && $data['target_create_type'] == 'target_seperate'){ $seperateDatas = []; foreach ($data['target'] as $key => $targetData) { $newItem = [ 'schedule' => $data['schedule'], 'target' => [$targetData], 'condition' => $data['condition'], 'detail' => $data['detail'] ]; $newItem['detail']['subject'] = $newItem['detail']['subject']." 개별 적용 - ".$key+1; foreach ($data['execution'] as $executionData) { if($targetData['id'] == $executionData['id'] && $targetData['media'] == $executionData['media']){ $newItem['execution'] = [$executionData]; } continue; } $seperateDatas[] = $newItem; } $db = \Config\Database::connect(); $db->transStart(); foreach ($seperateDatas as $seperateData) { $result = $this->automation->createAutomation($seperateData); if (!$result) { // If creation fails, rollback and exit $db->transRollback(); return false; } } $result = $db->transComplete(); return $this->respond($result); } $result = $this->automation->createAutomation($data); return $this->respond($result); }else{ return $this->fail("잘못된 요청"); } } public function copyAutomation() { if($this->request->isAJAX() && strtolower($this->request->getMethod()) === 'post'){ $data = $this->request->getRawInput(); $result = $this->automation->copyAutomation($data); return $this->respond($result); }else{ return $this->fail("잘못된 요청"); } } public function updateAutomation() { if($this->request->isAJAX() && strtolower($this->request->getMethod()) === 'put'){ $data = $this->request->getRawInput(); if(empty($data['seq'])){ return $this->fail("잘못된 요청"); } $validationResult = $this->validationData($data); if($validationResult['result'] != true){ return $this->failValidationErrors($validationResult); } $mediaMapping = ['광고주' => 'company', '페이스북' => 'facebook', '구글' => 'google', '카카오' => 'kakao']; $typeMapping = ['광고주' => 'advertiser', '매체광고주' => 'account', '캠페인' => 'campaign', '광고그룹' => 'adgroup', '광고' => 'ad']; $execTypeMapping = ['상태' => 'status', '예산' => 'budget']; $execBudgetTypeMapping = ['원' => 'won', '%' => 'percent']; $data['schedule']['schedule_value'] = json_encode($data['schedule']['schedule_value']); $data['schedule']['exec_once'] = $data['schedule']['exec_once'] == "true" ? 1 : 0; if(!empty($data['target'])) { foreach ($data['target'] as &$target) { $target['media'] = $mediaMapping[$target['media']] ?? $target['media']; $target['type'] = $typeMapping[$target['type']] ?? $target['type']; } } if(!empty($data['execution'])) { foreach ($data['execution'] as &$execution) { $execution['media'] = $mediaMapping[$execution['media']] ?? $execution['media']; $execution['type'] = $typeMapping[$execution['type']] ?? $execution['type']; $execution['exec_type'] = $execTypeMapping[$execution['exec_type']] ?? $execution['exec_type']; $execution['exec_budget_type'] = $execBudgetTypeMapping[$execution['exec_budget_type']] ?? $execution['exec_budget_type']; } } $result = $this->automation->updateAutomation($data); return $this->respond($result); }else{ return $this->fail("잘못된 요청"); } } public function deleteAutomation() { if($this->request->isAJAX() && strtolower($this->request->getMethod()) === 'delete'){ $data = $this->request->getRawInput(); $result = $this->automation->deleteAutomation($data); return $this->respond($result); }else{ return $this->fail("잘못된 요청"); } } private function validationData($data) { $validation = \Config\Services::validation(); $validationRules = [ 'schedule.schedule_value' => 'required', ]; $validationMessages = [ 'schedule.schedule_value' => [ 'required' => '일정을 지정해주세요.', ], ]; $validation->setRules($validationRules, $validationMessages); if (!$validation->run($data)) { $result = [ 'result' => false, 'msg' => $validation->getErrors(), ]; return $result; } $validation->reset(); if(!empty($data['target'])){ foreach ($data['condition'] as $condition) { $validationRules = [ 'type' => 'required', 'type_value' => 'required', 'compare' => 'required', 'operation' => 'required', ]; $validationMessages = [ 'type' => ['required' => '조건 항목은 필수 항목입니다.'], 'type_value' => ['required' => '조건 값은 필수 항목입니다.'], 'compare' => ['required' => '일치여부는 필수 항목입니다.'], 'operation' => ['required' => '연산조건은 필수 항목입니다.'], ]; $validation->setRules($validationRules, $validationMessages); if (!$validation->run($condition)) { $result = [ 'result' => false, 'msg' => $validation->getErrors(), ]; return $result; } } $validation->reset(); } if(empty($data['execution'])){ $validationRules['execution'] = 'required'; $validationMessages['execution'] = ['required' => '실행항목을 추가해주세요.']; }else{ foreach ($data['execution'] as $execution) { $validationRules = [ 'order' => 'required', 'exec_type' => 'required', 'exec_value' => 'required', ]; $validationMessages = [ 'order' => ['required' => '실행순서는 필수 항목입니다.'], 'exec_type' => ['required' => '실행항목은 필수 항목입니다.'], 'exec_value' => ['required' => '세부항목은 필수 항목입니다.'], ]; if($execution['exec_type'] == '예산'){ $validationRules['exec_budget_type'] = 'required'; $validationMessages['exec_budget_type'] = ['required' => '단위는 필수 항목입니다.']; } $validation->setRules($validationRules, $validationMessages); if (!$validation->run($execution)) { $result = [ 'result' => false, 'msg' => $validation->getErrors(), ]; return $result; } } } $validation->reset(); $validationRules = ['detail.subject' => 'required']; $validationMessages = ['detail.subject' => ['required' => '제목은 필수 항목입니다.']]; $validation->setRules($validationRules, $validationMessages); if (!$validation->run($data)) { $result = [ 'result' => false, 'msg' => $validation->getErrors(), ]; return $result; } return ['result' => true]; } public function automation() { CLI::write("자동화를 실행합니다.", "light_red"); $automations = $this->automation->getAutomations(); $seqs = []; $logs = []; $step = 1; $total = count($automations); foreach ($automations as $automation) { $result = []; //if($automation['aa_seq'] != '202'){continue;} if(!empty($automation)){ $schedulePassData = $this->checkAutomationSchedule($automation); $result['schedule'] = $schedulePassData; if(!empty($schedulePassData['result'])){ $targets = $this->automation->getAutomationTargets($automation['aa_seq']); if(!empty($targets)){ $checkTargets = []; foreach ($targets as $target) { $targetData = $this->checkAutomationTarget($target); if($targetData['result'] == false){ continue; } $checkTargets[] = $targetData['target']; } $targetDatas = $this->setData($checkTargets); if(empty($targetDatas)){ $resultRow = $this->recordResult([ 'seq' => $automation['aa_seq'], 'status'=> 'not_execution' ]); $resultTarget = [ 'result' => false, 'msg' => '비교 대상 데이터가 존재하지 않습니다.', 'seq' => $automation['aa_seq'], ]; $result['target'] = $resultTarget; if(!empty($resultRow)){ $this->recordLog($result, $resultRow); } continue; }else{ $resultTarget = [ 'result' => true, 'msg' => '대상이 일치합니다.', 'seq' => $automation['aa_seq'], 'target' => $targetDatas, ]; $result['target'] = $resultTarget; } if(!empty($targetDatas)){ $conditionPassData = $this->checkAutomationCondition($targetDatas, $automation['aa_seq']); $result['conditions'] = $conditionPassData; if($conditionPassData['result'] == false){ $resultRow = $this->recordResult($conditionPassData); if(!empty($resultRow)){ $this->recordLog($result, $resultRow); } continue; } $seqs[] = $conditionPassData['seq']; $logs[] = $result; } }else{ $seqs[] = $schedulePassData['seq']; $logs[] = $result; } } } } if(!empty($seqs)){ $executionData = []; foreach ($seqs as $seq) { CLI::showProgress($step++, $total); $executionData[] = $this->automationExecution($seq); } foreach ($executionData as $exec) { $resultRow = $this->recordResult($exec['result']); if(!empty($resultRow)){ foreach ($logs as &$log) { if($log['schedule']['seq'] === $exec['result']['seq']){ $log['executions'] = $exec['log']; $this->recordLog($log, $resultRow); } } } } foreach ($executionData as $exec) { if($exec['result'] == true){ $automation = $this->automation->getAutomationBySeq($exec['result']['seq']); if(!empty($automation['aa_slack_webhook']) && !empty($automation['aa_slack_msg'])){ $slackChat = new SlackChat; $slackChat->sendWebHookMessage($automation['aa_slack_webhook'], $automation['aa_slack_msg']); } } } } CLI::write("자동화 실행 완료", "light_red"); } public function checkAutomationSchedule($automation) { $resultArray = [ 'result' => false, 'status' => 'not_execution', 'msg' => '설정 시간 일치하지 않음', 'seq' => $automation['aa_seq'], ]; $currentDate = new Time('now'); $currentDay = $currentDate->format('N'); $currentTime = $currentDate->toTimeString(); $schedule = $this->convertJsonToTimes($automation['aas_schedule_value']); if (array_key_exists($currentDay, $schedule)) { if (!empty($automation['aas_exec_once']) && !empty($automation['aar_exec_timestamp_success'])) { $execTimestampSuccess = Time::parse($automation['aar_exec_timestamp_success']); if ($execTimestampSuccess->toDateString() === $currentDate->toDateString()) { return $resultArray; } } foreach ($schedule[$currentDay] as $time) { $startTime = Time::parse($time); $endTime = $startTime->addMinutes(30); $now = Time::parse($currentTime); if ($now->isAfter($startTime) && $now->isBefore($endTime)) { $resultArray = [ 'result' => true, 'status' => 'success', 'msg' => '설정 시간 일치', 'seq' => $automation['aa_seq'], ]; return $resultArray; } } } return $resultArray; } public function checkAutomationTarget($target) { $types = ['advertiser', 'account', 'campaign', 'adgroup', 'ad']; $mediaTypes = ['company', 'facebook', 'google', 'kakao']; //값 별로 메소드 매칭 if (in_array($target['aat_type'], $types) && in_array($target['aat_media'], $mediaTypes)) { $methodName = "getTarget" . ucfirst($target['aat_media']); if (method_exists($this->automation, $methodName)) { $data = $this->automation->$methodName($target); if(empty($data)){ return ['result' => false]; } $data = $this->sumData($data); return [ 'result' => true, 'seq' => $target['aat_idx'], 'target' => $data, ]; } } } public function checkAutomationCondition($targetData, $seq) { $types = ['status', 'budget', 'dbcost', 'unique_total', 'spend', 'margin', 'margin_rate', 'sales', 'conversion']; $conditions = $this->automation->getAutomationConditionBySeq($seq); if(empty($conditions)){ return [ "result" => false, 'status' => 'failed', "msg" => '조건이 존재하지 않음', "seq" => $seq, ]; } $isTargetMatched = false; $allConditionsMatched = true; $operation = $conditions[0]['operation']; foreach ($conditions as $condition) { $conditionMatched = false; $message = "일치하는 조건이 없습니다."; if ($condition['type'] === 'status') {//status 비교 if ($targetData['status'] == $condition['type_value']) { $conditionMatched = true; $message = 'status 일치'; }else{ $message = 'status가 일치하지 않습니다.'; } }else{//그 외 필드 비교 foreach ($types as $type) { if ($condition['type'] === $type) { switch ($condition['compare']) { case 'less': $conditionMatched = $targetData[$type] < $condition['type_value']; $message = $conditionMatched ? $type." ".$condition['compare'].' 조건 일치' : $type.'값이 조건값보다 큽니다.'."(".$condition['compare'].")"; break; case 'greater': $conditionMatched = $targetData[$type] > $condition['type_value']; $message = $conditionMatched ? $type." ".$condition['compare'].' 조건 일치' : $type.'값이 조건값보다 작습니다.'."(".$condition['compare'].")"; break; case 'less_equal': $conditionMatched = $targetData[$type] <= $condition['type_value']; $message = $conditionMatched ? $type." ".$condition['compare'].' 조건 일치' : $type.'값이 조건값보다 크거나 같지 않습니다.'."(".$condition['compare'].")"; break; case 'greater_equal': $conditionMatched = $targetData[$type] >= $condition['type_value']; $message = $conditionMatched ? $type." ".$condition['compare'].' 조건 일치' : $type.'값이 조건값보다 작거나 같지 않습니다.'."(".$condition['compare'].")"; break; case 'equal': $conditionMatched = $targetData[$type] == $condition['type_value']; $message = $conditionMatched ? $type." ".$condition['compare'].' 조건 일치' : $type.'값이 조건값과 일치하지 않습니다.'."(".$condition['compare'].")"; break; case 'not_equal': $conditionMatched = $targetData[$type] != $condition['type_value']; $message = $conditionMatched ? $type." ".$condition['compare'].' 조건 일치' : $type.'값이 조건값과 같습니다.'."(".$condition['compare'].")"; break; default: $conditionMatched = false; break; } } } } if($operation == 'or'){ if($conditionMatched){ $isTargetMatched = true; break; } } if($operation == 'and'){ if (!$conditionMatched) { $allConditionsMatched = false; break; } } } if(($operation == 'or' && $isTargetMatched) || ($operation == 'and' && $allConditionsMatched)){ return [ "result" => true, 'status' => 'success', "msg" => $message, "seq" => $seq, ]; }else{ return [ "result" => false, 'status' => 'not_execution', "msg" => $message, "seq" => $seq, ]; } } public function automationExecution($seq) { $executions = $this->automation->getExecution($seq); if(empty($executions)){ return [ "result" => false, 'status' => 'failed', "msg" => '실행항목이 존재하지 않음', "seq" => $seq, ]; } $originalSettings = []; $result = [ 'result' => [], 'log' => [], ]; try { foreach ($executions as $execution) { $zenith = null; switch ($execution['media']) { case 'facebook': $zenith = new ZenithFB(); break; case 'google': $zenith = new ZenithGG(); break; case 'kakao': $zenith = new ZenithKM(); break; default: break; } if($zenith){ switch ($execution['type']) { case 'campaign': $customerId = null; if ($execution['media'] === 'google') { $customerId = $this->google->getCustomerById($execution['id'], 'campaign'); } switch ($execution['exec_type']) { case 'status': if ($execution['media'] === 'facebook') { if($execution['exec_value'] === "ON"){ $status = 'ACTIVE'; }else{ $status = 'PAUSED'; } $originalData = $zenith->getCampaignStatusBudget($execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'status' => $originalData['status'], ]; $return = $zenith->setCampaignStatus($execution['id'], $status); } } else if ($execution['media'] === 'google') { if($execution['exec_value'] === "ON"){ $status = ['status' => 'ENABLED']; }else{ $status = ['status' => 'PAUSED']; } $originalData = $zenith->getCampaignStatusBudget($customerId['customerId'], $execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'status' => $originalData['status'], ]; $return = $zenith->updateCampaign($customerId['customerId'], $execution['id'], $status); }else{ $return = false; } } else if ($execution['media'] === 'kakao') { $originalData = $zenith->getCampaignStatusBudget($execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'status' => $originalData['status'], ]; $return = $zenith->setCampaignOnOff($execution['id'], $execution['exec_value']); if(isset($return['http_code'])){ if($return['http_code'] == 200){ $return = true; }else{ throw new Exception("카카오 캠페인 상태 수정 오류 발생."); } }else{ throw new Exception("카카오 캠페인 상태 수정 오류 발생."); } }else{ $return = false; } } break; case 'budget': if ($execution['media'] === 'facebook') { $originalData = $zenith->getCampaignStatusBudget($execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'budget' => $originalData['budget'] ?? 0, ]; if ($execution['exec_budget_type'] == 'percent') { $adjustedBudget = ($originalData['budget']) * (1 + ($execution['exec_value'] / 100)); $adjustedBudget = round($adjustedBudget); } else { $adjustedBudget = $execution['exec_value']; } $data = [ 'id' => $execution['id'], 'budget' => $adjustedBudget ]; $return = $zenith->updateCampaignBudget($data); }else{ $return = false; } } else if ($execution['media'] === 'google') { $originalData = $zenith->getCampaignStatusBudget($customerId['customerId'], $execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'budget' => $originalData['budget'] ?? 0, ]; if ($execution['exec_budget_type'] == 'percent') { $adjustedBudget = ($originalData['budget']) * (1 + ($execution['exec_value'] / 100)); $adjustedBudget = round($adjustedBudget); } else { $adjustedBudget = $execution['exec_value']; } $return = $zenith->updateCampaignBudget($customerId['customerId'], $execution['id'], ['budget' => $adjustedBudget]); }else{ $return = false; } } else if ($execution['media'] === 'kakao') { $originalData = $zenith->getCampaignStatusBudget($execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'budget' => $originalData['budget'] ?? 0, ]; if ($execution['exec_budget_type'] == 'percent') { $adjustedBudget = ($originalData['budget']) * (1 + ($execution['exec_value'] / 100)); $adjustedBudget = round($adjustedBudget); } else { $adjustedBudget = $execution['exec_value']; } $data = [ 'type' => 'campaign', 'id' => $execution['id'], 'budget' => $adjustedBudget ]; $return = $zenith->setDailyBudgetAmount($data); if(isset($return['code']) && isset($return['msg'])){ throw new Exception($return['msg']); }else{ $return = true; } }else{ $return = false; } } break; default: break; } break; case 'adgroup': $customerId = null; if ($execution['media'] === 'google') { $customerId = $this->google->getCustomerById($execution['id'], 'adgroup'); } switch ($execution['exec_type']) { case 'status': if ($execution['media'] === 'facebook') { if($execution['exec_value'] === "ON"){ $status = 'ACTIVE'; }else{ $status = 'PAUSED'; } $originalData = $zenith->getAdsetStatusBudget($execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'status' => $originalData['status'], ]; $return = $zenith->setAdsetStatus($execution['id'], $status); }else{ $return = false; } } else if ($execution['media'] === 'google') { if($execution['exec_value'] === "ON"){ $status = ['status' => 'ENABLED']; }else{ $status = ['status' => 'PAUSED']; } $originalData = $zenith->getAdgroupStatus($customerId['customerId'], $execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'status' => $originalData['status'], ]; $return = $zenith->updateAdGroup($customerId['customerId'], $execution['id'], $status); }else{ $return = false; } } else if ($execution['media'] === 'kakao') { $originalData = $zenith->getAdgroupStatusBudget($execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'status' => $originalData['status'], ]; $return = $zenith->setAdGroupOnOff($execution['id'], $execution['exec_value']); if(isset($return['http_code'])){ if($return['http_code'] == 200){ $return = true; }else{ throw new Exception("카카오 광고그룹 상태 수정 오류 발생."); } }else{ throw new Exception("카카오 광고그룹 상태 수정 오류 발생."); } }else{ $return = false; } } break; case 'budget': if ($execution['media'] === 'facebook') { $originalData = $zenith->getAdsetStatusBudget($execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'budget' => $originalData['budget'] ?? 0, ]; if ($execution['exec_budget_type'] == 'percent') { $adjustedBudget = ($originalData['budget']) * (1 + ($execution['exec_value'] / 100)); $adjustedBudget = round($adjustedBudget); } else { $adjustedBudget = $execution['exec_value']; } $data = [ 'id' => $execution['id'], 'budget' => $adjustedBudget ]; $return = $zenith->updateAdSetBudget($data); }else{ $return = false; } } else if ($execution['media'] === 'kakao') { $originalData = $zenith->getAdgroupStatusBudget($execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'budget' => $originalData['budget'] ?? 0, ]; if ($execution['exec_budget_type'] == 'percent') { $adjustedBudget = ($originalData['budget']) * (1 + ($execution['exec_value'] / 100)); $adjustedBudget = round($adjustedBudget); } else { $adjustedBudget = $execution['exec_value']; } $data = [ 'type' => 'adgroup', 'id' => $execution['id'], 'budget' => $adjustedBudget ]; $return = $zenith->setDailyBudgetAmount($data); if(isset($return['code']) && isset($return['msg'])){ throw new Exception($return['msg']); }else{ $return = true; } }else{ $return = false; } } break; default: break; } break; case 'ad': $customerId = null; if ($execution['media'] === 'google') { $customerId = $this->google->getCustomerById($execution['id'], 'ad'); } switch ($execution['exec_type']) { case 'status': if ($execution['media'] === 'facebook') { if($execution['exec_value'] === "ON"){ $status = 'ACTIVE'; }else{ $status = 'PAUSED'; } $originalData = $zenith->getAdStatusBudget($execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'status' => $originalData['status'], ]; $return = $zenith->setAdStatus($execution['id'], $status); }else{ $return = false; } } else if ($execution['media'] === 'google') { if($execution['exec_value'] === "ON"){ $status = ['status' => 'ENABLED']; }else{ $status = ['status' => 'PAUSED']; } $originalData = $zenith->getAdStatus($customerId['customerId'], $execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'status' => $originalData['status'], ]; $return = $zenith->updateAdGroupAd($customerId['customerId'], null, $execution['id'], $status); }else{ $return = false; } } else if ($execution['media'] === 'kakao') { $originalData = $zenith->getAdStatusBudget($execution['id']); if(!empty($originalData)){ $originalSettings[] = [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'status' => $originalData['status'], ]; $return = $zenith->setCreativeOnOff($execution['id'], $execution['exec_value']); if(isset($return['http_code'])){ if($return['http_code'] == 200){ $return = true; }else{ throw new Exception("카카오 광고그룹 상태 수정 오류 발생."); } }else{ throw new Exception("카카오 광고그룹 상태 수정 오류 발생."); } }else{ $return = false; } } break; } break; default: break; } if(!empty($return)){ $logData = [ 'media' => $execution['media'], 'id' => $execution['id'], 'type' => $execution['type'], 'change_type' => $execution['exec_type'] == 'status' ? 'status' : 'budget', 'old_value' => '', 'change_value' => $execution['exec_value'], 'nickname' => '자동화', ]; $logger = new AdvLoggerService(); $logger->insertLog($logData); $result['log'][] = [ "result" => true, 'status' => 'success', "msg" => '실행 완료', "seq" => $seq, "data" => [ 'media' => $execution['media'], 'type' => $execution['type'], 'id' => $execution['id'], 'exec_type' => $execution['exec_type'], 'exec_value' => $execution['exec_value'], ] ]; }else{ throw new Exception("Api 오류 발생."); } } } $result['result'] = [ "result" => true, 'status' => 'success', "seq" => $seq, ]; return $result; } catch (Exception $e) { foreach ($originalSettings as $original) { switch ($original['media']) { case 'facebook': $zenith = new ZenithFB(); break; case 'google': $zenith = new ZenithGG(); break; case 'kakao': $zenith = new ZenithKM(); break; default: break; } switch ($original['type']) { case 'campaign': $customerId = null; if ($original['media'] === 'google') { $customerId = $this->google->getCustomerById($original['id'], 'campaign'); } if(isset($original['status'])){ if ($original['media'] === 'facebook') { $zenith->setCampaignStatus($original['id'], $original['status']); } else if ($original['media'] === 'google') { $zenith->updateCampaign($customerId['customerId'], $original['id'], $original['status']); } else if ($original['media'] === 'kakao') { $zenith->setCampaignOnOff($original['id'], $original['status']); } } if(isset($original['budget'])){ if ($original['media'] === 'facebook') { $data = [ 'id' => $original['id'], 'budget' => $original['budget'] ]; $zenith->updateCampaignBudget($data); } else if ($original['media'] === 'google') { $zenith->updateCampaignBudget($customerId['customerId'], $original['id'], ['budget' => $original['budget']]); } else if ($original['media'] === 'kakao') { $data = [ 'type' => 'campaign', 'id' => $original['id'], 'budget' => $original['budget'] ]; $zenith->setDailyBudgetAmount($data); } } break; case 'adgroup': $customerId = null; if ($original['media'] === 'google') { $customerId = $this->google->getCustomerById($original['id'], 'adgroup'); } if(isset($original['status'])){ if ($original['media'] === 'facebook') { $zenith->setAdsetStatus($original['id'], $original['status']); } else if ($original['media'] === 'google') { $zenith->updateAdGroup($customerId['customerId'], $original['id'], $original['status']); } else if ($original['media'] === 'kakao') { $zenith->setAdGroupOnOff($original['id'], $original['status']); } } if(isset($original['budget'])){ if ($original['media'] === 'facebook') { $data = [ 'id' => $original['id'], 'budget' => $original['budget'] ]; $zenith->updateAdSetBudget($data); } else if ($original['media'] === 'kakao') { $data = [ 'type' => 'adgroup', 'id' => $original['id'], 'budget' => $original['budget'] ]; $zenith->setDailyBudgetAmount($data); } } break; case 'ad': $customerId = null; if ($original['media'] === 'google') { $customerId = $this->google->getCustomerById($original['id'], 'ad'); } if(isset($original['status'])){ if ($original['media'] === 'facebook') { $zenith->setAdStatus($original['id'], $original['status']); } else if ($original['media'] === 'google') { $zenith->updateAdGroupAd($customerId['customerId'], null, $original['id'], $original['status']); } else if ($original['media'] === 'kakao') { $zenith->setCreativeOnOff($original['id'], $original['status']); } } break; default: break; } } $result['result'] = [ "result" => false, 'status' => 'failed', "seq" => $seq ]; $result['log'] = [ "result" => false, 'status' => 'failed', 'msg' => $e->getMessage(), "seq" => $seq, ]; return $result; } } /* public function checkAutomationCondition($target) { $types = ['budget', 'dbcost', 'dbcount', 'cost', 'margin', 'margin_rate', 'sale', 'impression', 'click', 'cpc', 'ctr', 'conversion']; $conditions = $this->automation->getAutomationConditionBySeq($target['aa_seq']); if(empty($conditions)){return false;} //순서 재정렬 usort($conditions, function($a, $b) { return $a['order'] - $b['order']; }); $isTargetMatched = false; $allConditionsMatched = true; foreach ($conditions as $condition) { $conditionMatched = false; $message = "일치하는 조건이 존재하지 않습니다."; if ($condition['type'] === 'status') {//status 비교 if ($target['status'] == $condition['type_value']) { $conditionMatched = true; $message = 'status 일치'; }else{ $message = 'status가 일치하지 않습니다.'; } }else{//그 외 필드 비교 foreach ($types as $type) { if ($condition['type'] === $type) { switch ($condition['compare']) { case 'less': $conditionMatched = $target[$type] < $condition['type_value']; $message = $conditionMatched ? $condition['compare'].' 조건 일치' : $type.'값이 조건값보다 큽니다.'."(".$condition['compare'].")";; break; case 'greater': $conditionMatched = $target[$type] > $condition['type_value']; $message = $conditionMatched ? $condition['compare'].' 조건 일치' : $type.'값이 조건값보다 작습니다.'."(".$condition['compare'].")";; break; case 'less_equal': $conditionMatched = $target[$type] <= $condition['type_value']; $message = $conditionMatched ? $condition['compare'].' 조건 일치' : $type.'값이 조건값보다 크거나 같지 않습니다.'."(".$condition['compare'].")";; break; case 'greater_equal': $conditionMatched = $target[$type] >= $condition['type_value']; $message = $conditionMatched ? $condition['compare'].' 조건 일치' : $type.'값이 조건값보다 작거나 같지 않습니다.'."(".$condition['compare'].")";; break; case 'equal': $conditionMatched = $target[$type] == $condition['type_value']; $message = $conditionMatched ? $condition['compare'].' 조건 일치' : $type.'값이 조건값과 일치하지 않습니다.'."(".$condition['compare'].")";; break; case 'not_equal': $conditionMatched = $target[$type] != $condition['type_value']; $message = $conditionMatched ? $condition['compare'].' 조건 일치' : $type.'값이 조건값과 같습니다.'."(".$condition['compare'].")"; break; default: $conditionMatched = false; $message = '비교할 조건이 존재하지 않습니다.'; break; } } } } if($condition['operation'] == 'or'){ if($conditionMatched){ $isTargetMatched = true; break; }else{ //조건이 하나이고 조건에 해당되지 않을때 if(count($conditions) == 1){ $allConditionsMatched = false; } //and 조건이 없을때 if (!in_array('and', array_column($conditions, 'operation'))) { $allConditionsMatched = false; } } } if($condition['operation'] == 'and'){ if(!$conditionMatched){ $allConditionsMatched = false; //뒤에 or 조건이 있을수 있어서 or 조건이 없을때만 break $nextKey = array_search($condition, $conditions) + 1; if ($nextKey < count($conditions)) { if ($conditions[$nextKey]['operation'] != 'or') { break; } } else { break; } } } } if($isTargetMatched || $allConditionsMatched){ $matchedArray['match'][] = [ 'aa_seq' => $target['aa_seq'], 'msg' => $message ]; }else{ $matchedArray['notMatch'][] = [ 'aa_seq' => $target['aa_seq'], 'msg' => $message ]; } return $matchedArray; } */ private function sumData($data) { $formatData = []; $formatData['budget'] = $formatData['click'] = $formatData['spend'] = $formatData['sales'] = $formatData['unique_total'] = $formatData['margin'] = 0; if(isset($data[0]['status']) && in_array($data[0]['status'], ['ON', '1', 'ACTIVE', 'ENABLED'])){ $formatData['status'] = 'ON'; }else{ $formatData['status'] = 'OFF'; } foreach ($data as $d) { $formatData['budget'] += $d['budget']; //$formatData['impressions'] += $d['impressions']; $formatData['click'] += $d['click']; $formatData['spend'] += $d['spend']; $formatData['sales'] += $d['sales']; $formatData['unique_total'] += $d['unique_total']; $formatData['margin'] += $d['margin']; } return $formatData; } private function setData($data) { $formatData = []; $formatData['budget'] = $formatData['click'] = $formatData['spend'] = $formatData['sales'] = $formatData['unique_total'] = $formatData['margin'] = $formatData['dbcost'] = $formatData['conversion']= $formatData['margin_rate'] = 0; if(isset($data[0]['status']) && in_array($data[0]['status'], ['ON', '1', 'ACTIVE', 'ENABLED'])){ $formatData['status'] = 'ON'; }else{ $formatData['status'] = 'OFF'; } foreach ($data as $d) { $formatData['budget'] += $d['budget']; //$formatData['impressions'] += $d['impressions']; $formatData['click'] += $d['click']; $formatData['spend'] += $d['spend']; $formatData['sales'] += $d['sales']; $formatData['unique_total'] += $d['unique_total']; $formatData['margin'] += $d['margin']; //CPC(Cost Per Click: 클릭당단가 (1회 클릭당 비용)) = 지출액/링크클릭 /* if($formatData['click'] > 0){ $formatData['cpc'] = round($formatData['spend'] / $formatData['click']); } */ //CTR(Click Through Rate: 클릭율 (노출 대비 클릭한 비율)) = (링크클릭/노출수)*100 /* if($formatData['impressions'] > 0){ $formatData['ctr'] = ($formatData['click'] / $formatData['impressions']) * 100; } */ //CPA(Cost Per Action: 현재 DB단가(전환당 비용)) = 지출액/유효db if($formatData['unique_total'] > 0){ $formatData['dbcost'] = round($formatData['spend'] / $formatData['unique_total']); } //CVR(Conversion Rate:전환율 = (유효db / 링크클릭)*100 if ($formatData['click'] > 0) { $formatData['conversion'] = ($formatData['unique_total'] / $formatData['click']) * 100; } //수익률 = (수익/매출액)*100 if ($formatData['sales'] > 0) { $formatData['margin_rate'] = round(($formatData['margin'] / $formatData['sales']) * 100); } else { $formatData['margin_rate'] = 0; } } return $formatData; } private function recordResult($result) { $resultData = [ 'idx' => $result['seq'], 'result' => $result['status'], 'exec_timestamp' => date('Y-m-d H:i:s') ]; $row = $this->automation->recodeResult($resultData); return $row; } private function recordLog($log, $resultRow) { $data = []; if(!empty($log['schedule'])){ $data['idx'] = $resultRow['seq']; $data['reg_datetime'] = $resultRow['reg_datetime']; $data['schedule_desc'] = json_encode($log['schedule'], JSON_UNESCAPED_UNICODE); } if(isset($log['target'])) { $data['idx'] = $resultRow['seq']; $data['reg_datetime'] = $resultRow['reg_datetime']; $data['target_desc'] = json_encode($log['target'], JSON_UNESCAPED_UNICODE); } if(isset($log['conditions'])) { $data['idx'] = $resultRow['seq']; $data['reg_datetime'] = $resultRow['reg_datetime']; $data['conditions_desc'] = json_encode($log['conditions'], JSON_UNESCAPED_UNICODE); } if(isset($log['executions'])) { $data['reg_datetime'] = $resultRow['reg_datetime']; $data['executions_desc'] = json_encode($log['executions'], JSON_UNESCAPED_UNICODE); } if(!empty($data)){ $this->automation->recodeLog($data); } } private function convertJsonToTimes($json) { $data = json_decode($json, true); $convertedTimes = []; foreach ($data as $key => $times) { foreach ($times as $time) { // 값을 2로 나누어 시간으로 변환 $hour = floor($time / 2); $minute = ($time % 2) * 30; $convertedTime = sprintf("%d:%02d", $hour, $minute); $convertedTimes[$key][] = $convertedTime; } } return $convertedTimes; } }