VendorController.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  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\VendorPartnershipModel;
  7. use App\Models\VendorModel;
  8. use App\Models\InfluencerModel;
  9. class VendorController extends ResourceController
  10. {
  11. protected $modelName = 'App\Models\VendorInfluencerMappingModel';
  12. protected $format = 'json';
  13. protected $vendorInfluencerModel;
  14. protected $vendorPartnershipModel;
  15. protected $statusHistoryModel;
  16. protected $vendorModel;
  17. protected $influencerModel;
  18. public function __construct()
  19. {
  20. $this->vendorInfluencerModel = new VendorInfluencerMappingModel();
  21. $this->vendorPartnershipModel = new VendorPartnershipModel();
  22. $this->statusHistoryModel = new VendorInfluencerStatusHistoryModel();
  23. $this->vendorModel = new VendorModel();
  24. $this->influencerModel = new InfluencerModel();
  25. }
  26. /**
  27. * 벤더사의 인플루언서 요청 목록 조회 (히스토리 테이블 기반)
  28. */
  29. public function getInfluencerRequests()
  30. {
  31. try {
  32. $request = $this->request->getJSON();
  33. $vendorSeq = $request->vendorSeq ?? null;
  34. $status = $request->status ?? null;
  35. $page = $request->page ?? 1;
  36. $size = $request->size ?? 20;
  37. log_message('debug', 'getInfluencerRequests 호출: ' . json_encode([
  38. 'vendorSeq' => $vendorSeq,
  39. 'status' => $status,
  40. 'page' => $page,
  41. 'size' => $size
  42. ]));
  43. if (!$vendorSeq) {
  44. return $this->response->setStatusCode(400)->setJSON([
  45. 'success' => false,
  46. 'message' => '벤더사 SEQ는 필수입니다.'
  47. ]);
  48. }
  49. $result = $this->vendorPartnershipModel->getVendorRequestsWithPagination($vendorSeq, $page, $size, $status);
  50. // 통계 계산 (히스토리 테이블이 없을 경우를 대비한 안전장치)
  51. $statsFormatted = [
  52. 'pending' => 0,
  53. 'approved' => 0,
  54. 'rejected' => 0,
  55. 'total' => 0
  56. ];
  57. try {
  58. $stats = $this->statusHistoryModel->getStatusStatsByVendor($vendorSeq);
  59. foreach ($stats as $stat) {
  60. $statsFormatted['total'] += $stat['count'];
  61. switch ($stat['STATUS']) {
  62. case 'PENDING':
  63. $statsFormatted['pending'] = $stat['count'];
  64. break;
  65. case 'APPROVED':
  66. $statsFormatted['approved'] = $stat['count'];
  67. break;
  68. case 'REJECTED':
  69. $statsFormatted['rejected'] = $stat['count'];
  70. break;
  71. }
  72. }
  73. } catch (\Exception $statsError) {
  74. log_message('warning', '통계 조회 실패 (히스토리 테이블 없음?): ' . $statsError->getMessage());
  75. // 히스토리 테이블이 없으면 메인 테이블에서 대략적인 통계 계산
  76. try {
  77. $mainStats = $this->vendorInfluencerModel
  78. ->where('VENDOR_SEQ', $vendorSeq)
  79. ->where('IS_ACT', 'Y')
  80. ->countAllResults();
  81. $statsFormatted['total'] = $mainStats;
  82. $statsFormatted['pending'] = $mainStats; // 히스토리가 없으면 모두 PENDING으로 가정
  83. } catch (\Exception $mainStatsError) {
  84. log_message('error', '메인 테이블 통계도 실패: ' . $mainStatsError->getMessage());
  85. }
  86. }
  87. log_message('debug', 'API 응답 데이터: ' . json_encode([
  88. 'items_count' => count($result['data']),
  89. 'pagination' => $result['pagination'],
  90. 'stats' => $statsFormatted
  91. ]));
  92. // 프론트엔드에서 기대하는 응답 구조에 맞춤
  93. return $this->response->setJSON([
  94. 'success' => true,
  95. 'data' => [
  96. 'items' => $result['data'], // 프론트엔드에서 data.items로 접근
  97. 'total' => $result['pagination']['total'],
  98. 'page' => $result['pagination']['currentPage'],
  99. 'totalPages' => $result['pagination']['totalPages'],
  100. 'size' => $result['pagination']['limit'],
  101. 'stats' => $statsFormatted
  102. ]
  103. ]);
  104. } catch (\Exception $e) {
  105. log_message('error', '인플루언서 요청 목록 조회 오류: ' . $e->getMessage());
  106. log_message('error', '스택 트레이스: ' . $e->getTraceAsString());
  107. return $this->response->setStatusCode(500)->setJSON([
  108. 'success' => false,
  109. 'message' => '요청 목록 조회 중 오류가 발생했습니다.',
  110. 'error' => $e->getMessage()
  111. ]);
  112. }
  113. }
  114. /**
  115. * 인플루언서 요청 승인/거절 처리 (히스토리 테이블 기반)
  116. */
  117. public function processInfluencerRequest()
  118. {
  119. try {
  120. $request = $this->request->getJSON();
  121. $mappingSeq = $request->mappingSeq ?? null;
  122. $action = $request->action ?? null; // 'approve' or 'reject'
  123. $processedBy = $request->processedBy ?? null;
  124. $responseMessage = $request->responseMessage ?? '';
  125. log_message('debug', '승인 처리 요청: ' . json_encode([
  126. 'mappingSeq' => $mappingSeq,
  127. 'action' => $action,
  128. 'processedBy' => $processedBy,
  129. 'responseMessage' => $responseMessage
  130. ]));
  131. if (!$mappingSeq || !$action || !$processedBy) {
  132. return $this->response->setStatusCode(400)->setJSON([
  133. 'success' => false,
  134. 'message' => '필수 파라미터가 누락되었습니다. (mappingSeq, action, processedBy 필요)'
  135. ]);
  136. }
  137. // action 검증
  138. if (!in_array($action, ['approve', 'reject'])) {
  139. return $this->response->setStatusCode(400)->setJSON([
  140. 'success' => false,
  141. 'message' => 'action은 approve 또는 reject만 가능합니다.'
  142. ]);
  143. }
  144. // 매핑 정보와 현재 상태 확인
  145. $mapping = $this->vendorInfluencerModel->getWithCurrentStatus($mappingSeq);
  146. if (!$mapping) {
  147. return $this->response->setStatusCode(404)->setJSON([
  148. 'success' => false,
  149. 'message' => '요청을 찾을 수 없습니다.'
  150. ]);
  151. }
  152. // 현재 상태가 PENDING인지 확인
  153. if ($mapping['CURRENT_STATUS'] !== 'PENDING') {
  154. return $this->response->setStatusCode(400)->setJSON([
  155. 'success' => false,
  156. 'message' => '이미 처리된 요청입니다. 현재 상태: ' . $mapping['CURRENT_STATUS']
  157. ]);
  158. }
  159. // 처리자 확인
  160. $processingUser = $this->validateProcessor($processedBy);
  161. if (!$processingUser['success']) {
  162. return $this->response->setStatusCode(400)->setJSON($processingUser);
  163. }
  164. // 상태 변경
  165. $newStatus = ($action === 'approve') ? 'APPROVED' : 'REJECTED';
  166. $statusMessage = $responseMessage ?: ($action === 'approve' ? '승인 처리됨' : '거부 처리됨');
  167. log_message('debug', "상태 변경: {$mapping['CURRENT_STATUS']} → {$newStatus}");
  168. // 히스토리 테이블에 상태 변경 기록
  169. $this->statusHistoryModel->changeStatus($mappingSeq, $newStatus, $statusMessage, $processedBy);
  170. // 메인 테이블 업데이트 (응답 관련 정보)
  171. $this->vendorInfluencerModel->update($mappingSeq, [
  172. 'RESPONSE_MESSAGE' => $responseMessage,
  173. 'RESPONSE_DATE' => date('Y-m-d H:i:s'),
  174. 'APPROVED_BY' => $processedBy
  175. ]);
  176. // 승인인 경우 파트너십 시작일 설정
  177. if ($action === 'approve') {
  178. $this->vendorInfluencerModel->update($mappingSeq, [
  179. 'PARTNERSHIP_START_DATE' => date('Y-m-d H:i:s')
  180. ]);
  181. }
  182. log_message('debug', "승인 처리 완료: action={$action}, newStatus={$newStatus}");
  183. return $this->response->setJSON([
  184. 'success' => true,
  185. 'message' => $action === 'approve' ? '요청이 승인되었습니다.' : '요청이 거부되었습니다.',
  186. 'data' => [
  187. 'mappingSeq' => $mappingSeq,
  188. 'action' => $action,
  189. 'status' => $newStatus,
  190. 'processedBy' => $processingUser['data']['name'],
  191. 'responseMessage' => $responseMessage
  192. ]
  193. ]);
  194. } catch (\Exception $e) {
  195. log_message('error', '승인 처리 중 예외 발생: ' . $e->getMessage());
  196. log_message('error', '승인 처리 스택 트레이스: ' . $e->getTraceAsString());
  197. return $this->response->setStatusCode(500)->setJSON([
  198. 'success' => false,
  199. 'message' => '요청 처리 중 오류가 발생했습니다.',
  200. 'error' => $e->getMessage()
  201. ]);
  202. }
  203. }
  204. /**
  205. * 처리자 검증 (벤더사 또는 사용자)
  206. */
  207. private function validateProcessor($processedBy)
  208. {
  209. // 1. 먼저 USER_LIST에서 확인 (인플루언서)
  210. $user = $this->influencerModel
  211. ->where('SEQ', $processedBy)
  212. ->where('IS_ACT', 'Y')
  213. ->first();
  214. if ($user) {
  215. return [
  216. 'success' => true,
  217. 'data' => [
  218. 'type' => 'user',
  219. 'seq' => $user['SEQ'],
  220. 'name' => $user['NICK_NAME'] ?: $user['NAME']
  221. ]
  222. ];
  223. }
  224. // 2. VENDOR_LIST에서 확인 (벤더사)
  225. $vendor = $this->vendorModel
  226. ->where('SEQ', $processedBy)
  227. ->where('IS_ACT', 'Y')
  228. ->first();
  229. if ($vendor) {
  230. return [
  231. 'success' => true,
  232. 'data' => [
  233. 'type' => 'vendor',
  234. 'seq' => $vendor['SEQ'],
  235. 'name' => $vendor['COMPANY_NAME'] . ' (벤더사)'
  236. ]
  237. ];
  238. }
  239. return [
  240. 'success' => false,
  241. 'message' => "처리자 SEQ {$processedBy}는 USER_LIST나 VENDOR_LIST에서 찾을 수 없습니다."
  242. ];
  243. }
  244. /**
  245. * 벤더사 파트너십 해지 - 단순화된 방식
  246. */
  247. public function terminatePartnership()
  248. {
  249. try {
  250. $request = $this->request->getJSON();
  251. $mappingSeq = $request->mappingSeq ?? null;
  252. $terminatedBy = $request->terminatedBy ?? null;
  253. $terminateReason = $request->terminateReason ?? '';
  254. log_message('info', '파트너십 해지 요청: ' . json_encode([
  255. 'mappingSeq' => $mappingSeq,
  256. 'terminatedBy' => $terminatedBy,
  257. 'terminateReason' => $terminateReason
  258. ]));
  259. if (!$mappingSeq || !$terminatedBy) {
  260. return $this->response->setStatusCode(400)->setJSON([
  261. 'success' => false,
  262. 'message' => '필수 파라미터가 누락되었습니다.'
  263. ]);
  264. }
  265. // 매핑 정보 확인 (메인 테이블만 사용)
  266. $mapping = $this->vendorInfluencerModel->where('SEQ', $mappingSeq)
  267. ->where('IS_ACT', 'Y')
  268. ->first();
  269. if (!$mapping) {
  270. return $this->response->setStatusCode(404)->setJSON([
  271. 'success' => false,
  272. 'message' => '파트너십을 찾을 수 없습니다.'
  273. ]);
  274. }
  275. // 현재 상태 확인 (히스토리 테이블 기준)
  276. $currentStatus = $this->statusHistoryModel->getCurrentStatus($mappingSeq);
  277. $actualStatus = $currentStatus ? $currentStatus['STATUS'] : $mapping['STATUS'];
  278. log_message('info', '현재 매핑 정보: ' . json_encode($mapping));
  279. log_message('info', '히스토리 테이블 현재 상태: ' . json_encode($currentStatus));
  280. log_message('info', '실제 확인할 상태: ' . $actualStatus);
  281. // 현재 상태가 APPROVED인지 확인
  282. if ($actualStatus !== 'APPROVED') {
  283. return $this->response->setStatusCode(400)->setJSON([
  284. 'success' => false,
  285. 'message' => '승인된 파트너십만 해지할 수 있습니다. 현재 상태: ' . $actualStatus
  286. ]);
  287. }
  288. // 처리자 확인
  289. $processingUser = $this->validateProcessor($terminatedBy);
  290. if (!$processingUser['success']) {
  291. return $this->response->setStatusCode(400)->setJSON($processingUser);
  292. }
  293. log_message('info', '처리자 검증 완료: ' . json_encode($processingUser['data']));
  294. // 메인 테이블 직접 업데이트 (단순하고 확실한 방법)
  295. $statusMessage = '파트너십 해지: ' . $terminateReason;
  296. $actualChangedBy = $processingUser['data']['seq'] ?? $terminatedBy ?: 1;
  297. $updateData = [
  298. 'STATUS' => 'TERMINATED',
  299. 'RESPONSE_MESSAGE' => $statusMessage,
  300. 'RESPONSE_DATE' => date('Y-m-d H:i:s'),
  301. 'PARTNERSHIP_END_DATE' => date('Y-m-d H:i:s'),
  302. 'APPROVED_BY' => $actualChangedBy,
  303. 'MOD_DATE' => date('Y-m-d H:i:s')
  304. ];
  305. log_message('info', "메인 테이블 업데이트 데이터: " . json_encode($updateData));
  306. // 업데이트 전 데이터 저장
  307. $beforeUpdate = $this->vendorInfluencerModel->find($mappingSeq);
  308. log_message('info', "업데이트 전 데이터: " . json_encode($beforeUpdate));
  309. // UNIQUE 제약조건 우회를 위해 직접 SQL 사용
  310. $db = \Config\Database::connect();
  311. try {
  312. // 1. 먼저 기존 TERMINATED 레코드가 있는지 확인
  313. $existingTerminated = $db->query(
  314. "SELECT SEQ FROM VENDOR_INFLUENCER_MAPPING
  315. WHERE VENDOR_SEQ = ? AND INFLUENCER_SEQ = ? AND STATUS = 'TERMINATED' AND SEQ != ?",
  316. [$beforeUpdate['VENDOR_SEQ'], $beforeUpdate['INFLUENCER_SEQ'], $mappingSeq]
  317. )->getRowArray();
  318. if ($existingTerminated) {
  319. log_message('warning', '기존 TERMINATED 레코드 존재 - 비활성화: ' . json_encode($existingTerminated));
  320. // 기존 TERMINATED 레코드를 비활성화
  321. $db->query(
  322. "UPDATE VENDOR_INFLUENCER_MAPPING SET IS_ACT = 'N' WHERE SEQ = ?",
  323. [$existingTerminated['SEQ']]
  324. );
  325. }
  326. // 2. 직접 SQL로 현재 레코드 업데이트
  327. $updateSql = "UPDATE VENDOR_INFLUENCER_MAPPING SET
  328. STATUS = 'TERMINATED',
  329. RESPONSE_MESSAGE = ?,
  330. RESPONSE_DATE = ?,
  331. PARTNERSHIP_END_DATE = ?,
  332. APPROVED_BY = ?,
  333. MOD_DATE = ?
  334. WHERE SEQ = ?";
  335. $updateParams = [
  336. $statusMessage,
  337. date('Y-m-d H:i:s'),
  338. date('Y-m-d H:i:s'),
  339. $actualChangedBy,
  340. date('Y-m-d H:i:s'),
  341. $mappingSeq
  342. ];
  343. log_message('info', "직접 SQL 실행: " . $updateSql);
  344. log_message('info', "SQL 파라미터: " . json_encode($updateParams));
  345. $updateResult = $db->query($updateSql, $updateParams);
  346. $affectedRows = $db->affectedRows();
  347. log_message('info', "직접 SQL 업데이트 결과: 영향받은 행 수={$affectedRows}");
  348. if ($affectedRows === 0) {
  349. throw new \Exception('직접 SQL 업데이트 실패 - 영향받은 행이 0개');
  350. }
  351. } catch (\Exception $sqlError) {
  352. log_message('error', '직접 SQL 업데이트 실패: ' . $sqlError->getMessage());
  353. return $this->response->setStatusCode(500)->setJSON([
  354. 'success' => false,
  355. 'message' => '파트너십 해지 처리 중 SQL 오류가 발생했습니다.',
  356. 'error' => '직접 SQL 업데이트 실패',
  357. 'debug' => $sqlError->getMessage()
  358. ]);
  359. }
  360. // 업데이트 후 데이터 확인
  361. $afterUpdate = $this->vendorInfluencerModel->find($mappingSeq);
  362. log_message('info', "업데이트 후 데이터: " . json_encode($afterUpdate));
  363. // 실제 상태 변경 확인
  364. if ($afterUpdate['STATUS'] !== 'TERMINATED') {
  365. log_message('error', '상태 변경 검증 실패: ' . $afterUpdate['STATUS']);
  366. return $this->response->setStatusCode(500)->setJSON([
  367. 'success' => false,
  368. 'message' => '파트너십 해지 처리 중 오류가 발생했습니다.',
  369. 'error' => '상태 변경 검증 실패',
  370. 'debug' => [
  371. 'expected' => 'TERMINATED',
  372. 'actual' => $afterUpdate['STATUS']
  373. ]
  374. ]);
  375. }
  376. log_message('info', '파트너십 해지 완료: mappingSeq=' . $mappingSeq);
  377. return $this->response->setJSON([
  378. 'success' => true,
  379. 'message' => '파트너십이 해지되었습니다.',
  380. 'data' => [
  381. 'mappingSeq' => $mappingSeq,
  382. 'status' => 'TERMINATED',
  383. 'terminatedBy' => $processingUser['data']['name'],
  384. 'terminateReason' => $terminateReason,
  385. 'terminateDate' => date('Y-m-d H:i:s'),
  386. 'verifiedStatus' => $afterUpdate['STATUS'] // 검증된 상태
  387. ]
  388. ]);
  389. } catch (\Exception $e) {
  390. log_message('error', '파트너십 해지 오류: ' . $e->getMessage());
  391. return $this->response->setStatusCode(500)->setJSON([
  392. 'success' => false,
  393. 'message' => '파트너십 해지 중 오류가 발생했습니다.',
  394. 'error' => '시스템 오류',
  395. 'debug' => ENVIRONMENT === 'development' ? $e->getMessage() : null
  396. ]);
  397. }
  398. }
  399. /**
  400. * 벤더사 상태 통계 조회
  401. */
  402. public function getStatusStats()
  403. {
  404. try {
  405. $request = $this->request->getJSON();
  406. $vendorSeq = $request->vendorSeq ?? null;
  407. if (!$vendorSeq) {
  408. return $this->response->setStatusCode(400)->setJSON([
  409. 'success' => false,
  410. 'message' => '벤더사 SEQ는 필수입니다.'
  411. ]);
  412. }
  413. $stats = $this->statusHistoryModel->getStatusStatsByVendor($vendorSeq);
  414. return $this->response->setJSON([
  415. 'success' => true,
  416. 'data' => $stats
  417. ]);
  418. } catch (\Exception $e) {
  419. log_message('error', '상태 통계 조회 오류: ' . $e->getMessage());
  420. return $this->response->setStatusCode(500)->setJSON([
  421. 'success' => false,
  422. 'message' => '상태 통계 조회 중 오류가 발생했습니다.',
  423. 'error' => $e->getMessage()
  424. ]);
  425. }
  426. }
  427. /**
  428. * 인플루언서 요청 승인/거절 (프론트엔드 호환용)
  429. * 프론트엔드에서 /api/vendor-influencer/approve 호출에 대응
  430. */
  431. public function approveInfluencerRequest()
  432. {
  433. try {
  434. $request = $this->request->getJSON();
  435. $mappingSeq = $request->mappingSeq ?? null;
  436. $action = $request->action ?? null; // 'APPROVE' or 'REJECT'
  437. $processedBy = $request->processedBy ?? null;
  438. $responseMessage = $request->responseMessage ?? '';
  439. log_message('debug', '프론트엔드 승인 처리 요청: ' . json_encode([
  440. 'mappingSeq' => $mappingSeq,
  441. 'action' => $action,
  442. 'processedBy' => $processedBy,
  443. 'responseMessage' => $responseMessage
  444. ]));
  445. if (!$mappingSeq || !$action || !$processedBy) {
  446. return $this->response->setStatusCode(400)->setJSON([
  447. 'success' => false,
  448. 'message' => '필수 파라미터가 누락되었습니다. (mappingSeq, action, processedBy 필요)'
  449. ]);
  450. }
  451. // action 값 정규화 (프론트엔드에서는 대문자로 전송)
  452. $normalizedAction = strtolower($action);
  453. if (!in_array($normalizedAction, ['approve', 'reject'])) {
  454. return $this->response->setStatusCode(400)->setJSON([
  455. 'success' => false,
  456. 'message' => 'action은 APPROVE 또는 REJECT만 가능합니다.'
  457. ]);
  458. }
  459. // 매핑 정보와 현재 상태 확인
  460. $mapping = $this->vendorInfluencerModel->getWithCurrentStatus($mappingSeq);
  461. if (!$mapping) {
  462. return $this->response->setStatusCode(404)->setJSON([
  463. 'success' => false,
  464. 'message' => '요청을 찾을 수 없습니다.'
  465. ]);
  466. }
  467. // 현재 상태가 PENDING인지 확인
  468. if ($mapping['CURRENT_STATUS'] !== 'PENDING') {
  469. return $this->response->setStatusCode(400)->setJSON([
  470. 'success' => false,
  471. 'message' => '이미 처리된 요청입니다. 현재 상태: ' . $mapping['CURRENT_STATUS']
  472. ]);
  473. }
  474. // 처리자 확인
  475. $processingUser = $this->validateProcessor($processedBy);
  476. if (!$processingUser['success']) {
  477. return $this->response->setStatusCode(400)->setJSON($processingUser);
  478. }
  479. // 상태 변경
  480. $newStatus = ($normalizedAction === 'approve') ? 'APPROVED' : 'REJECTED';
  481. $statusMessage = $responseMessage ?: ($normalizedAction === 'approve' ? '승인 처리됨' : '거부 처리됨');
  482. log_message('debug', "프론트엔드 상태 변경: {$mapping['CURRENT_STATUS']} → {$newStatus}");
  483. // 히스토리 테이블에 상태 변경 기록
  484. $this->statusHistoryModel->changeStatus($mappingSeq, $newStatus, $statusMessage, $processedBy);
  485. // 메인 테이블 업데이트 (응답 관련 정보)
  486. $this->vendorInfluencerModel->update($mappingSeq, [
  487. 'RESPONSE_MESSAGE' => $responseMessage,
  488. 'RESPONSE_DATE' => date('Y-m-d H:i:s'),
  489. 'APPROVED_BY' => $processedBy
  490. ]);
  491. // 승인인 경우 파트너십 시작일 설정
  492. if ($normalizedAction === 'approve') {
  493. $this->vendorInfluencerModel->update($mappingSeq, [
  494. 'PARTNERSHIP_START_DATE' => date('Y-m-d H:i:s')
  495. ]);
  496. }
  497. log_message('debug', "프론트엔드 승인 처리 완료: action={$normalizedAction}, newStatus={$newStatus}");
  498. return $this->response->setJSON([
  499. 'success' => true,
  500. 'message' => $normalizedAction === 'approve' ? '요청이 승인되었습니다.' : '요청이 거부되었습니다.',
  501. 'data' => [
  502. 'mappingSeq' => $mappingSeq,
  503. 'action' => $action,
  504. 'status' => $newStatus,
  505. 'processedBy' => $processingUser['data']['name'],
  506. 'responseMessage' => $responseMessage
  507. ]
  508. ]);
  509. } catch (\Exception $e) {
  510. log_message('error', '프론트엔드 승인 처리 중 예외 발생: ' . $e->getMessage());
  511. log_message('error', '프론트엔드 승인 처리 스택 트레이스: ' . $e->getTraceAsString());
  512. return $this->response->setStatusCode(500)->setJSON([
  513. 'success' => false,
  514. 'message' => '요청 처리 중 오류가 발생했습니다.',
  515. 'error' => $e->getMessage()
  516. ]);
  517. }
  518. }
  519. /**
  520. * 데이터베이스 상태 디버깅 (임시)
  521. */
  522. public function debugMappingStatus($mappingSeq = null)
  523. {
  524. try {
  525. if (!$mappingSeq) {
  526. $mappingSeq = $this->request->getGet('seq') ?? 1;
  527. }
  528. // 메인 테이블 상태
  529. $mainData = $this->vendorInfluencerModel->where('SEQ', $mappingSeq)->first();
  530. // 히스토리 테이블 전체
  531. $historyData = $this->statusHistoryModel->where('MAPPING_SEQ', $mappingSeq)
  532. ->orderBy('CHANGED_DATE', 'DESC')
  533. ->findAll();
  534. // 현재 상태 (IS_CURRENT='Y')
  535. $currentStatus = $this->statusHistoryModel->getCurrentStatus($mappingSeq);
  536. return $this->response->setJSON([
  537. 'success' => true,
  538. 'mappingSeq' => $mappingSeq,
  539. 'mainTable' => $mainData,
  540. 'historyTable' => $historyData,
  541. 'currentStatus' => $currentStatus,
  542. 'timestamp' => date('Y-m-d H:i:s')
  543. ]);
  544. } catch (\Exception $e) {
  545. return $this->response->setJSON([
  546. 'success' => false,
  547. 'error' => $e->getMessage()
  548. ]);
  549. }
  550. }
  551. /**
  552. * 디버깅용: 히스토리 테이블 insert 테스트
  553. */
  554. public function debugHistoryInsert()
  555. {
  556. try {
  557. $request = $this->request->getJSON();
  558. $mappingSeq = $request->mappingSeq ?? 1;
  559. // 최소한의 데이터로 테스트 insert
  560. $testData = [
  561. 'MAPPING_SEQ' => (int)$mappingSeq,
  562. 'STATUS' => 'PENDING',
  563. 'PREVIOUS_STATUS' => null,
  564. 'STATUS_MESSAGE' => 'Test insert',
  565. 'CHANGED_BY' => 1,
  566. 'IS_CURRENT' => 'N', // 테스트용이므로 N으로 설정
  567. 'CHANGED_DATE' => date('Y-m-d H:i:s')
  568. ];
  569. log_message('debug', '테스트 insert 데이터: ' . json_encode($testData));
  570. // validation 체크
  571. if (!$this->statusHistoryModel->validate($testData)) {
  572. $validationErrors = $this->statusHistoryModel->errors();
  573. return $this->response->setJSON([
  574. 'success' => false,
  575. 'message' => 'Validation 실패',
  576. 'errors' => $validationErrors,
  577. 'data' => $testData
  578. ]);
  579. }
  580. $result = $this->statusHistoryModel->insert($testData, false);
  581. if (!$result) {
  582. $dbError = $this->statusHistoryModel->db->error();
  583. return $this->response->setJSON([
  584. 'success' => false,
  585. 'message' => 'DB Insert 실패',
  586. 'dbError' => $dbError,
  587. 'data' => $testData
  588. ]);
  589. }
  590. return $this->response->setJSON([
  591. 'success' => true,
  592. 'message' => '테스트 insert 성공',
  593. 'insertId' => $result,
  594. 'data' => $testData
  595. ]);
  596. } catch (\Exception $e) {
  597. return $this->response->setJSON([
  598. 'success' => false,
  599. 'message' => '테스트 insert 중 오류',
  600. 'error' => $e->getMessage(),
  601. 'trace' => $e->getTraceAsString()
  602. ]);
  603. }
  604. }
  605. /**
  606. * 메인 테이블과 히스토리 테이블 상태 동기화
  607. */
  608. public function syncMappingStatus()
  609. {
  610. try {
  611. $request = $this->request->getJSON();
  612. $mappingSeq = $request->mappingSeq ?? $this->request->getGet('seq');
  613. if (!$mappingSeq) {
  614. return $this->response->setJSON([
  615. 'success' => false,
  616. 'message' => 'mappingSeq가 필요합니다.'
  617. ]);
  618. }
  619. // 현재 히스토리 테이블 상태 조회
  620. $currentStatus = $this->statusHistoryModel->getCurrentStatus($mappingSeq);
  621. if (!$currentStatus) {
  622. return $this->response->setJSON([
  623. 'success' => false,
  624. 'message' => '히스토리 테이블에서 현재 상태를 찾을 수 없습니다.'
  625. ]);
  626. }
  627. // 메인 테이블 업데이트
  628. $updateData = [
  629. 'STATUS' => $currentStatus['STATUS'],
  630. 'MOD_DATE' => date('Y-m-d H:i:s')
  631. ];
  632. // TERMINATED 상태인 경우 추가 필드 업데이트
  633. if ($currentStatus['STATUS'] === 'TERMINATED') {
  634. $updateData['RESPONSE_MESSAGE'] = $currentStatus['STATUS_MESSAGE'] ?? '파트너십 해지';
  635. $updateData['RESPONSE_DATE'] = $currentStatus['CHANGED_DATE'];
  636. $updateData['PARTNERSHIP_END_DATE'] = $currentStatus['CHANGED_DATE'];
  637. $updateData['APPROVED_BY'] = $currentStatus['CHANGED_BY'];
  638. }
  639. $result = $this->vendorInfluencerModel->update($mappingSeq, $updateData);
  640. if ($result) {
  641. // 동기화 후 상태 확인
  642. $updatedMain = $this->vendorInfluencerModel->find($mappingSeq);
  643. return $this->response->setJSON([
  644. 'success' => true,
  645. 'message' => '상태 동기화 완료',
  646. 'data' => [
  647. 'mappingSeq' => $mappingSeq,
  648. 'syncedStatus' => $currentStatus['STATUS'],
  649. 'updatedMainTable' => $updatedMain
  650. ]
  651. ]);
  652. } else {
  653. return $this->response->setJSON([
  654. 'success' => false,
  655. 'message' => '메인 테이블 업데이트 실패'
  656. ]);
  657. }
  658. } catch (\Exception $e) {
  659. return $this->response->setJSON([
  660. 'success' => false,
  661. 'message' => '동기화 중 오류 발생',
  662. 'error' => $e->getMessage()
  663. ]);
  664. }
  665. }
  666. }