false, 'message' => '필수 파라미터가 부족합니다. (year, comp, member_id, category_group)' ], JSON_UNESCAPED_UNICODE); exit; } try { $pdo = db_conn(); $category_groups = explode(',', $category_group); $decodedItems = []; $calls_log = []; foreach ($category_groups as $cat) { $cat = trim($cat); if (empty($cat)) continue; $member_ids = []; if ($member_id === 'ALL') { // 1. Get total active legal contents count for the category group $stmt_cnt = $pdo->prepare(" SELECT COUNT(0) AS cnt FROM edu_contents WHERE base_year = ? AND category_code = 'CA10003' AND category_group = ? AND is_active = '1'; "); $stmt_cnt->execute([$year, $cat]); $total_legal_cnt = (int)$stmt_cnt->fetchColumn(); if ($total_legal_cnt > 0) { // 2. Query completed users using the SQL logic from 개발.md $stmt_users = $pdo->prepare(" SELECT b.member_id FROM edu_learning_histories b INNER JOIN edu_contents a ON a.content_id = b.content_id INNER JOIN edu_users c ON b.member_id = c.member_id AND b.sys_comp_code = c.sys_comp_code WHERE a.base_year = ? AND a.category_code = 'CA10003' AND a.is_active = '1' AND b.completed_at IS NOT NULL AND a.category_group = ? AND (? = '' OR c.belong_comp = ?) GROUP BY b.member_id, c.belong_comp HAVING COUNT(b.content_id) = ?; "); $stmt_users->execute([$year, $cat, $comp, $comp, $total_legal_cnt]); $member_ids = $stmt_users->fetchAll(PDO::FETCH_COLUMN); } } else { $member_ids = [$member_id]; } foreach ($member_ids as $m_id) { $calls_log[] = [ 'proc' => 'proc_get_legal_education_certificate', 'params' => [ 'year' => $year, 'comp' => $comp, 'member_id' => $m_id, 'category_group' => $cat ] ]; $stmt = $pdo->prepare("CALL proc_get_legal_education_certificate(?, ?, ?, ?);"); $stmt->execute([ $year, $comp, $m_id, $cat ]); $row = $stmt->fetch(PDO::FETCH_ASSOC); while ($stmt->nextRowset()) { } // 소진 if ($row) { $decodedRow = []; foreach ($row as $key => $val) { $decodedKey = strtolower($key); if (is_object($val) && isset($val->type) && $val->type === 'Buffer') { $decodedRow[$decodedKey] = pack('C*', ...$val->data); } else { $decodedRow[$decodedKey] = $val; } } // 각 아이템별 동적 스탬프 및 로고 워터마크 이미지 검증 (admin/file 경로 우선, 기존 img 경로 차선 Fallback) $belong_comp_code = $decodedRow['belong_comp_code'] ?? $decodedRow['sys_comp_code'] ?? $comp; $logo_url = ""; $stamp_url = ""; if (!empty($belong_comp_code)) { // /www/admin/file/logo 및 /www/admin/file/stamp 컨테이너 내 절대 파일 경로 $abs_logo_svg = '/www/admin/file/logo/' . $belong_comp_code . '_logo.svg'; $abs_logo_png = '/www/admin/file/logo/' . $belong_comp_code . '_logo.png'; $abs_stamp_svg = '/www/admin/file/stamp/' . $belong_comp_code . '_stamp.svg'; $abs_stamp_png = '/www/admin/file/stamp/' . $belong_comp_code . '_stamp.png'; // 로컬 상대 파일 경로 (__DIR__ 기준 fallback) $admin_logo_svg = __DIR__ . '/../file/logo/' . $belong_comp_code . '_logo.svg'; $admin_logo_png = __DIR__ . '/../file/logo/' . $belong_comp_code . '_logo.png'; $admin_stamp_svg = __DIR__ . '/../file/stamp/' . $belong_comp_code . '_stamp.svg'; $admin_stamp_png = __DIR__ . '/../file/stamp/' . $belong_comp_code . '_stamp.png'; // 기존 img/ 절대 파일 경로 $img_logo_svg = __DIR__ . '/../../img/' . $belong_comp_code . '_logo.svg'; $img_logo_png = __DIR__ . '/../../img/' . $belong_comp_code . '_logo.png'; $img_stamp_svg = __DIR__ . '/../../img/' . $belong_comp_code . '_stamp.svg'; $img_stamp_png = __DIR__ . '/../../img/' . $belong_comp_code . '_stamp.png'; // 로고 매핑 (절대경로 및 로컬경로 존재 확인 후 웹 URL 설정 - /admin/file/logo/ 우선) if (file_exists($abs_logo_svg)) { $logo_url = "/admin/file/logo/" . $belong_comp_code . "_logo.svg"; } elseif (file_exists($admin_logo_svg)) { $logo_url = "/admin/file/logo/" . $belong_comp_code . "_logo.svg"; } elseif (file_exists($abs_logo_png)) { $logo_url = "/admin/file/logo/" . $belong_comp_code . "_logo.png"; } elseif (file_exists($admin_logo_png)) { $logo_url = "/admin/file/logo/" . $belong_comp_code . "_logo.png"; } elseif (file_exists($img_logo_svg)) { $logo_url = "/img/" . $belong_comp_code . "_logo.svg"; } elseif (file_exists($img_logo_png)) { $logo_url = "/img/" . $belong_comp_code . "_logo.png"; } // 직인 매핑 (절대경로 및 로컬경로 존재 확인 후 웹 URL 설정 - /admin/file/stamp/ 우선) if (file_exists($abs_stamp_svg)) { $stamp_url = "/admin/file/stamp/" . $belong_comp_code . "_stamp.svg"; } elseif (file_exists($admin_stamp_svg)) { $stamp_url = "/admin/file/stamp/" . $belong_comp_code . "_stamp.svg"; } elseif (file_exists($abs_stamp_png)) { $stamp_url = "/admin/file/stamp/" . $belong_comp_code . "_stamp.png"; } elseif (file_exists($admin_stamp_png)) { $stamp_url = "/admin/file/stamp/" . $belong_comp_code . "_stamp.png"; } elseif (file_exists($img_stamp_svg)) { $stamp_url = "/img/" . $belong_comp_code . "_stamp.svg"; } elseif (file_exists($img_stamp_png)) { $stamp_url = "/img/" . $belong_comp_code . "_stamp.png"; } } $decodedRow['logo_url'] = $logo_url; $decodedRow['stamp_url'] = $stamp_url; $decodedItems[] = $decodedRow; } } } if (empty($decodedItems)) { echo json_encode([ 'success' => false, 'message' => '해당 조건의 수료 정보가 존재하지 않습니다.', 'debug' => [ 'api_params' => [ 'year' => $year, 'comp' => $comp, 'member_id' => $member_id, 'category_group' => $category_group ], 'processed_categories' => $category_groups, 'total_legal_cnt' => $total_legal_cnt ?? null, 'matched_member_ids' => $member_ids ?? [], 'procedure_calls' => $calls_log, 'step_info' => 'No certificates could be loaded or formatted.' ] ], JSON_UNESCAPED_UNICODE); exit; } echo json_encode([ 'success' => true, 'data' => $decodedItems, 'debug' => [ 'procedure_calls' => $calls_log ] ], JSON_UNESCAPED_UNICODE); } catch (Exception $e) { error_log('[get_legal_cert_print] Error: ' . $e->getMessage()); echo json_encode([ 'success' => false, 'message' => '서버 내부 오류가 발생했습니다.', 'detail' => $e->getMessage(), 'debug' => [ 'procedure_calls' => $calls_log ?? [] ] ], JSON_UNESCAPED_UNICODE); exit; }