diff --git a/app/Commands/NaverWorker.php b/app/Commands/NaverWorker.php index 0e73b0e..b57b206 100644 --- a/app/Commands/NaverWorker.php +++ b/app/Commands/NaverWorker.php @@ -5,6 +5,8 @@ namespace App\Commands; use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; +// 헬퍼 로드 (app/Helpers/log_helper.php 가 있어야 함 autoload 설정 넣어놓았음) + class NaverWorker extends BaseCommand { protected $group = 'Workers'; @@ -25,23 +27,31 @@ class NaverWorker extends BaseCommand while (true) { try { - // 'brPop'은 [큐이름, 데이터] 형태의 배열을 반환합니다. + // 1. Redis에서 데이터를 꺼냄 $result = $redis->brPop(['naver:raw_queue'], 30); - $result = $redis->brPop(['naver:raw_queue'], 30); if ($result) { + $rawData = $result[1]; try { - $payload = json_decode($result[1], true); - $this->processTask($payload); // 실제 DB 저장 등 + $payload = json_decode($rawData, true); + + // 2. 실제 작업 수행 + $this->processTask($payload); + + CLI::write("✅ Success: " . ($payload['articleNumber'] ?? 'Unknown'), 'cyan'); + } catch (\Exception $e) { - // 처리 실패 시 다시 큐에 넣어서 나중에 재시도하게 함 - $redis->lPush('naver:raw_queue', $result[1]); - CLI::error("처리 실패로 데이터를 큐에 다시 넣었습니다."); + // 3. 실패 시: Helper 함수 사용 + $errorMsg = $e->getMessage(); + + CLI::error("❌ Task Failed: $errorMsg"); + + // 공통 헬퍼 함수 호출 + write_custom_log("FAILED_DATA | Error: $errorMsg | Data: $rawData", 'ERROR', 'failed'); } } } catch (\Exception $e) { CLI::error("Worker Loop Error: " . $e->getMessage()); - // 루프가 너무 빨리 돌며 에러를 뿜는 것을 방지 sleep(1); } } @@ -49,8 +59,128 @@ class NaverWorker extends BaseCommand private function processTask($payload) { - // 여기서 DB 모델(ConfirmModel)을 불러와 저장하고 - // CURL을 사용하여 네이버 API를 호출하는 로직을 작성합니다. - CLI::write("Processing: " . ($payload['request_data']['articleNumber'] ?? 'Unknown')); + // 실제 비즈니스 로직 + // { + // "articleNumber": "2500000001", + // "reqeustType": "REG", + // "requestDatetime": "2025-12-22 19:20:12" + // } + + // 1. 필수 데이터 검증 + if (empty($payload['articleNumber']) || empty($payload['reqeustType'])) { + throw new \Exception("필수 파라미터 누락"); + } + + $articleNumber = $payload['articleNumber']; + /* + REG : 수동검증요청 , + MOD 매물정보수정 - 매물제공업체의 매물정보 수정으로 재검증요청 1차 실패 후 재검증 요청 , + CNC : 사용자취소 , + FIN : 서비스 노출/대기 검수완료 상태(필요한 상태인지 확인 필요) + */ + $requestType = $payload['reqeustType']; + + // 2. 네이버 API 클라이언트 초기화 + $naverClient = new \App\Libraries\NaverApiClient(); + // 3. 요청 유형에 따른 처리 + if (in_array($requestType, ['REG', 'MOD'])) { + // 매물 정보 조회 + $articleInfojson = $naverClient->getArticleInfo($articleNumber); + if (!$articleInfojson || !isset($articleInfojson['data']) || empty($articleInfojson['code'] !== 'success')) { + throw new \Exception("매물 정보 조회 실패: $articleNumber ::: message : " . ($articleInfojson['message'] ?? 'No response')); + } + // 받아온 정보 로그 기록 + write_custom_log("ARTICLE_INFO | ArticleNumber: $articleNumber | Info: " . json_encode($articleInfo , JSON_UNESCAPED_UNICODE), 'INFO', 'service'); + CLI::write("Fetched Article Info: " . json_encode($articleInfo)); + + $articleInfo = $articleInfojson['data']; + + /** + * $articleInfo['verificationTypeCode'] + * S : 현장확인매물 + * D : 홍보확인서 + * N : 신홍보확인서 + * M : 모바일 + * T : 전화 + * O : 모바일확인V2 + * + * S - reciept , result 테이블 사용 + * D,N,M,T,O - v2_vrfc_req , v2_article_info , v2_article_info_etc , v2_article_fail 테이블 사용 + */ + + // 공통 정보 + "articleNumber":"2500016420", + "verificationTypeCode":"S", + "cpId": "naver", + "cpArticleNumber":"nv-2025052201", + "realEstateTypeCode":"A01", + "realEstateType": "아파트", + "tradeTypeCode":"A1", + "tradeType": "매매", + "isUnregisteredVerificationRequested": false, + "isBuildingRegisterAreaCheckRequested":false, + "isAutoVerificationRequested": false, + + // $comp_sq = '2'; + // $rcpt_rating = '3'; + // $rcpt_key = $articleInfo['articleNumber']; // 매물 번호와 동일 + // $rcpt_cpid = $articleInfo['cpId']; + // $rcpt_atclno = $articleInfo['articleNumber']; + // $cpArticleNumber = $articleInfo['cpArticleNumber']; + + // $rcpt_deal_type + // $rcpt_product_info1 + // $rcpt_product_info2 + + // $rcpt_living_yn + // $rcpt_sido + // $rcpt_gugun + // $rcpt_dong + // $rcpt_x + // $rcpt_y + + // $rcpt_hscp_nm + // $rcpt_hscp_no + // $rcpt_ptp_nm + // $rcpt_ptp_no + + // $rcpt_dtl_addr + // $rcpt_li_addr + // $rcpt_jibun_addr + // $rcpt_etc_addr + // $rcpt_ref_addr + + // $rcpt_floor + // $rcpt_floor2 + // $rcpt_product + // $rcpt_product_nm + // $agent_id + // $agent_nm + // $agent_contact_tel + // $agent_head_tel + // $agent_fax + // $rsrv_date + // $rsrv_tm_ap + + + + + if ($articleInfo['verificationTypeCode'] == 'S') { // 현장확인매물 + // 현장확인매물 처리 로직 + $receiptModel = new ReceiptModel(); + + + } else { // 그외 일반매물 + + } + + + } + + + + + + CLI::write("Processing: " . ($payload['articleNumber'] ?? 'Unknown')); } } \ No newline at end of file diff --git a/app/Models/ReceiptModel.php b/app/Models/ReceiptModel.php new file mode 100644 index 0000000..088a677 --- /dev/null +++ b/app/Models/ReceiptModel.php @@ -0,0 +1,97 @@ +db->table('receipt a'); + + // 1. SELECT 문 구성 + $builder->select(" + a.rcpt_sq, a.comp_sq, a.rcpt_rating, ... (중략) ..., + DATE_FORMAT(COALESCE(b.rsrv_date, a.rsrv_date), '%Y-%m-%d') AS rsrv_date, + get_code_name('RECEIPT_STATUS1', LEFT(a.rcpt_stat, 2)) AS rcpt_stat_nm, + CASE WHEN imgs.has_I1 = 1 THEN 'Y' ELSE 'N' END AS conf_img_yn + ", false); + + // 2. JOIN 설정 + $builder->join('result b', 'a.rcpt_sq = b.rcpt_sq', 'left outer'); + $builder->join('region_codes c', 'a.rcpt_dong = c.region_cd', 'inner'); + $builder->join('departments d', 'b.dept_sq = d.dept_sq', 'left outer'); + $builder->join('users u', 'b.usr_sq = u.usr_sq', 'left outer'); + + // 서브쿼리 조인 (imgs) + $subQuery = $this->db->table('result_imgs') + ->select("rsrv_sq") + ->selectMax("CASE WHEN img_type = 'I5' AND use_yn = 'Y' THEN 1 ELSE 0 END", "has_I5") + ->selectMax("CASE WHEN img_type = 'I1' AND use_yn = 'Y' THEN 1 ELSE 0 END", "has_I1") + ->groupBy("rsrv_sq") + ->getCompiledSelect(); + + $builder->join("($subQuery) imgs", "imgs.rsrv_sq = b.rsrv_sq", "left", false); + + // 3. WHERE 조건 (권한 및 기본 필터) + if (in_array($params['usr_level'], ['4', '40'])) { + if (!empty($params['child_dept'])) { + $builder->whereIn('b.dept_sq', $params['child_dept']); + } else { + $builder->where('b.usr_sq', $params['usr_sq']); + } + } + + // 기본 3개월 데이터 제한 + $builder->where('a.insert_tm >= DATE_ADD(CURDATE(), INTERVAL -3 MONTH)', null, false); + + // 4. 검색 조건 동적 생성 + if (!empty($params['rcpt_atclno'])) { + $builder->where('a.rcpt_atclno', $params['rcpt_atclno']); + } else { + if (!empty($params['sdate'])) { + $builder->where('a.insert_tm >=', $params['sdate'] . ' 00:00:00'); + $builder->where('a.insert_tm <', date('Y-m-d', strtotime($params['edate'] . ' +1 day'))); + } + + // 중개사/매도자 통합 검색 (Group Start/End 사용) + if (!empty($params['agent_nm'])) { + $builder->groupStart() + ->like('a.agent_nm', $params['agent_nm']) + ->orLike('a.sellr_nm', $params['agent_nm']) + ->groupEnd(); + } + + // 상태(Status) 다중 체크 + if ($params['stat_all'] !== 'Y' && !empty($params['stat_arr'])) { + $builder->groupStart(); + foreach ($params['stat_arr'] as $stat) { + $builder->orLike('a.rcpt_stat', $stat, 'after'); + } + $builder->groupEnd(); + } + } + + // 5. 정렬 및 페이징 + $builder->orderBy('a.rcpt_atclno', 'desc'); + + // 데이터 수 조회를 위해 복제 또는 countAllResults 활용 + $totalCount = 0; + if ($total) { + $countBuilder = clone $builder; + $totalCount = $countBuilder->countAllResults(false); + } + + $offset = ($pageNum - 1) * $pageSize; + $result = $builder->get($pageSize, $offset)->getResultArray(); + + return [ + 'data' => $result, + 'total' => $totalCount + ]; + } +} \ No newline at end of file diff --git a/worker/api_receiver.php b/worker/api_receiver.php index 6342f8f..8b3984e 100644 --- a/worker/api_receiver.php +++ b/worker/api_receiver.php @@ -66,7 +66,7 @@ try { } catch (Exception $e) { // 7. 장애 발생 시 로그 기록 (시스템 로그) - writeLog( json_encode($api_info) ." | Received: " . json_encode($data), 'ERROR'); + writeLog( 'Exception :' . apiResponse($data) , 'ERROR'); http_response_code(500);