- InterviewProject/Attachment/Knowledge 모델 3개 신규 - 기존 모델 확장 (Question, Answer, Session, Category) - 서비스 확장: 프로젝트 CRUD, 첨부파일, 지식 관리 - 컨트롤러 확장: 프로젝트/첨부/지식 API 엔드포인트 - 라우트 20개 추가 (프로젝트, 첨부, 지식) - InterviewQuestionMasterSeeder: 8개 도메인 80개 질문 - UI 확장: 프로젝트 모드/기존 모드 전환 - 프로젝트 선택 바, 상태 바, 도메인 사이드바 - 탭 구조 (질문편집/인터뷰/첨부파일/추출지식) - 구조화 답변 입력 (테이블, 수식, 다중선택 등) - 첨부파일 업로드/관리 - 지식 수동 추가/검증/필터링
409 lines
13 KiB
PHP
409 lines
13 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Sales;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Services\Sales\InterviewScenarioService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Response;
|
|
use Illuminate\View\View;
|
|
|
|
class InterviewScenarioController extends Controller
|
|
{
|
|
public function __construct(
|
|
private InterviewScenarioService $service,
|
|
) {}
|
|
|
|
// ============================================================
|
|
// 페이지
|
|
// ============================================================
|
|
|
|
public function index(Request $request): View|Response
|
|
{
|
|
if ($request->header('HX-Request')) {
|
|
return response('', 200)->header('HX-Redirect', route('sales.interviews.index'));
|
|
}
|
|
|
|
return view('sales.interviews.index');
|
|
}
|
|
|
|
// ============================================================
|
|
// 카테고리 API
|
|
// ============================================================
|
|
|
|
public function categories(): JsonResponse
|
|
{
|
|
return response()->json($this->service->getCategories());
|
|
}
|
|
|
|
public function storeCategory(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'name' => 'required|string|max:100',
|
|
'description' => 'nullable|string',
|
|
]);
|
|
|
|
$category = $this->service->createCategory($validated);
|
|
|
|
return response()->json($category, 201);
|
|
}
|
|
|
|
public function updateCategory(Request $request, int $id): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'name' => 'required|string|max:100',
|
|
'description' => 'nullable|string',
|
|
]);
|
|
|
|
$category = $this->service->updateCategory($id, $validated);
|
|
|
|
return response()->json($category);
|
|
}
|
|
|
|
public function destroyCategory(int $id): JsonResponse
|
|
{
|
|
$this->service->deleteCategory($id);
|
|
|
|
return response()->json(['message' => '삭제되었습니다.']);
|
|
}
|
|
|
|
// ============================================================
|
|
// 트리 API
|
|
// ============================================================
|
|
|
|
public function tree(): JsonResponse
|
|
{
|
|
return response()->json($this->service->getTree());
|
|
}
|
|
|
|
// ============================================================
|
|
// 템플릿(항목) API
|
|
// ============================================================
|
|
|
|
public function storeTemplate(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'interview_category_id' => 'required|integer|exists:interview_categories,id',
|
|
'name' => 'required|string|max:200',
|
|
'description' => 'nullable|string',
|
|
]);
|
|
|
|
$template = $this->service->createTemplate($validated);
|
|
|
|
return response()->json($template, 201);
|
|
}
|
|
|
|
public function updateTemplate(Request $request, int $id): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'name' => 'required|string|max:200',
|
|
'description' => 'nullable|string',
|
|
]);
|
|
|
|
$template = $this->service->updateTemplate($id, $validated);
|
|
|
|
return response()->json($template);
|
|
}
|
|
|
|
public function destroyTemplate(int $id): JsonResponse
|
|
{
|
|
$this->service->deleteTemplate($id);
|
|
|
|
return response()->json(['message' => '삭제되었습니다.']);
|
|
}
|
|
|
|
// ============================================================
|
|
// 질문 API
|
|
// ============================================================
|
|
|
|
public function storeQuestion(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'interview_template_id' => 'required|integer|exists:interview_templates,id',
|
|
'question_text' => 'required|string|max:500',
|
|
'question_type' => 'nullable|string|in:checkbox,text,number,select,multi_select,file_upload,formula_input,table_input,bom_tree,price_table,dimension_diagram',
|
|
'options' => 'nullable|array',
|
|
'ai_hint' => 'nullable|string',
|
|
'expected_format' => 'nullable|string|max:100',
|
|
'depends_on' => 'nullable|array',
|
|
'domain' => 'nullable|string|max:50',
|
|
'is_required' => 'nullable|boolean',
|
|
]);
|
|
|
|
$question = $this->service->createQuestion($validated);
|
|
|
|
return response()->json($question, 201);
|
|
}
|
|
|
|
public function updateQuestion(Request $request, int $id): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'question_text' => 'required|string|max:500',
|
|
'question_type' => 'nullable|string|in:checkbox,text,number,select,multi_select,file_upload,formula_input,table_input,bom_tree,price_table,dimension_diagram',
|
|
'is_required' => 'nullable|boolean',
|
|
]);
|
|
|
|
$question = $this->service->updateQuestion($id, $validated);
|
|
|
|
return response()->json($question);
|
|
}
|
|
|
|
public function destroyQuestion(int $id): JsonResponse
|
|
{
|
|
$this->service->deleteQuestion($id);
|
|
|
|
return response()->json(['message' => '삭제되었습니다.']);
|
|
}
|
|
|
|
// ============================================================
|
|
// MD 파일 일괄 가져오기
|
|
// ============================================================
|
|
|
|
public function bulkImport(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'category_id' => 'required|integer|exists:interview_categories,id',
|
|
'templates' => 'required|array|min:1',
|
|
'templates.*.name' => 'required|string|max:200',
|
|
'templates.*.questions' => 'required|array|min:1',
|
|
'templates.*.questions.*' => 'required|string|max:500',
|
|
]);
|
|
|
|
$result = $this->service->bulkImport($validated['category_id'], $validated['templates']);
|
|
|
|
return response()->json($result, 201);
|
|
}
|
|
|
|
// ============================================================
|
|
// 세션 API
|
|
// ============================================================
|
|
|
|
public function sessions(Request $request): JsonResponse
|
|
{
|
|
$filters = $request->only(['status', 'category_id']);
|
|
$sessions = $this->service->getSessions($filters);
|
|
|
|
return response()->json($sessions);
|
|
}
|
|
|
|
public function storeSession(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'interview_category_id' => 'required|integer|exists:interview_categories,id',
|
|
'interviewee_name' => 'nullable|string|max:100',
|
|
'interviewee_company' => 'nullable|string|max:200',
|
|
'interview_date' => 'nullable|date',
|
|
'memo' => 'nullable|string',
|
|
]);
|
|
|
|
$session = $this->service->startSession($validated);
|
|
|
|
return response()->json($session, 201);
|
|
}
|
|
|
|
public function showSession(int $id): JsonResponse
|
|
{
|
|
$session = $this->service->getSessionDetail($id);
|
|
|
|
return response()->json($session);
|
|
}
|
|
|
|
public function toggleAnswer(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'session_id' => 'required|integer',
|
|
'question_id' => 'required|integer',
|
|
'answer_text' => 'nullable|string',
|
|
'memo' => 'nullable|string',
|
|
]);
|
|
|
|
$answer = $this->service->toggleAnswer($validated);
|
|
|
|
return response()->json($answer);
|
|
}
|
|
|
|
public function completeSession(int $id): JsonResponse
|
|
{
|
|
$session = $this->service->completeSession($id);
|
|
|
|
return response()->json($session);
|
|
}
|
|
|
|
// ============================================================
|
|
// 프로젝트 API
|
|
// ============================================================
|
|
|
|
public function projects(Request $request): JsonResponse
|
|
{
|
|
$filters = $request->only(['status', 'search']);
|
|
|
|
return response()->json($this->service->getProjects($filters));
|
|
}
|
|
|
|
public function showProject(int $id): JsonResponse
|
|
{
|
|
return response()->json($this->service->getProject($id));
|
|
}
|
|
|
|
public function storeProject(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'company_name' => 'required|string|max:200',
|
|
'company_type' => 'nullable|string|max:100',
|
|
'contact_person' => 'nullable|string|max:100',
|
|
'contact_info' => 'nullable|string|max:200',
|
|
'product_categories' => 'nullable|array',
|
|
]);
|
|
|
|
$project = $this->service->createProject($validated);
|
|
|
|
return response()->json($project, 201);
|
|
}
|
|
|
|
public function updateProject(Request $request, int $id): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'company_name' => 'sometimes|string|max:200',
|
|
'company_type' => 'nullable|string|max:100',
|
|
'contact_person' => 'nullable|string|max:100',
|
|
'contact_info' => 'nullable|string|max:200',
|
|
'status' => 'nullable|string|in:draft,interviewing,analyzing,code_generated,deployed',
|
|
'product_categories' => 'nullable|array',
|
|
'summary' => 'nullable|string',
|
|
]);
|
|
|
|
$project = $this->service->updateProject($id, $validated);
|
|
|
|
return response()->json($project);
|
|
}
|
|
|
|
public function destroyProject(int $id): JsonResponse
|
|
{
|
|
$this->service->deleteProject($id);
|
|
|
|
return response()->json(['message' => '삭제되었습니다.']);
|
|
}
|
|
|
|
public function projectTree(int $id): JsonResponse
|
|
{
|
|
return response()->json($this->service->getProjectTree($id));
|
|
}
|
|
|
|
public function projectProgress(int $id): JsonResponse
|
|
{
|
|
$project = $this->service->updateProjectProgress($id);
|
|
|
|
return response()->json($project);
|
|
}
|
|
|
|
// ============================================================
|
|
// 첨부파일 API
|
|
// ============================================================
|
|
|
|
public function attachments(int $projectId): JsonResponse
|
|
{
|
|
return response()->json($this->service->getAttachments($projectId));
|
|
}
|
|
|
|
public function uploadAttachment(Request $request, int $projectId): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'file' => 'required|file|max:51200',
|
|
'file_type' => 'nullable|string|in:excel_template,pdf_quote,sample_bom,price_list,photo,voice,other',
|
|
'description' => 'nullable|string|max:500',
|
|
]);
|
|
|
|
$attachment = $this->service->uploadAttachment(
|
|
$projectId,
|
|
$request->only(['file_type', 'description']),
|
|
$request->file('file')
|
|
);
|
|
|
|
return response()->json($attachment, 201);
|
|
}
|
|
|
|
public function destroyAttachment(int $id): JsonResponse
|
|
{
|
|
$this->service->deleteAttachment($id);
|
|
|
|
return response()->json(['message' => '삭제되었습니다.']);
|
|
}
|
|
|
|
// ============================================================
|
|
// 지식 API
|
|
// ============================================================
|
|
|
|
public function knowledge(Request $request, int $projectId): JsonResponse
|
|
{
|
|
$filters = $request->only(['domain', 'is_verified', 'min_confidence']);
|
|
|
|
return response()->json($this->service->getKnowledge($projectId, $filters));
|
|
}
|
|
|
|
public function storeKnowledge(Request $request, int $projectId): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'domain' => 'required|string|in:product_classification,bom_structure,dimension_formula,component_config,pricing_structure,quantity_formula,conditional_logic,quote_format',
|
|
'knowledge_type' => 'required|string|in:fact,rule,formula,mapping,range,table',
|
|
'title' => 'required|string|max:300',
|
|
'content' => 'required|array',
|
|
'source_type' => 'nullable|string|in:interview_answer,voice_recording,document,manual',
|
|
'source_id' => 'nullable|integer',
|
|
'confidence' => 'nullable|numeric|min:0|max:1',
|
|
]);
|
|
|
|
$knowledge = $this->service->createKnowledge($projectId, $validated);
|
|
|
|
return response()->json($knowledge, 201);
|
|
}
|
|
|
|
public function updateKnowledge(Request $request, int $id): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'title' => 'sometimes|string|max:300',
|
|
'content' => 'sometimes|array',
|
|
'confidence' => 'nullable|numeric|min:0|max:1',
|
|
]);
|
|
|
|
$knowledge = $this->service->updateKnowledge($id, $validated);
|
|
|
|
return response()->json($knowledge);
|
|
}
|
|
|
|
public function verifyKnowledge(int $id): JsonResponse
|
|
{
|
|
$knowledge = $this->service->verifyKnowledge($id);
|
|
|
|
return response()->json($knowledge);
|
|
}
|
|
|
|
public function destroyKnowledge(int $id): JsonResponse
|
|
{
|
|
$this->service->deleteKnowledge($id);
|
|
|
|
return response()->json(['message' => '삭제되었습니다.']);
|
|
}
|
|
|
|
// ============================================================
|
|
// 구조화 답변 저장 API
|
|
// ============================================================
|
|
|
|
public function saveAnswer(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'session_id' => 'required|integer',
|
|
'question_id' => 'required|integer',
|
|
'is_checked' => 'nullable|boolean',
|
|
'answer_text' => 'nullable|string',
|
|
'answer_data' => 'nullable|array',
|
|
'attachments' => 'nullable|array',
|
|
'memo' => 'nullable|string',
|
|
]);
|
|
|
|
$answer = $this->service->saveAnswer($validated);
|
|
|
|
return response()->json($answer);
|
|
}
|
|
}
|