Files
sam-manage/app/Http/Controllers/Sales/ConsultationController.php
pro 2f381b2285 feat:레거시 영업관리 시스템 MNG 마이그레이션
- 영업/매니저 시나리오 모달 구현 (6단계 체크리스트)
- 상담 기록 기능 (텍스트, 음성, 첨부파일)
- 음성 녹음 + Speech-to-Text 변환
- 첨부파일 Drag & Drop 업로드
- 매니저 지정 드롭다운

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 21:45:11 +09:00

287 lines
9.1 KiB
PHP

<?php
namespace App\Http\Controllers\Sales;
use App\Http\Controllers\Controller;
use App\Models\Tenants\Tenant;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
/**
* 상담 기록 관리 컨트롤러
*
* 테넌트별 상담 기록(텍스트, 음성, 파일)을 관리합니다.
*/
class ConsultationController extends Controller
{
/**
* 상담 기록 목록 (HTMX 부분 뷰)
*/
public function index(int $tenantId, Request $request): View
{
$tenant = Tenant::findOrFail($tenantId);
$scenarioType = $request->input('scenario_type', 'sales');
$stepId = $request->input('step_id');
// 캐시에서 상담 기록 조회
$cacheKey = "consultations:{$tenantId}:{$scenarioType}";
$consultations = cache()->get($cacheKey, []);
// 특정 단계 필터링
if ($stepId) {
$consultations = array_filter($consultations, fn($c) => ($c['step_id'] ?? null) == $stepId);
}
// 최신순 정렬
usort($consultations, fn($a, $b) => strtotime($b['created_at']) - strtotime($a['created_at']));
return view('sales.modals.consultation-log', [
'tenant' => $tenant,
'consultations' => $consultations,
'scenarioType' => $scenarioType,
'stepId' => $stepId,
]);
}
/**
* 텍스트 상담 기록 저장
*/
public function store(Request $request): JsonResponse
{
$request->validate([
'tenant_id' => 'required|integer|exists:tenants,id',
'scenario_type' => 'required|in:sales,manager',
'step_id' => 'nullable|integer',
'content' => 'required|string|max:5000',
]);
$tenantId = $request->input('tenant_id');
$scenarioType = $request->input('scenario_type');
$stepId = $request->input('step_id');
$content = $request->input('content');
// 캐시 키
$cacheKey = "consultations:{$tenantId}:{$scenarioType}";
$consultations = cache()->get($cacheKey, []);
// 새 상담 기록 추가
$consultation = [
'id' => uniqid('cons_'),
'type' => 'text',
'content' => $content,
'step_id' => $stepId,
'created_by' => auth()->id(),
'created_by_name' => auth()->user()->name,
'created_at' => now()->toDateTimeString(),
];
$consultations[] = $consultation;
// 캐시에 저장 (90일 유지)
cache()->put($cacheKey, $consultations, now()->addDays(90));
return response()->json([
'success' => true,
'consultation' => $consultation,
]);
}
/**
* 상담 기록 삭제
*/
public function destroy(string $consultationId, Request $request): JsonResponse
{
$request->validate([
'tenant_id' => 'required|integer|exists:tenants,id',
'scenario_type' => 'required|in:sales,manager',
]);
$tenantId = $request->input('tenant_id');
$scenarioType = $request->input('scenario_type');
// 캐시 키
$cacheKey = "consultations:{$tenantId}:{$scenarioType}";
$consultations = cache()->get($cacheKey, []);
// 상담 기록 찾기 및 삭제
$found = false;
foreach ($consultations as $index => $consultation) {
if ($consultation['id'] === $consultationId) {
// 파일이 있으면 삭제
if (isset($consultation['file_path'])) {
Storage::delete($consultation['file_path']);
}
unset($consultations[$index]);
$found = true;
break;
}
}
if (!$found) {
return response()->json([
'success' => false,
'message' => '상담 기록을 찾을 수 없습니다.',
], 404);
}
// 캐시에 저장
cache()->put($cacheKey, array_values($consultations), now()->addDays(90));
return response()->json([
'success' => true,
]);
}
/**
* 음성 파일 업로드
*/
public function uploadAudio(Request $request): JsonResponse
{
$request->validate([
'tenant_id' => 'required|integer|exists:tenants,id',
'scenario_type' => 'required|in:sales,manager',
'step_id' => 'nullable|integer',
'audio' => 'required|file|mimes:webm,mp3,wav,ogg|max:51200', // 50MB
'transcript' => 'nullable|string|max:10000',
'duration' => 'nullable|integer',
]);
$tenantId = $request->input('tenant_id');
$scenarioType = $request->input('scenario_type');
$stepId = $request->input('step_id');
$transcript = $request->input('transcript');
$duration = $request->input('duration');
// 파일 저장
$file = $request->file('audio');
$fileName = 'audio_' . now()->format('Ymd_His') . '_' . uniqid() . '.' . $file->getClientOriginalExtension();
$path = $file->storeAs("tenant/consultations/{$tenantId}", $fileName, 'local');
// 캐시 키
$cacheKey = "consultations:{$tenantId}:{$scenarioType}";
$consultations = cache()->get($cacheKey, []);
// 새 상담 기록 추가
$consultation = [
'id' => uniqid('cons_'),
'type' => 'audio',
'file_path' => $path,
'file_name' => $fileName,
'transcript' => $transcript,
'duration' => $duration,
'step_id' => $stepId,
'created_by' => auth()->id(),
'created_by_name' => auth()->user()->name,
'created_at' => now()->toDateTimeString(),
];
$consultations[] = $consultation;
// 캐시에 저장
cache()->put($cacheKey, $consultations, now()->addDays(90));
return response()->json([
'success' => true,
'consultation' => $consultation,
]);
}
/**
* 첨부파일 업로드
*/
public function uploadFile(Request $request): JsonResponse
{
$request->validate([
'tenant_id' => 'required|integer|exists:tenants,id',
'scenario_type' => 'required|in:sales,manager',
'step_id' => 'nullable|integer',
'file' => 'required|file|max:20480', // 20MB
]);
$tenantId = $request->input('tenant_id');
$scenarioType = $request->input('scenario_type');
$stepId = $request->input('step_id');
// 파일 저장
$file = $request->file('file');
$originalName = $file->getClientOriginalName();
$fileName = now()->format('Ymd_His') . '_' . uniqid() . '_' . $originalName;
$path = $file->storeAs("tenant/attachments/{$tenantId}", $fileName, 'local');
// 캐시 키
$cacheKey = "consultations:{$tenantId}:{$scenarioType}";
$consultations = cache()->get($cacheKey, []);
// 새 상담 기록 추가
$consultation = [
'id' => uniqid('cons_'),
'type' => 'file',
'file_path' => $path,
'file_name' => $originalName,
'file_size' => $file->getSize(),
'file_type' => $file->getMimeType(),
'step_id' => $stepId,
'created_by' => auth()->id(),
'created_by_name' => auth()->user()->name,
'created_at' => now()->toDateTimeString(),
];
$consultations[] = $consultation;
// 캐시에 저장
cache()->put($cacheKey, $consultations, now()->addDays(90));
return response()->json([
'success' => true,
'consultation' => $consultation,
]);
}
/**
* 파일 삭제
*/
public function deleteFile(string $fileId, Request $request): JsonResponse
{
return $this->destroy($fileId, $request);
}
/**
* 파일 다운로드 URL 생성
*/
public function getDownloadUrl(string $consultationId, Request $request): JsonResponse
{
$request->validate([
'tenant_id' => 'required|integer|exists:tenants,id',
'scenario_type' => 'required|in:sales,manager',
]);
$tenantId = $request->input('tenant_id');
$scenarioType = $request->input('scenario_type');
// 캐시 키
$cacheKey = "consultations:{$tenantId}:{$scenarioType}";
$consultations = cache()->get($cacheKey, []);
// 상담 기록 찾기
$consultation = collect($consultations)->firstWhere('id', $consultationId);
if (!$consultation || !isset($consultation['file_path'])) {
return response()->json([
'success' => false,
'message' => '파일을 찾을 수 없습니다.',
], 404);
}
// 임시 다운로드 URL 생성 (5분 유효)
$url = Storage::temporaryUrl($consultation['file_path'], now()->addMinutes(5));
return response()->json([
'success' => true,
'url' => $url,
]);
}
}