VendorControllerV2.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. <?php
  2. namespace App\Controllers;
  3. use CodeIgniter\RESTful\ResourceController;
  4. use App\Models\VendorInfluencerMappingModel;
  5. use App\Models\VendorInfluencerStatusHistoryModel;
  6. use App\Models\VendorModel;
  7. use App\Models\UserModel;
  8. class VendorControllerV2 extends ResourceController
  9. {
  10. protected $modelName = 'App\Models\VendorInfluencerMappingModel';
  11. protected $format = 'json';
  12. protected $vendorInfluencerModel;
  13. protected $statusHistoryModel;
  14. protected $vendorModel;
  15. protected $userModel;
  16. public function __construct()
  17. {
  18. $this->vendorInfluencerModel = new VendorInfluencerMappingModel();
  19. $this->statusHistoryModel = new VendorInfluencerStatusHistoryModel();
  20. $this->vendorModel = new VendorModel();
  21. $this->userModel = new UserModel();
  22. }
  23. /**
  24. * 벤더사의 인플루언서 요청 목록 조회 (히스토리 테이블 기반)
  25. */
  26. public function getInfluencerRequests()
  27. {
  28. try {
  29. $request = $this->request->getJSON();
  30. $vendorSeq = $request->vendorSeq ?? null;
  31. $status = $request->status ?? null;
  32. $page = $request->page ?? 1;
  33. $size = $request->size ?? 20;
  34. log_message('debug', 'getInfluencerRequests 호출: ' . json_encode([
  35. 'vendorSeq' => $vendorSeq,
  36. 'status' => $status,
  37. 'page' => $page,
  38. 'size' => $size
  39. ]));
  40. if (!$vendorSeq) {
  41. return $this->response->setStatusCode(400)->setJSON([
  42. 'success' => false,
  43. 'message' => '벤더사 SEQ는 필수입니다.'
  44. ]);
  45. }
  46. $result = $this->vendorInfluencerModel->getInfluencerRequestsByVendor($vendorSeq, $page, $size, $status);
  47. // 통계 계산
  48. $stats = $this->statusHistoryModel->getStatusStatsByVendor($vendorSeq);
  49. $statsFormatted = [
  50. 'pending' => 0,
  51. 'approved' => 0,
  52. 'rejected' => 0,
  53. 'total' => 0
  54. ];
  55. foreach ($stats as $stat) {
  56. $statsFormatted['total'] += $stat['count'];
  57. switch ($stat['STATUS']) {
  58. case 'PENDING':
  59. $statsFormatted['pending'] = $stat['count'];
  60. break;
  61. case 'APPROVED':
  62. $statsFormatted['approved'] = $stat['count'];
  63. break;
  64. case 'REJECTED':
  65. $statsFormatted['rejected'] = $stat['count'];
  66. break;
  67. }
  68. }
  69. log_message('debug', 'API 응답 데이터: ' . json_encode([
  70. 'items_count' => count($result['data']),
  71. 'pagination' => $result['pagination'],
  72. 'stats' => $statsFormatted
  73. ]));
  74. // 프론트엔드에서 기대하는 응답 구조에 맞춤
  75. return $this->response->setJSON([
  76. 'success' => true,
  77. 'data' => [
  78. 'items' => $result['data'], // 프론트엔드에서 data.items로 접근
  79. 'total' => $result['pagination']['total'],
  80. 'page' => $result['pagination']['currentPage'],
  81. 'totalPages' => $result['pagination']['totalPages'],
  82. 'size' => $result['pagination']['limit'],
  83. 'stats' => $statsFormatted
  84. ]
  85. ]);
  86. } catch (\Exception $e) {
  87. log_message('error', '인플루언서 요청 목록 조회 오류: ' . $e->getMessage());
  88. log_message('error', '스택 트레이스: ' . $e->getTraceAsString());
  89. return $this->response->setStatusCode(500)->setJSON([
  90. 'success' => false,
  91. 'message' => '요청 목록 조회 중 오류가 발생했습니다.',
  92. 'error' => $e->getMessage()
  93. ]);
  94. }
  95. }
  96. /**
  97. * 인플루언서 요청 승인/거절 처리 (히스토리 테이블 기반)
  98. */
  99. public function processInfluencerRequest()
  100. {
  101. try {
  102. $request = $this->request->getJSON();
  103. $mappingSeq = $request->mappingSeq ?? null;
  104. $action = $request->action ?? null; // 'approve' or 'reject'
  105. $processedBy = $request->processedBy ?? null;
  106. $responseMessage = $request->responseMessage ?? '';
  107. log_message('debug', '승인 처리 요청: ' . json_encode([
  108. 'mappingSeq' => $mappingSeq,
  109. 'action' => $action,
  110. 'processedBy' => $processedBy,
  111. 'responseMessage' => $responseMessage
  112. ]));
  113. if (!$mappingSeq || !$action || !$processedBy) {
  114. return $this->response->setStatusCode(400)->setJSON([
  115. 'success' => false,
  116. 'message' => '필수 파라미터가 누락되었습니다. (mappingSeq, action, processedBy 필요)'
  117. ]);
  118. }
  119. // action 검증
  120. if (!in_array($action, ['approve', 'reject'])) {
  121. return $this->response->setStatusCode(400)->setJSON([
  122. 'success' => false,
  123. 'message' => 'action은 approve 또는 reject만 가능합니다.'
  124. ]);
  125. }
  126. // 매핑 정보와 현재 상태 확인
  127. $mapping = $this->vendorInfluencerModel->getWithCurrentStatus($mappingSeq);
  128. if (!$mapping) {
  129. return $this->response->setStatusCode(404)->setJSON([
  130. 'success' => false,
  131. 'message' => '요청을 찾을 수 없습니다.'
  132. ]);
  133. }
  134. // 현재 상태가 PENDING인지 확인
  135. if ($mapping['CURRENT_STATUS'] !== 'PENDING') {
  136. return $this->response->setStatusCode(400)->setJSON([
  137. 'success' => false,
  138. 'message' => '이미 처리된 요청입니다. 현재 상태: ' . $mapping['CURRENT_STATUS']
  139. ]);
  140. }
  141. // 처리자 확인
  142. $processingUser = $this->validateProcessor($processedBy);
  143. if (!$processingUser['success']) {
  144. return $this->response->setStatusCode(400)->setJSON($processingUser);
  145. }
  146. // 상태 변경
  147. $newStatus = ($action === 'approve') ? 'APPROVED' : 'REJECTED';
  148. $statusMessage = $responseMessage ?: ($action === 'approve' ? '승인 처리됨' : '거부 처리됨');
  149. log_message('debug', "상태 변경: {$mapping['CURRENT_STATUS']} → {$newStatus}");
  150. // 히스토리 테이블에 상태 변경 기록
  151. $this->statusHistoryModel->changeStatus($mappingSeq, $newStatus, $statusMessage, $processedBy);
  152. // 메인 테이블 업데이트 (응답 관련 정보)
  153. $this->vendorInfluencerModel->update($mappingSeq, [
  154. 'RESPONSE_MESSAGE' => $responseMessage,
  155. 'RESPONSE_DATE' => date('Y-m-d H:i:s'),
  156. 'APPROVED_BY' => $processedBy
  157. ]);
  158. // 승인인 경우 파트너십 시작일 설정
  159. if ($action === 'approve') {
  160. $this->vendorInfluencerModel->update($mappingSeq, [
  161. 'PARTNERSHIP_START_DATE' => date('Y-m-d H:i:s')
  162. ]);
  163. }
  164. log_message('debug', "승인 처리 완료: action={$action}, newStatus={$newStatus}");
  165. return $this->response->setJSON([
  166. 'success' => true,
  167. 'message' => $action === 'approve' ? '요청이 승인되었습니다.' : '요청이 거부되었습니다.',
  168. 'data' => [
  169. 'mappingSeq' => $mappingSeq,
  170. 'action' => $action,
  171. 'status' => $newStatus,
  172. 'processedBy' => $processingUser['data']['name'],
  173. 'responseMessage' => $responseMessage
  174. ]
  175. ]);
  176. } catch (\Exception $e) {
  177. log_message('error', '승인 처리 중 예외 발생: ' . $e->getMessage());
  178. log_message('error', '승인 처리 스택 트레이스: ' . $e->getTraceAsString());
  179. return $this->response->setStatusCode(500)->setJSON([
  180. 'success' => false,
  181. 'message' => '요청 처리 중 오류가 발생했습니다.',
  182. 'error' => $e->getMessage()
  183. ]);
  184. }
  185. }
  186. /**
  187. * 처리자 검증 (벤더사 또는 사용자)
  188. */
  189. private function validateProcessor($processedBy)
  190. {
  191. // 1. 먼저 USER_LIST에서 확인 (인플루언서)
  192. $user = $this->userModel
  193. ->where('SEQ', $processedBy)
  194. ->where('IS_ACT', 'Y')
  195. ->first();
  196. if ($user) {
  197. return [
  198. 'success' => true,
  199. 'data' => [
  200. 'type' => 'user',
  201. 'seq' => $user['SEQ'],
  202. 'name' => $user['NICK_NAME'] ?: $user['NAME']
  203. ]
  204. ];
  205. }
  206. // 2. VENDOR_LIST에서 확인 (벤더사)
  207. $vendor = $this->vendorModel
  208. ->where('SEQ', $processedBy)
  209. ->where('IS_ACT', 'Y')
  210. ->first();
  211. if ($vendor) {
  212. return [
  213. 'success' => true,
  214. 'data' => [
  215. 'type' => 'vendor',
  216. 'seq' => $vendor['SEQ'],
  217. 'name' => $vendor['COMPANY_NAME'] . ' (벤더사)'
  218. ]
  219. ];
  220. }
  221. return [
  222. 'success' => false,
  223. 'message' => "처리자 SEQ {$processedBy}는 USER_LIST나 VENDOR_LIST에서 찾을 수 없습니다."
  224. ];
  225. }
  226. /**
  227. * 파트너십 해지 (히스토리 테이블 기반)
  228. */
  229. public function terminatePartnership()
  230. {
  231. try {
  232. $request = $this->request->getJSON();
  233. $mappingSeq = $request->mappingSeq ?? null;
  234. $terminatedBy = $request->terminatedBy ?? null;
  235. $terminationReason = $request->terminationReason ?? '';
  236. if (!$mappingSeq || !$terminatedBy) {
  237. return $this->response->setStatusCode(400)->setJSON([
  238. 'success' => false,
  239. 'message' => '필수 파라미터가 누락되었습니다.'
  240. ]);
  241. }
  242. // 매핑 정보와 현재 상태 확인
  243. $mapping = $this->vendorInfluencerModel->getWithCurrentStatus($mappingSeq);
  244. if (!$mapping) {
  245. return $this->response->setStatusCode(404)->setJSON([
  246. 'success' => false,
  247. 'message' => '파트너십을 찾을 수 없습니다.'
  248. ]);
  249. }
  250. // 현재 상태가 APPROVED인지 확인
  251. if ($mapping['CURRENT_STATUS'] !== 'APPROVED') {
  252. return $this->response->setStatusCode(400)->setJSON([
  253. 'success' => false,
  254. 'message' => '승인된 파트너십만 해지할 수 있습니다. 현재 상태: ' . $mapping['CURRENT_STATUS']
  255. ]);
  256. }
  257. // 처리자 확인
  258. $processingUser = $this->validateProcessor($terminatedBy);
  259. if (!$processingUser['success']) {
  260. return $this->response->setStatusCode(400)->setJSON($processingUser);
  261. }
  262. // 상태를 TERMINATED로 변경
  263. $statusMessage = '파트너십 해지: ' . $terminationReason;
  264. $this->statusHistoryModel->changeStatus($mappingSeq, 'TERMINATED', $statusMessage, $terminatedBy);
  265. // 해지 날짜 업데이트
  266. $this->vendorInfluencerModel->update($mappingSeq, [
  267. 'PARTNERSHIP_END_DATE' => date('Y-m-d H:i:s')
  268. ]);
  269. return $this->response->setJSON([
  270. 'success' => true,
  271. 'message' => '파트너십이 해지되었습니다.',
  272. 'data' => [
  273. 'mappingSeq' => $mappingSeq,
  274. 'status' => 'TERMINATED',
  275. 'terminatedBy' => $processingUser['data']['name']
  276. ]
  277. ]);
  278. } catch (\Exception $e) {
  279. log_message('error', '파트너십 해지 오류: ' . $e->getMessage());
  280. return $this->response->setStatusCode(500)->setJSON([
  281. 'success' => false,
  282. 'message' => '파트너십 해지 중 오류가 발생했습니다.',
  283. 'error' => $e->getMessage()
  284. ]);
  285. }
  286. }
  287. /**
  288. * 벤더사 상태 통계 조회
  289. */
  290. public function getStatusStats()
  291. {
  292. try {
  293. $request = $this->request->getJSON();
  294. $vendorSeq = $request->vendorSeq ?? null;
  295. if (!$vendorSeq) {
  296. return $this->response->setStatusCode(400)->setJSON([
  297. 'success' => false,
  298. 'message' => '벤더사 SEQ는 필수입니다.'
  299. ]);
  300. }
  301. $stats = $this->statusHistoryModel->getStatusStatsByVendor($vendorSeq);
  302. return $this->response->setJSON([
  303. 'success' => true,
  304. 'data' => $stats
  305. ]);
  306. } catch (\Exception $e) {
  307. log_message('error', '상태 통계 조회 오류: ' . $e->getMessage());
  308. return $this->response->setStatusCode(500)->setJSON([
  309. 'success' => false,
  310. 'message' => '상태 통계 조회 중 오류가 발생했습니다.',
  311. 'error' => $e->getMessage()
  312. ]);
  313. }
  314. }
  315. }