Files
sam-sales/sales_manager_scenario/save_consultation.php
2025-12-17 12:59:26 +09:00

219 lines
7.3 KiB
PHP

<?php
// 출력 버퍼링 시작 및 에러 리포팅 비활성화
error_reporting(0);
ob_start();
require_once($_SERVER['DOCUMENT_ROOT'] . "/session.php");
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
// GCS 업로드 함수 (GCP_storage_dev.md 참고)
function uploadToGCS($file_path, $bucket_name, $object_name, $service_account_path) {
if (!file_exists($service_account_path)) {
error_log('GCS 업로드 실패: 서비스 계정 파일 없음');
return false;
}
$serviceAccount = json_decode(file_get_contents($service_account_path), true);
if (!$serviceAccount) {
error_log('GCS 업로드 실패: 서비스 계정 JSON 파싱 오류');
return false;
}
// OAuth 2.0 토큰 생성
$now = time();
$jwtHeader = base64_encode(json_encode(['alg' => 'RS256', 'typ' => 'JWT']));
$jwtClaim = base64_encode(json_encode([
'iss' => $serviceAccount['client_email'],
'scope' => 'https://www.googleapis.com/auth/devstorage.full_control',
'aud' => 'https://oauth2.googleapis.com/token',
'exp' => $now + 3600,
'iat' => $now
]));
$privateKey = openssl_pkey_get_private($serviceAccount['private_key']);
if (!$privateKey) {
error_log('GCS 업로드 실패: 개인 키 읽기 오류');
return false;
}
openssl_sign($jwtHeader . '.' . $jwtClaim, $signature, $privateKey, OPENSSL_ALGO_SHA256);
openssl_free_key($privateKey);
$jwt = $jwtHeader . '.' . $jwtClaim . '.' . base64_encode($signature);
// OAuth 토큰 요청
$tokenCh = curl_init('https://oauth2.googleapis.com/token');
curl_setopt($tokenCh, CURLOPT_POST, true);
curl_setopt($tokenCh, CURLOPT_POSTFIELDS, http_build_query([
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion' => $jwt
]));
curl_setopt($tokenCh, CURLOPT_RETURNTRANSFER, true);
curl_setopt($tokenCh, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
$tokenResponse = curl_exec($tokenCh);
$tokenCode = curl_getinfo($tokenCh, CURLINFO_HTTP_CODE);
curl_close($tokenCh);
if ($tokenCode !== 200) {
error_log('GCS 업로드 실패: OAuth 토큰 요청 실패 (HTTP ' . $tokenCode . ')');
return false;
}
$tokenData = json_decode($tokenResponse, true);
if (!isset($tokenData['access_token'])) {
error_log('GCS 업로드 실패: OAuth 토큰 없음');
return false;
}
$accessToken = $tokenData['access_token'];
// GCS에 파일 업로드
$file_content = file_get_contents($file_path);
$mime_type = mime_content_type($file_path) ?: 'audio/webm';
$upload_url = 'https://storage.googleapis.com/upload/storage/v1/b/' .
urlencode($bucket_name) . '/o?uploadType=media&name=' .
urlencode($object_name);
$ch = curl_init($upload_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $accessToken,
'Content-Type: ' . $mime_type,
'Content-Length: ' . strlen($file_content)
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, $file_content);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code === 200) {
return 'gs://' . $bucket_name . '/' . $object_name;
} else {
error_log('GCS 업로드 실패 (HTTP ' . $code . '): ' . $response);
return false;
}
}
// 출력 버퍼 비우기
ob_clean();
header('Content-Type: application/json; charset=utf-8');
// 1. 권한 및 세션 체크
if (!isset($user_id) || $level > 5) {
echo json_encode(['success' => false, 'message' => '접근 권한이 없습니다.']);
exit;
}
$manager_id = $user_id; // session.php에서 user_id를 manager_id로 사용
$upload_dir = $_SERVER['DOCUMENT_ROOT'] . "/uploads/manager_consultations/" . $manager_id . "/";
// 2. 파일 업로드 처리
if (!file_exists($upload_dir)) mkdir($upload_dir, 0777, true);
if (!isset($_FILES['audio_file'])) {
echo json_encode(['success' => false, 'message' => '오디오 파일이 없습니다.']);
exit;
}
// 파일 크기 확인
if ($_FILES['audio_file']['size'] == 0) {
echo json_encode(['success' => false, 'message' => '오디오 파일이 비어있습니다.']);
exit;
}
$file_name = date('Ymd_His') . "_" . uniqid() . ".webm";
$file_path = $upload_dir . $file_name;
if (!move_uploaded_file($_FILES['audio_file']['tmp_name'], $file_path)) {
echo json_encode(['success' => false, 'message' => '파일 저장 실패']);
exit;
}
// 3. GCS 업로드 (선택사항 - 파일이 큰 경우)
$gcs_uri = null;
$file_size = filesize($file_path);
$max_local_size = 10 * 1024 * 1024; // 10MB
if ($file_size > $max_local_size) {
// GCS 설정 확인
$gcs_config_file = $_SERVER['DOCUMENT_ROOT'] . '/apikey/gcs_config.txt';
$bucket_name = null;
if (file_exists($gcs_config_file)) {
$gcs_config = parse_ini_file($gcs_config_file);
$bucket_name = isset($gcs_config['bucket_name']) ? $gcs_config['bucket_name'] : null;
}
if ($bucket_name) {
$googleServiceAccountFile = $_SERVER['DOCUMENT_ROOT'] . '/apikey/google_service_account.json';
$gcs_object_name = 'manager_consultations/' . $manager_id . '/' . basename($file_path);
$gcs_uri = uploadToGCS($file_path, $bucket_name, $gcs_object_name, $googleServiceAccountFile);
if ($gcs_uri) {
// GCS 업로드 성공 시 로컬 파일 삭제 (선택사항)
// @unlink($file_path);
}
}
}
// 4. DB 저장
$transcript = isset($_POST['transcript']) ? trim($_POST['transcript']) : '';
$step_id = isset($_POST['step_id']) ? intval($_POST['step_id']) : 2;
$web_path = "/uploads/manager_consultations/" . $manager_id . "/" . $file_name;
$file_path_to_store = $gcs_uri ? $gcs_uri : $web_path;
try {
$pdo = db_connect();
if (!$pdo) {
throw new Exception('데이터베이스 연결 실패');
}
$expiry_date = date('Y-m-d H:i:s', strtotime('+30 days')); // 30일 보관
// manager_consultations 테이블에 저장 (테이블이 없으면 생성 필요)
$sql = "INSERT INTO manager_consultations
(manager_id, step_id, audio_file_path, transcript_text, file_expiry_date, created_at)
VALUES (?, ?, ?, ?, ?, NOW())";
$stmt = $pdo->prepare($sql);
if (!$stmt) {
throw new Exception('SQL 준비 실패');
}
$executeResult = $stmt->execute([
$manager_id,
$step_id,
$file_path_to_store,
$transcript,
$expiry_date
]);
if (!$executeResult) {
throw new Exception('SQL 실행 실패');
}
$insertId = $pdo->lastInsertId();
echo json_encode([
'success' => true,
'message' => '녹음 파일이 저장되었습니다.',
'id' => $insertId,
'file_path' => $file_path_to_store,
'gcs_uri' => $gcs_uri
]);
} catch (Exception $e) {
error_log('DB 저장 오류: ' . $e->getMessage());
echo json_encode([
'success' => false,
'message' => '데이터베이스 저장 실패: ' . $e->getMessage()
]);
}
?>