feat:가망고객(prospect) 상담 기록 및 첨부파일 기능 추가
- SalesConsultation 모델에 prospect 관련 메서드 추가 - createTextByProspect(), createAudioByProspect(), createFileByProspect() - getByProspectAndType() 조회 메서드 - ConsultationController에 prospect 라우트 추가 - prospectIndex(), prospectStore(), prospectUploadAudio(), prospectUploadFile() - scenario-modal.blade.php에서 @if(!$isProspectMode) 조건 제거 - 가망고객 모드에서도 상담 기록 섹션 표시 - voice-recorder, file-uploader, consultation-log에 prospect 모드 지원 - routes/web.php에 prospect 상담 기록 라우트 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Sales\SalesConsultation;
|
||||
use App\Models\Sales\TenantProspect;
|
||||
use App\Models\Tenants\Tenant;
|
||||
use App\Services\GoogleCloudStorageService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -279,4 +280,172 @@ public function downloadFile(int $consultationId): BinaryFileResponse
|
||||
|
||||
return response()->download($localPath, $consultation->file_name);
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Prospect(가망고객) 관련 메서드
|
||||
// ========================================
|
||||
|
||||
/**
|
||||
* 가망고객 상담 기록 목록 (HTMX 부분 뷰)
|
||||
*/
|
||||
public function prospectIndex(int $prospectId, Request $request): View
|
||||
{
|
||||
$prospect = TenantProspect::findOrFail($prospectId);
|
||||
$scenarioType = $request->input('scenario_type', 'sales');
|
||||
$stepId = $request->input('step_id');
|
||||
|
||||
$consultations = SalesConsultation::getByProspectAndType($prospectId, $scenarioType, $stepId);
|
||||
|
||||
return view('sales.modals.consultation-log', [
|
||||
'prospect' => $prospect,
|
||||
'isProspect' => true,
|
||||
'consultations' => $consultations,
|
||||
'scenarioType' => $scenarioType,
|
||||
'stepId' => $stepId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 가망고객 텍스트 상담 기록 저장
|
||||
*/
|
||||
public function prospectStore(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'prospect_id' => 'required|integer|exists:tenant_prospects,id',
|
||||
'scenario_type' => 'required|in:sales,manager',
|
||||
'step_id' => 'nullable|integer',
|
||||
'content' => 'required|string|max:5000',
|
||||
]);
|
||||
|
||||
$consultation = SalesConsultation::createTextByProspect(
|
||||
$request->input('prospect_id'),
|
||||
$request->input('scenario_type'),
|
||||
$request->input('step_id'),
|
||||
$request->input('content')
|
||||
);
|
||||
|
||||
$consultation->load('creator');
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'consultation' => [
|
||||
'id' => $consultation->id,
|
||||
'type' => $consultation->consultation_type,
|
||||
'content' => $consultation->content,
|
||||
'created_by_name' => $consultation->creator->name,
|
||||
'created_at' => $consultation->created_at->format('Y-m-d H:i'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 가망고객 음성 파일 업로드
|
||||
*/
|
||||
public function prospectUploadAudio(Request $request, GoogleCloudStorageService $gcs): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'prospect_id' => 'required|integer|exists:tenant_prospects,id',
|
||||
'scenario_type' => 'required|in:sales,manager',
|
||||
'step_id' => 'nullable|integer',
|
||||
'audio' => 'required|file|mimes:webm,mp3,wav,ogg|max:51200',
|
||||
'transcript' => 'nullable|string|max:10000',
|
||||
'duration' => 'nullable|integer',
|
||||
]);
|
||||
|
||||
$prospectId = $request->input('prospect_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();
|
||||
$localPath = $file->storeAs("prospect/consultations/{$prospectId}", $fileName, 'local');
|
||||
$fileSize = $file->getSize();
|
||||
|
||||
$gcsUri = null;
|
||||
$maxLocalSize = 10 * 1024 * 1024;
|
||||
|
||||
if ($fileSize > $maxLocalSize && $gcs->isAvailable()) {
|
||||
$gcsObjectName = "consultations/prospect/{$prospectId}/{$scenarioType}/{$fileName}";
|
||||
$localFullPath = Storage::disk('local')->path($localPath);
|
||||
$gcsUri = $gcs->upload($localFullPath, $gcsObjectName);
|
||||
}
|
||||
|
||||
$consultation = SalesConsultation::createAudioByProspect(
|
||||
$prospectId,
|
||||
$scenarioType,
|
||||
$stepId,
|
||||
$localPath,
|
||||
$fileName,
|
||||
$fileSize,
|
||||
$transcript,
|
||||
$duration,
|
||||
$gcsUri
|
||||
);
|
||||
|
||||
$consultation->load('creator');
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'consultation' => [
|
||||
'id' => $consultation->id,
|
||||
'type' => $consultation->consultation_type,
|
||||
'file_name' => $consultation->file_name,
|
||||
'transcript' => $consultation->transcript,
|
||||
'duration' => $consultation->duration,
|
||||
'formatted_duration' => $consultation->formatted_duration,
|
||||
'created_by_name' => $consultation->creator->name,
|
||||
'created_at' => $consultation->created_at->format('Y-m-d H:i'),
|
||||
'has_gcs' => !empty($gcsUri),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 가망고객 첨부파일 업로드
|
||||
*/
|
||||
public function prospectUploadFile(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'prospect_id' => 'required|integer|exists:tenant_prospects,id',
|
||||
'scenario_type' => 'required|in:sales,manager',
|
||||
'step_id' => 'nullable|integer',
|
||||
'file' => 'required|file|max:20480',
|
||||
]);
|
||||
|
||||
$prospectId = $request->input('prospect_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("prospect/attachments/{$prospectId}", $fileName, 'local');
|
||||
|
||||
$consultation = SalesConsultation::createFileByProspect(
|
||||
$prospectId,
|
||||
$scenarioType,
|
||||
$stepId,
|
||||
$path,
|
||||
$originalName,
|
||||
$file->getSize(),
|
||||
$file->getMimeType()
|
||||
);
|
||||
|
||||
$consultation->load('creator');
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'consultation' => [
|
||||
'id' => $consultation->id,
|
||||
'type' => $consultation->consultation_type,
|
||||
'file_name' => $consultation->file_name,
|
||||
'file_size' => $consultation->file_size,
|
||||
'formatted_file_size' => $consultation->formatted_file_size,
|
||||
'created_by_name' => $consultation->creator->name,
|
||||
'created_at' => $consultation->created_at->format('Y-m-d H:i'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user