connect('redis', 6379); $redis->select(9); CLI::write(CLI::color('🟢 Naver Worker running...', 'green')); } catch (\Exception $e) { CLI::error("Redis 연결 불가: " . $e->getMessage()); return; } while (true) { $result = $redis->brPop(['naver:raw_queue'], 30); if (!$result) { // 데이터가 없어서 타임아웃 난 경우. // 굳이 sleep 안 해도 바로 다음 brPop이 다시 30초 대기를 시작함. continue; } if ($result) { $rawData = $result[1]; // [1] 꺼내자마자 DB에 원문 저장 (2차 임시 저장) $logId = $logModel->insert([ 'raw_payload' => $rawData, 'status' => 'INIT' ]); try { $responseJson = json_decode($result[1], true); $payload = $responseJson['request_data'] ?? []; if (empty($payload)) { throw new \Exception("빈 페이로드 데이터"); } // 서비스의 함수 하나로 모든 처리 완료 $insertId = $naverService->processArticle($payload); // [3] 성공 시 로그 업데이트 $logModel->update($logId, [ 'atcl_no' => $payload['articleNumber'] ?? null, 'status' => 'SUCCESS', 'target_db_id' => $insertId ]); CLI::write("✅ Success! DB ID: $insertId", 'cyan'); } catch (\Exception $e) { CLI::error("❌ Task Failed: " . $e->getMessage()); // 실패 로그는 여기서 남김 helper('log'); write_custom_log("FAILED_DATA | Error: " . $e->getMessage(), 'ERROR', 'failed'); } } } } private function insertVrfc($payload) { // 1. 필수 데이터 검증 if (empty($payload['articleNumber']) || empty($payload['requestType'])) { throw new \Exception("필수 파라미터 누락"); } $articleNumber = $payload['articleNumber']; $requestType = $payload['requestType']; $requestDatetime = date('Y-m-d H:i:s', strtotime($payload['requestDatetime'])); $vrfcModel = model(\App\Models\VrfcReqModel::class); // 중복 요청 체크 $existing = $vrfcModel->where('atcl_no', $articleNumber) ->where('req_type', $requestType) ->first(); if ($existing) { throw new \Exception("중복 요청: " . $articleNumber . " / " . $requestType); } // 2. 네이버 API 클라이언트 초기화 $naverClient = new \App\Libraries\NaverApiClient(); // 매물 정보 조회 $articleInfojson = $naverClient->getArticleInfo($articleNumber); // if (!$articleInfojson || !isset($articleInfojson['data']) || empty($articleInfojson['code'] !== 'success')) { // throw new \Exception("매물 정보 조회 실패: $articleNumber ::: message : " . ($articleInfojson['message'] ?? 'No response')); // } if (!$articleInfojson || !isset($articleInfojson['data']) || $articleInfojson['code'] !== 'success') { $msg = $articleInfojson['message'] ?? 'No message'; throw new \Exception("네이버 API 응답 에러: $articleNumber | 메시지: $msg"); } $articleInfo = $articleInfojson['data']; // 받아온 정보 로그 기록 CLI::write("DEBUG: write_custom_log 호출 직전"); write_custom_log("ARTICLE_INFO | ArticleNumber: $articleNumber | Info: " . json_encode($articleInfo , JSON_UNESCAPED_UNICODE), 'INFO', 'service'); CLI::write("DEBUG: write_custom_log 호출 완료"); /** * $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 테이블 사용 */ // {"code":"success","message":"","data":{"articleNumber":"2500000001","realEstateType":"빌라/연립","realEstateTypeCode":"C02","cpId":"toad","cpArticleNumber":"15882606","tradeType":"전세","tradeTypeCode":"B1","statusTypeCode":"E12","verificationTypeCode":"M","isUnregisteredVerificationRequested":false,"isBuildingRegisterAreaCheckRequested":false,"isAutoVerificationRequested":false,"exposureStartDateTime":"2025-01-02 09:12:02","facilities":{"roomCount":2,"bathroomCount":1},"address":{"legalDivision":{"cityNumber":"1100000000","divisionNumber":"1168000000","sectorNumber":"1168010700","legalDivisionAddress":"서울특별시 강남구 신사동"},"isVirtualAddress":false,"correspondenceFloorCount":2,"longitude":0.0,"latitude":0.0},"space":{"totalSpace":34.5,"groundSpace":34.5,"buildingSpace":34.5,"supplySpace":34.5,"exclusiveSpace":34.5},"price":{"dealAmount":777777777,"warrantyAmount":2999999999,"leaseAmount":7777777},"floor":{"correspondenceFloorCount":2,"totalFloorCount":3,"undergroundFloorCount":null}}} $comp_sq = '2'; $rcpt_rating = '3'; $files = $articleInfo['files'] ?? []; $certRegister = []; $confirm_doc_img_url = []; $referenceFileUrl = []; foreach ($files as $file) { // 파일 처리 로직 $fileTypeCode = $file['fileTypeCode']; if ( $fileTypeCode == 'RCDOC' ) { $certRegister[] = $file['fileUrl']; } elseif ( $fileTypeCode == 'ADDOC' ) { $confirm_doc_img_url[] = $file['fileUrl']; } elseif ( $fileTypeCode == 'REFER') { $referenceFileUrl[] = $file['fileUrl']; } } $vrfc_params = [ 'reqSeq' => '', 'atcl_no' => $articleInfo['articleNumber'], 'step' => '', 'cpid' => $articleInfo['cpId'], 'cp_atcl_id' => $articleInfo['cpArticleNumber'], 'trade_type' => $articleInfo['tradeTypeCode'], 'realtor_nm' => $articleInfo['realtor']['realtorName'], 'realtor_tel_no' => $articleInfo['realtor']['representativeCellphoneNumber'], 'seller_tel_no' => $articleInfo['seller']['sellerTelephoneNumber'], 'vrfc_type' => $articleInfo['verificationTypeCode'], 'rgbk_confirm' => $articleInfo['isUnregisteredVerificationRequested'] ? 'Y' : 'N', 'req_type' => '', 'rdate' => $requestDatetime ?? db_now('Y-m-d H:i:s'), 'cpTelNo' => $articleInfo['seller']['sellerTelephoneNumber'], 'stat_cd' => '10', 'try_cnt' => '0', 'insert_user' => 'admin', 'insert_tm' => db_now(), 'memo' => '', 'contact_fail_cnt' => '0', 'sync_yn' => 'N', 'reg_try_cnt' => '0', 'tel_fail_cause' => null, 'rgbk_confirm_owner_nm' => $articleInfo['seller']['ownerName'] ?? null, 'direct_trad_yn' => $articleInfo['seller']['isDirectTrade'] === true ? 'Y' : 'N', 'confirm_doc_img_url' => json_encode($confirm_doc_img_url, JSON_UNESCAPED_UNICODE), 'confirm_doc_owner_check_yn' => '', 'owner_verifiable' => null, 'vrfc_cmpl_type' => null, 'rgbk_doc_img_url' => null, 'certRegister' => json_encode($certRegister, JSON_UNESCAPED_UNICODE), 'referenceFileUrl' => json_encode($referenceFileUrl, JSON_UNESCAPED_UNICODE), ]; write_custom_log("VRFC_PARAMS | " . json_encode($vrfc_params , JSON_UNESCAPED_UNICODE), 'INFO', 'service'); // if ($articleInfo['verificationTypeCode'] == 'S') { // 현장확인매물 // // 현장확인매물 처리 로직 // } else { // 그외 일반매물 // // V2 매물 처리 로직 // $v2ArticleModel = model(\App\Models\V2ArticleModel::class); // // 매물 번호로 중복 체크 // $existingV2 = $v2ArticleModel->where('atcl_no', $articleInfo['articleNumber'])->first(); // if ($existingV2) { // throw new \Exception("중복 매물 번호: " . $articleInfo['articleNumber']); // } // $v2ArticleModel->insert($vrfc_params); // $vr_sq = $v2ArticleModel->getInsertID(); // 방금 삽입한 vr_sq 값 가져오기 // CLI::write("Inserted V2 Article with vr_sq: " . $vr_sq); // // 오류 처리 // if ($v2ArticleModel->errors()) { // $errorMessages = implode(', ', $v2ArticleModel->errors()); // throw new \Exception("V2 매물 삽입 오류: " . $errorMessages); // } // } } }