Files
sam-manage/app/Http/Controllers/RoadmapController.php
김보곤 322442aef6 fix: [roadmap] 개발서버 문서 경로 설정 가능하도록 개선
- config/roadmap.php 추가 (ROADMAP_DOCS_BASE 환경변수)
- RoadmapController에서 config 기반 경로 사용
- 로컬: base_path('../docs') 기본값 유지
- 서버: .env에서 ROADMAP_DOCS_BASE 설정
2026-03-04 13:21:56 +09:00

243 lines
9.1 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Admin\AdminRoadmapPlan;
use App\Services\Roadmap\RoadmapPlanService;
use Illuminate\Support\Str;
use Illuminate\View\View;
class RoadmapController extends Controller
{
/** @deprecated config('roadmap.docs_base')로 대체 */
private const DOCS_BASE = __DIR__.'/../../../../docs';
private const DOCUMENT_REGISTRY = [
[
'category' => 'vision',
'category_label' => '비전 & 전략',
'icon' => 'ri-lightbulb-line',
'color' => 'indigo',
'items' => [
[
'slug' => 'ai-automation-vision',
'title' => 'SAM AI 자동화 비전',
'description' => 'SAM의 장기 비전과 AI 자동화 전략. 영업에서 출고까지 End-to-End 자동화 로드맵.',
'path' => 'system/ai-automation-vision.md',
'date' => '2026-03-02',
'badge' => '설계 확정',
],
[
'slug' => 'scaling-roadmap',
'title' => '10,000 테넌트 스케일링 로드맵',
'description' => '현재 아키텍처 진단부터 5단계 스케일링 계획까지. 세계 수준 엔지니어링 시나리오.',
'path' => 'system/scaling-roadmap.md',
'date' => '2026-02-22',
'badge' => '가상 시나리오',
],
],
],
[
'category' => 'launch',
'category_label' => '프로젝트 런칭',
'icon' => 'ri-rocket-line',
'color' => 'blue',
'items' => [
[
'slug' => 'project-launch-roadmap',
'title' => 'SAM 프로젝트 런칭 로드맵',
'description' => '전체 시스템 구성, MVP 범위, 마일스톤(MS1~MS3), 개발 완료율 현황.',
'path' => 'guides/project-launch-roadmap.md',
'date' => '2025-12-02',
'badge' => '진행중',
],
[
'slug' => 'production-deployment-plan',
'title' => '운영 환경 배포 계획서',
'description' => 'MS3 정식 런칭 배포 계획. 무중단 전환, 롤백, Jenkins CI/CD 자동화.',
'path' => 'plans/production-deployment-plan.md',
'date' => '2026-02-22',
'badge' => '계획 수립',
],
],
],
[
'category' => 'product',
'category_label' => '제품 설계',
'icon' => 'ri-draft-line',
'color' => 'green',
'items' => [
[
'slug' => 'erp-storyboard',
'title' => 'SAM ERP 스토리보드 D1.4',
'description' => '전체 ERP 메뉴 구조와 화면 설계. 대시보드, MES, HR, 전자결재, 회계, 구독 관리.',
'path' => 'plans/SAM_ERP_Storyboard_D1.4.md',
'date' => '2026-01-16',
'badge' => 'D1.4',
],
[
'slug' => 'erp-accounting-storyboard',
'title' => 'SAM ERP 회계관리 스토리보드 D1.6',
'description' => '세금계산서, 계좌 입출금, OCR, 일일 보고서, 건설/생산 대시보드.',
'path' => 'plans/SAM_ERP_회계관리_Storyboard_D1.6.md',
'date' => '2026-02-20',
'badge' => 'D1.6',
],
[
'slug' => 'integrated-master-plan',
'title' => '통합 개선 마스터 플랜',
'description' => '제품코드 추적성 + 검사 단위 구조 통합 개선. 7단계 Phase 로드맵.',
'path' => 'plans/integrated-master-plan.md',
'date' => '2026-02-27',
'badge' => 'Phase 0~3 완료',
],
[
'slug' => 'ai-quotation-engine-plan',
'title' => 'AI 견적서 자동생성 엔진 개발 계획',
'description' => '인터뷰 내용을 AI가 분석하여 SAM 표준 견적서로 자동 변환하는 엔진. Claude API 기반.',
'path' => 'plans/ai-quotation-engine-plan.md',
'date' => '2026-03-02',
'badge' => '기획 초안',
],
],
],
[
'category' => 'system',
'category_label' => '시스템 개요',
'icon' => 'ri-server-line',
'color' => 'gray',
'items' => [
[
'slug' => 'system-overview',
'title' => 'SAM 시스템 개요',
'description' => '프로젝트 아키텍처, 기술 스택, 멀티테넌시, 레거시 마이그레이션 현황.',
'path' => 'system/overview.md',
'date' => '2026-02-27',
'badge' => '최신',
],
],
],
];
public function __construct(
private readonly RoadmapPlanService $planService
) {}
private function getDocsBasePath(): string
{
$path = config('roadmap.docs_base', self::DOCS_BASE);
return realpath($path) ?: $path;
}
public function index(): View
{
$summary = $this->planService->getDashboardSummary();
$statuses = AdminRoadmapPlan::getStatuses();
$categories = AdminRoadmapPlan::getCategories();
$priorities = AdminRoadmapPlan::getPriorities();
$phases = AdminRoadmapPlan::getPhases();
return view('roadmap.index', compact(
'summary', 'statuses', 'categories', 'priorities', 'phases'
));
}
public function plans(): View
{
$statuses = AdminRoadmapPlan::getStatuses();
$categories = AdminRoadmapPlan::getCategories();
$priorities = AdminRoadmapPlan::getPriorities();
$phases = AdminRoadmapPlan::getPhases();
return view('roadmap.plans.index', compact('statuses', 'categories', 'priorities', 'phases'));
}
public function createPlan(): View
{
$statuses = AdminRoadmapPlan::getStatuses();
$categories = AdminRoadmapPlan::getCategories();
$priorities = AdminRoadmapPlan::getPriorities();
$phases = AdminRoadmapPlan::getPhases();
return view('roadmap.plans.create', compact('statuses', 'categories', 'priorities', 'phases'));
}
public function showPlan(int $id): View
{
$plan = $this->planService->getPlanById($id, true);
if (! $plan) {
abort(404, '계획을 찾을 수 없습니다.');
}
$statuses = AdminRoadmapPlan::getStatuses();
$categories = AdminRoadmapPlan::getCategories();
$priorities = AdminRoadmapPlan::getPriorities();
$phases = AdminRoadmapPlan::getPhases();
return view('roadmap.plans.show', compact(
'plan', 'statuses', 'categories', 'priorities', 'phases'
));
}
public function editPlan(int $id): View
{
$plan = $this->planService->getPlanById($id, true);
if (! $plan) {
abort(404, '계획을 찾을 수 없습니다.');
}
$statuses = AdminRoadmapPlan::getStatuses();
$categories = AdminRoadmapPlan::getCategories();
$priorities = AdminRoadmapPlan::getPriorities();
$phases = AdminRoadmapPlan::getPhases();
return view('roadmap.plans.edit', compact(
'plan', 'statuses', 'categories', 'priorities', 'phases'
));
}
public function documents(): View
{
$registry = self::DOCUMENT_REGISTRY;
// 각 문서의 파일 존재 여부 확인
$docsBase = $this->getDocsBasePath();
foreach ($registry as &$group) {
foreach ($group['items'] as &$item) {
$item['exists'] = file_exists($docsBase.'/'.$item['path']);
}
}
return view('roadmap.documents.index', compact('registry'));
}
public function showDocument(string $slug): View
{
$document = null;
foreach (self::DOCUMENT_REGISTRY as $group) {
foreach ($group['items'] as $item) {
if ($item['slug'] === $slug) {
$document = $item;
$document['category_label'] = $group['category_label'];
$document['color'] = $group['color'];
break 2;
}
}
}
if (! $document) {
abort(404, '문서를 찾을 수 없습니다.');
}
$docsBase = $this->getDocsBasePath();
$filePath = $docsBase.'/'.$document['path'];
$content = null;
if (file_exists($filePath)) {
$markdown = file_get_contents($filePath);
$content = Str::markdown($markdown);
}
return view('roadmap.documents.show', compact('document', 'content'));
}
}