vendorInfluencerModel = new VendorInfluencerMappingModel(); $this->statusHistoryModel = new VendorInfluencerStatusHistoryModel(); $this->vendorModel = new VendorModel(); $this->userModel = new UserModel(); } /** * 벤더사의 인플루언서 요청 목록 조회 (히스토리 테이블 기반) */ public function getInfluencerRequests() { try { $request = $this->request->getJSON(); $vendorSeq = $request->vendorSeq ?? null; $status = $request->status ?? null; $page = $request->page ?? 1; $size = $request->size ?? 20; log_message('debug', 'getInfluencerRequests 호출: ' . json_encode([ 'vendorSeq' => $vendorSeq, 'status' => $status, 'page' => $page, 'size' => $size ])); if (!$vendorSeq) { return $this->response->setStatusCode(400)->setJSON([ 'success' => false, 'message' => '벤더사 SEQ는 필수입니다.' ]); } $result = $this->vendorInfluencerModel->getInfluencerRequestsByVendor($vendorSeq, $page, $size, $status); // 통계 계산 $stats = $this->statusHistoryModel->getStatusStatsByVendor($vendorSeq); $statsFormatted = [ 'pending' => 0, 'approved' => 0, 'rejected' => 0, 'total' => 0 ]; foreach ($stats as $stat) { $statsFormatted['total'] += $stat['count']; switch ($stat['STATUS']) { case 'PENDING': $statsFormatted['pending'] = $stat['count']; break; case 'APPROVED': $statsFormatted['approved'] = $stat['count']; break; case 'REJECTED': $statsFormatted['rejected'] = $stat['count']; break; } } log_message('debug', 'API 응답 데이터: ' . json_encode([ 'items_count' => count($result['data']), 'pagination' => $result['pagination'], 'stats' => $statsFormatted ])); // 프론트엔드에서 기대하는 응답 구조에 맞춤 return $this->response->setJSON([ 'success' => true, 'data' => [ 'items' => $result['data'], // 프론트엔드에서 data.items로 접근 'total' => $result['pagination']['total'], 'page' => $result['pagination']['currentPage'], 'totalPages' => $result['pagination']['totalPages'], 'size' => $result['pagination']['limit'], 'stats' => $statsFormatted ] ]); } catch (\Exception $e) { log_message('error', '인플루언서 요청 목록 조회 오류: ' . $e->getMessage()); log_message('error', '스택 트레이스: ' . $e->getTraceAsString()); return $this->response->setStatusCode(500)->setJSON([ 'success' => false, 'message' => '요청 목록 조회 중 오류가 발생했습니다.', 'error' => $e->getMessage() ]); } } /** * 인플루언서 요청 승인/거절 처리 (히스토리 테이블 기반) */ public function processInfluencerRequest() { try { $request = $this->request->getJSON(); $mappingSeq = $request->mappingSeq ?? null; $action = $request->action ?? null; // 'approve' or 'reject' $processedBy = $request->processedBy ?? null; $responseMessage = $request->responseMessage ?? ''; log_message('debug', '승인 처리 요청: ' . json_encode([ 'mappingSeq' => $mappingSeq, 'action' => $action, 'processedBy' => $processedBy, 'responseMessage' => $responseMessage ])); if (!$mappingSeq || !$action || !$processedBy) { return $this->response->setStatusCode(400)->setJSON([ 'success' => false, 'message' => '필수 파라미터가 누락되었습니다. (mappingSeq, action, processedBy 필요)' ]); } // action 검증 if (!in_array($action, ['approve', 'reject'])) { return $this->response->setStatusCode(400)->setJSON([ 'success' => false, 'message' => 'action은 approve 또는 reject만 가능합니다.' ]); } // 매핑 정보와 현재 상태 확인 $mapping = $this->vendorInfluencerModel->getWithCurrentStatus($mappingSeq); if (!$mapping) { return $this->response->setStatusCode(404)->setJSON([ 'success' => false, 'message' => '요청을 찾을 수 없습니다.' ]); } // 현재 상태가 PENDING인지 확인 if ($mapping['CURRENT_STATUS'] !== 'PENDING') { return $this->response->setStatusCode(400)->setJSON([ 'success' => false, 'message' => '이미 처리된 요청입니다. 현재 상태: ' . $mapping['CURRENT_STATUS'] ]); } // 처리자 확인 $processingUser = $this->validateProcessor($processedBy); if (!$processingUser['success']) { return $this->response->setStatusCode(400)->setJSON($processingUser); } // 상태 변경 $newStatus = ($action === 'approve') ? 'APPROVED' : 'REJECTED'; $statusMessage = $responseMessage ?: ($action === 'approve' ? '승인 처리됨' : '거부 처리됨'); log_message('debug', "상태 변경: {$mapping['CURRENT_STATUS']} → {$newStatus}"); // 히스토리 테이블에 상태 변경 기록 $this->statusHistoryModel->changeStatus($mappingSeq, $newStatus, $statusMessage, $processedBy); // 메인 테이블 업데이트 (응답 관련 정보) $this->vendorInfluencerModel->update($mappingSeq, [ 'RESPONSE_MESSAGE' => $responseMessage, 'RESPONSE_DATE' => date('Y-m-d H:i:s'), 'APPROVED_BY' => $processedBy ]); // 승인인 경우 파트너십 시작일 설정 if ($action === 'approve') { $this->vendorInfluencerModel->update($mappingSeq, [ 'PARTNERSHIP_START_DATE' => date('Y-m-d H:i:s') ]); } log_message('debug', "승인 처리 완료: action={$action}, newStatus={$newStatus}"); return $this->response->setJSON([ 'success' => true, 'message' => $action === 'approve' ? '요청이 승인되었습니다.' : '요청이 거부되었습니다.', 'data' => [ 'mappingSeq' => $mappingSeq, 'action' => $action, 'status' => $newStatus, 'processedBy' => $processingUser['data']['name'], 'responseMessage' => $responseMessage ] ]); } catch (\Exception $e) { log_message('error', '승인 처리 중 예외 발생: ' . $e->getMessage()); log_message('error', '승인 처리 스택 트레이스: ' . $e->getTraceAsString()); return $this->response->setStatusCode(500)->setJSON([ 'success' => false, 'message' => '요청 처리 중 오류가 발생했습니다.', 'error' => $e->getMessage() ]); } } /** * 처리자 검증 (벤더사 또는 사용자) */ private function validateProcessor($processedBy) { // 1. 먼저 USER_LIST에서 확인 (인플루언서) $user = $this->userModel ->where('SEQ', $processedBy) ->where('IS_ACT', 'Y') ->first(); if ($user) { return [ 'success' => true, 'data' => [ 'type' => 'user', 'seq' => $user['SEQ'], 'name' => $user['NICK_NAME'] ?: $user['NAME'] ] ]; } // 2. VENDOR_LIST에서 확인 (벤더사) $vendor = $this->vendorModel ->where('SEQ', $processedBy) ->where('IS_ACT', 'Y') ->first(); if ($vendor) { return [ 'success' => true, 'data' => [ 'type' => 'vendor', 'seq' => $vendor['SEQ'], 'name' => $vendor['COMPANY_NAME'] . ' (벤더사)' ] ]; } return [ 'success' => false, 'message' => "처리자 SEQ {$processedBy}는 USER_LIST나 VENDOR_LIST에서 찾을 수 없습니다." ]; } /** * 파트너십 해지 (히스토리 테이블 기반) */ public function terminatePartnership() { try { $request = $this->request->getJSON(); $mappingSeq = $request->mappingSeq ?? null; $terminatedBy = $request->terminatedBy ?? null; $terminationReason = $request->terminationReason ?? ''; if (!$mappingSeq || !$terminatedBy) { return $this->response->setStatusCode(400)->setJSON([ 'success' => false, 'message' => '필수 파라미터가 누락되었습니다.' ]); } // 매핑 정보와 현재 상태 확인 $mapping = $this->vendorInfluencerModel->getWithCurrentStatus($mappingSeq); if (!$mapping) { return $this->response->setStatusCode(404)->setJSON([ 'success' => false, 'message' => '파트너십을 찾을 수 없습니다.' ]); } // 현재 상태가 APPROVED인지 확인 if ($mapping['CURRENT_STATUS'] !== 'APPROVED') { return $this->response->setStatusCode(400)->setJSON([ 'success' => false, 'message' => '승인된 파트너십만 해지할 수 있습니다. 현재 상태: ' . $mapping['CURRENT_STATUS'] ]); } // 처리자 확인 $processingUser = $this->validateProcessor($terminatedBy); if (!$processingUser['success']) { return $this->response->setStatusCode(400)->setJSON($processingUser); } // 상태를 TERMINATED로 변경 $statusMessage = '파트너십 해지: ' . $terminationReason; $this->statusHistoryModel->changeStatus($mappingSeq, 'TERMINATED', $statusMessage, $terminatedBy); // 해지 날짜 업데이트 $this->vendorInfluencerModel->update($mappingSeq, [ 'PARTNERSHIP_END_DATE' => date('Y-m-d H:i:s') ]); return $this->response->setJSON([ 'success' => true, 'message' => '파트너십이 해지되었습니다.', 'data' => [ 'mappingSeq' => $mappingSeq, 'status' => 'TERMINATED', 'terminatedBy' => $processingUser['data']['name'] ] ]); } catch (\Exception $e) { log_message('error', '파트너십 해지 오류: ' . $e->getMessage()); return $this->response->setStatusCode(500)->setJSON([ 'success' => false, 'message' => '파트너십 해지 중 오류가 발생했습니다.', 'error' => $e->getMessage() ]); } } /** * 벤더사 상태 통계 조회 */ public function getStatusStats() { try { $request = $this->request->getJSON(); $vendorSeq = $request->vendorSeq ?? null; if (!$vendorSeq) { return $this->response->setStatusCode(400)->setJSON([ 'success' => false, 'message' => '벤더사 SEQ는 필수입니다.' ]); } $stats = $this->statusHistoryModel->getStatusStatsByVendor($vendorSeq); return $this->response->setJSON([ 'success' => true, 'data' => $stats ]); } catch (\Exception $e) { log_message('error', '상태 통계 조회 오류: ' . $e->getMessage()); return $this->response->setStatusCode(500)->setJSON([ 'success' => false, 'message' => '상태 통계 조회 중 오류가 발생했습니다.', 'error' => $e->getMessage() ]); } } }