Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,36 @@ public class JobRoadmapController {

@GetMapping
@Operation(
summary = "직업 로드맵 다건 조회",
description = "직업 로드맵 목록을 페이징과 키워드 검색으로 조회합니다."
summary = "직업 로드맵 목록 조회",
description = """
### 개요
모든 직업의 로드맵 목록을 페이징과 키워드 검색으로 조회합니다.

### 쿼리 파라미터
- `page`: 페이지 번호 (0부터 시작, 기본값: 0)
- `size`: 페이지 크기 (기본값: 10)
- `keyword`: 검색 키워드 (선택, 직업명으로 검색)

### 반환 정보
각 직업 로드맵의 요약 정보:
- id: 직업 로드맵 ID
- jobName: 직업명
- jobDescription: 직업 설명

### 응답 형식
- content: 직업 로드맵 목록
- totalElements: 전체 개수
- totalPages: 전체 페이지 수
- number: 현재 페이지 번호
- size: 페이지 크기

### 응답 코드
- **200**: 조회 성공

### 참고
- 인증 불필요 (누구나 조회 가능)
- 키워드는 직업명(jobName)에 대해 부분 일치 검색
"""
)
public RsData<JobRoadmapPagingResponse> getJobRoadmaps(
@RequestParam(defaultValue = "0") int page,
Expand All @@ -37,7 +65,71 @@ public RsData<JobRoadmapPagingResponse> getJobRoadmaps(
@GetMapping("/{id}")
@Operation(
summary = "직업 로드맵 상세 조회",
description = "특정 직업 로드맵의 상세 정보(직업 정보 + 모든 노드)를 조회합니다."
description = """
### 개요
특정 직업의 통합 로드맵을 트리 구조로 조회합니다.
여러 멘토의 멘토 로드맵을 통합하여 생성된 로드맵입니다.

### 직업 로드맵이란?
- 동일 직업의 멘토들의 로드맵을 통합한 결과
- 비선형 구조 (트리형) - 다양한 학습 경로 표현
- 멘토들의 빈도, 순서 패턴, 연결 관계를 분석하여 생성
- 주기적으로 자동 업데이트 (멘토 로드맵 변경 시)

### 반환 정보

**로드맵 기본 정보:**
- id, jobId, jobName: 직업 정보
- totalNodeCount: 전체 노드 개수
- createdDate, modifiedDate: 생성/수정일

**노드 정보 (트리 구조):**

각 노드는 다음 정보를 포함합니다:

*기본 정보:*
- id, parentId, childIds: 트리 구조 정보
- taskId, taskName: Task 정보
- level: 트리 깊이 (0: 루트, 1: 1단계 자식...)
- stepOrder: 같은 부모 내 순서

*학습 정보 (여러 멘토의 정보 통합):*
- learningAdvice: 학습 조언 통합
- recommendedResources: 추천 자료 통합
- learningGoals: 학습 목표 통합
- difficulty: 평균 난이도 (1-5)
- importance: 평균 중요도 (1-5)
- estimatedHours: 평균 예상 학습 시간

*통계 정보:*
- weight: 노드 가중치 (빈도, 위치, 연결성 기반)
- mentorCount: 이 노드를 사용한 멘토 수
- totalMentorCount: 해당 직업의 전체 멘토 수
- mentorCoverageRatio: 커버리지 비율 (0.0 ~ 1.0)
- isEssential: 필수 노드 여부 (50% 이상)
- essentialLevel:
- "CORE": 80% 이상의 멘토가 선택 (핵심 필수)
- "COMMON": 50% 이상의 멘토가 선택 (일반 필수)
- "OPTIONAL": 50% 미만의 멘토가 선택 (선택)

*자식 노드:*
- children: 자식 노드 목록 (재귀 구조)

### 응답 구조
- 루트 노드들만 최상위에 반환
- 각 노드의 children 필드에 자식 노드들이 재귀적으로 포함
- 전체 트리 구조를 한 번에 조회 가능

### 응답 코드
- **200**: 조회 성공
- **404**: 직업 로드맵을 찾을 수 없음

### 참고
- 로그인한 사용자만 조회 가능
- 통합 알고리즘: 빈도수(40%), 멘토 커버리지(30%), 위치(20%), 연결성(10%)
- essentialLevel로 필수/선택 경로 구분 가능
- 통계 정보를 활용해 학습 우선순위 판단 가능
"""
)
public RsData<JobRoadmapResponse> getJobRoadmapById(@PathVariable Long id) {
JobRoadmapResponse roadmap = jobRoadmapService.getJobRoadmapById(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,51 @@ public class MentorRoadmapController {
@Operation(
summary = "멘토 로드맵 생성",
description = """
멘토가 자신의 커리어 로드맵을 생성합니다.
- 멘토는 하나의 로드맵만 생성 가능
- TaskId는 nullable (DB에 있는 Task 중 선택하게 하고, 원하는 Task 없는 경우 null 가능)
- TaskName은 필수 (표시용 이름. DB에 있는 Task 선택시 해당 taskName으로 저장, 없는 경우 입력한 이름으로 저장)
- stepOrder는 1부터 시작하는 연속된 숫자로, 로드맵 상 노드의 순서를 나타냄
- 노드들은 stepOrder 순으로 자동 정렬(멘토 로드맵은 선형으로만 구성)

사용 시나리오:
1. TaskController로 Task 검색
2. Task 선택 시 TaskId와 TaskName 획득
3. Task 없는 경우 TaskId는 null, TaskName 직접 입력
4. description(Task에 대한 멘토의 경험, 조언, 학습 방법 등) 입력
### 개요
멘토가 자신의 커리어 여정을 단계별로 기록한 로드맵을 생성합니다.

### 제약 사항
- **멘토당 1개의 로드맵만 생성 가능** (중복 시 409 에러)
- **최소 1개 이상의 노드** 필요
- **stepOrder는 1부터 시작하는 연속된 숫자** (예: 1,2,3,...)
- **멘토 권한(MENTOR)** 필요

### 노드(RoadmapNode) 구성

**필수 필드:**
- `taskName`: 기술/단계 이름 (최대 100자)
- `stepOrder`: 로드맵 상 순서 (1부터 시작, 중복 불가)

**선택 필드:**
- `taskId`: 표준 Task ID (nullable)
- Task 검색(`/tasks/search`)으로 선택 가능
- null이면 자동으로 pending alias 등록 (관리자 승인 대기)
- `learningAdvice`: 학습 조언/방법 (최대 2000자)
- `recommendedResources`: 추천 자료 (최대 2000자)
- `learningGoals`: 학습 목표 (최대 1000자)
- `difficulty`: 난이도 (1-5)
- `importance`: 중요도 (1-5)
- `hoursPerDay`: 하루 학습 시간
- `weeks`: 학습 주차
- `estimatedHours`: 자동 계산 (hoursPerDay × weeks × 7)

### 사용 시나리오
1. `/tasks/search`로 Task 검색
2. 원하는 Task 선택 → taskId, taskName 획득
3. 원하는 Task 없으면 → taskId는 null, taskName 직접 입력
4. 각 단계별 학습 정보 입력 (조언, 자료, 목표 등)
5. 로드맵 생성 요청

### 응답 코드
- **201**: 생성 성공
- **400**: 유효성 검증 실패 (stepOrder 불연속/중복 등)
- **403**: 멘토 권한 없음
- **404**: 존재하지 않는 taskId 참조
- **409**: 이미 로드맵 존재

### 참고
- 멘토 로드맵은 선형 구조 (순차적 학습 경로)
- 로드맵 생성 후 직업 로드맵 통합 이벤트 발행 (비동기)
"""
)
@PostMapping
Expand All @@ -58,12 +91,29 @@ public RsData<MentorRoadmapSaveResponse> create(@Valid @RequestBody MentorRoadma
@Operation(
summary = "멘토 로드맵 상세 조회 (로드맵 ID)",
description = """
로드맵 ID로 멘토 로드맵 상세 정보를 조회합니다.
로그인한 사용자만 조회할 수 있습니다.
### 개요
로드맵 ID로 멘토 로드맵의 전체 정보를 조회합니다.

반환 정보:
- 로드맵 기본 정보 (로드맵 ID, 멘토 ID, 제목, 설명, 생성일, 수정일 등)
### 권한
- 로그인한 모든 사용자 조회 가능

### 반환 정보
- 로드맵 기본 정보 (id, mentorId, title, description)
- 모든 노드 정보 (stepOrder 순으로 정렬)
- 생성일, 수정일

### 노드 정보 구조
각 노드는 다음 정보를 포함합니다:
- taskId, taskName: Task 정보
- learningAdvice, recommendedResources, learningGoals: 학습 가이드
- difficulty, importance: 난이도/중요도 (1-5)
- hoursPerDay, weeks, estimatedHours: 학습 시간 정보
- stepOrder: 로드맵 상 순서

### 응답 코드
- **200**: 조회 성공
- **401**: 인증 필요
- **404**: 로드맵을 찾을 수 없음
"""
)
@GetMapping("/{id}")
Expand All @@ -81,14 +131,25 @@ public RsData<MentorRoadmapResponse> getById(@PathVariable Long id) {
@Operation(
summary = "멘토 로드맵 상세 조회 (멘토 ID)",
description = """
멘토 ID로 해당 멘토의 로드맵 상세 정보를 조회합니다.
로그인한 사용자만 조회할 수 있습니다.
### 개요
멘토 ID로 해당 멘토의 로드맵 전체 정보를 조회합니다.

### 권한
- 로그인한 모든 사용자 조회 가능

반환 정보:
- 로드맵 기본 정보 (로드맵 ID, 멘토 ID, 제목, 설명, 생성일, 수정일 등)
### 반환 정보
- 로드맵 기본 정보 (id, mentorId, title, description)
- 모든 노드 정보 (stepOrder 순으로 정렬)
- 생성일, 수정일

주의: 멘토가 로드맵을 생성하지 않았다면 404 에러가 발생합니다.
### 응답 코드
- **200**: 조회 성공
- **401**: 인증 필요
- **404**: 해당 멘토의 로드맵을 찾을 수 없음

### 참고
- 멘토가 로드맵을 생성하지 않았으면 404 에러 발생
- 멘토 ID는 Member ID가 아닌 Mentor 엔티티의 ID입니다
"""
)
@GetMapping("/mentor/{mentorId}")
Expand All @@ -103,7 +164,36 @@ public RsData<MentorRoadmapResponse> getByMentorId(@PathVariable Long mentorId)
);
}

@Operation(summary = "멘토 로드맵 수정", description = "로드맵 ID로 로드맵을 찾아 수정합니다. 본인이 생성한 로드맵만 수정할 수 있습니다.")
@Operation(
summary = "멘토 로드맵 수정",
description = """
### 개요
로드맵 ID로 로드맵을 찾아 전체 내용을 수정합니다.

### 권한
- **본인이 생성한 로드맵만 수정 가능** (타인 수정 시 403 에러)
- 멘토 권한(MENTOR) 필요

### 수정 방식
- **전체 교체 방식**: 기존 노드를 모두 삭제하고 새 노드로 교체
- 로드맵 제목, 설명도 함께 수정
- 생성 API와 동일한 유효성 검증 적용

### 요청 형식
- 생성 API와 동일한 Request Body 사용
- 모든 노드를 다시 전송해야 함 (부분 수정 불가)

### 응답 코드
- **200**: 수정 성공
- **400**: 유효성 검증 실패
- **403**: 본인의 로드맵이 아님
- **404**: 로드맵을 찾을 수 없음

### 참고
- 수정 후 직업 로드맵 통합 이벤트 발행 (비동기)
- taskId가 null인 노드는 자동으로 pending alias 등록
"""
)
@PutMapping("/{id}")
@PreAuthorize("hasRole('MENTOR')")
public RsData<MentorRoadmapSaveResponse> update(@PathVariable Long id, @Valid @RequestBody MentorRoadmapSaveRequest request) {
Expand All @@ -118,7 +208,30 @@ public RsData<MentorRoadmapSaveResponse> update(@PathVariable Long id, @Valid @R
);
}

@Operation(summary = "멘토 로드맵 삭제", description = "로드맵 ID로 로드맵을 삭제합니다. 본인이 생성한 로드맵만 삭제할 수 있습니다.")
@Operation(
summary = "멘토 로드맵 삭제",
description = """
### 개요
로드맵 ID로 로드맵을 삭제합니다.

### 권한
- **본인이 생성한 로드맵만 삭제 가능** (타인 삭제 시 403 에러)
- 멘토 권한(MENTOR) 필요

### 삭제 범위
- 로드맵과 모든 노드가 함께 삭제됩니다 (Cascade)
- 참조하던 Task는 삭제되지 않습니다 (Task는 공유 자원)

### 응답 코드
- **200**: 삭제 성공
- **403**: 본인의 로드맵이 아님
- **404**: 로드맵을 찾을 수 없음

### 참고
- 삭제 후 직업 로드맵 통합 이벤트 발행 (비동기)
- 삭제된 로드맵은 복구할 수 없습니다
"""
)
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('MENTOR')")
public RsData<Void> delete(@PathVariable Long id) {
Expand Down
Loading