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 @@ -4,14 +4,14 @@
import com.back.domain.member.mentor.repository.MentorRepository;
import com.back.domain.roadmap.roadmap.dto.request.MentorRoadmapSaveRequest;
import com.back.domain.roadmap.roadmap.dto.request.RoadmapNodeRequest;
import com.back.domain.roadmap.roadmap.dto.response.MentorRoadmapSaveResponse;
import com.back.domain.roadmap.roadmap.dto.response.MentorRoadmapResponse;
import com.back.domain.roadmap.roadmap.dto.response.MentorRoadmapSaveResponse;
import com.back.domain.roadmap.roadmap.entity.MentorRoadmap;
import com.back.domain.roadmap.roadmap.entity.RoadmapNode;
import com.back.domain.roadmap.roadmap.repository.MentorRoadmapRepository;
import com.back.domain.roadmap.roadmap.repository.RoadmapNodeRepository;
import com.back.domain.roadmap.task.entity.Task;
import com.back.domain.roadmap.task.repository.TaskRepository;
import com.back.domain.roadmap.task.service.TaskService;
import com.back.global.exception.ServiceException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -21,17 +21,15 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class MentorRoadmapService {
private final MentorRoadmapRepository mentorRoadmapRepository;
private final RoadmapNodeRepository roadmapNodeRepository;
private final TaskRepository taskRepository;
private final MentorRepository mentorRepository;
private final TaskService taskService;

// 멘토 로드맵 생성
@Transactional
Expand All @@ -48,6 +46,9 @@ public MentorRoadmapSaveResponse create(Long mentorId, MentorRoadmapSaveRequest
// 공통 검증(노드 개수, stepOrder 연속성)
validateRequest(request);

// taskId가 null인 자유입력 Task를 자동으로 pending alias로 등록
registerPendingAliasesForFreeInputTasks(request.nodes());

// MentorRoadmap 생성 및 저장 (로드맵 ID 확보)
MentorRoadmap mentorRoadmap = new MentorRoadmap(mentor, request.title(), request.description());
mentorRoadmap = mentorRoadmapRepository.save(mentorRoadmap);
Expand Down Expand Up @@ -106,6 +107,9 @@ public MentorRoadmapSaveResponse update(Long id, Long mentorId, MentorRoadmapSav
// 공통 검증
validateRequest(request);

// taskId가 null인 자유입력 Task를 자동으로 pending alias로 등록
registerPendingAliasesForFreeInputTasks(request.nodes());

// 로드맵 기본 정보 수정
mentorRoadmap.updateTitle(request.title());
mentorRoadmap.updateDescription(request.description());
Expand Down Expand Up @@ -159,6 +163,16 @@ public void delete(Long roadmapId, Long mentorId) {
log.info("멘토 로드맵 삭제 완료 - 멘토 ID: {}, 로드맵 ID: {}", mentorId, roadmapId);
}

// taskId가 null인 자유입력 Task를 자동으로 pending alias로 등록
private void registerPendingAliasesForFreeInputTasks(List<RoadmapNodeRequest> nodes) {
for (RoadmapNodeRequest node : nodes) {
if (node.taskId() == null && node.taskName() != null) {
// TaskService를 통해 pending alias 자동 등록 (이미 존재하면 무시)
taskService.createPendingAliasIfNotExists(node.taskName());
}
}
}

// 로드맵 요청 공통 유효성 검증
private void validateRequest(MentorRoadmapSaveRequest request) {
if (request.nodes().isEmpty()) {
Expand Down Expand Up @@ -218,26 +232,8 @@ private Map<Long, Task> getValidatedTasksMap(List<RoadmapNodeRequest> nodeReques
.distinct()
.toList();

if (taskIds.isEmpty()) {
return Map.of(); // 빈 맵 반환
}

// 일괄 조회로 존재하는 Task들 확인
List<Task> existingTasks = taskRepository.findAllById(taskIds);
Map<Long, Task> existingTaskMap = existingTasks.stream()
.collect(Collectors.toMap(Task::getId, Function.identity()));

// 존재하지 않는 TaskId 확인
List<Long> missingTaskIds = taskIds.stream()
.filter(taskId -> !existingTaskMap.containsKey(taskId))
.toList();

if (!missingTaskIds.isEmpty()) {
throw new ServiceException("404",
String.format("존재하지 않는 Task ID: %s", missingTaskIds));
}

return existingTaskMap;
// TaskService에 위임하여 검증 및 조회
return taskService.validateAndGetTasks(taskIds);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
Expand Down Expand Up @@ -44,6 +45,10 @@ public List<Task> searchByKeyword(String keyword){
return taskRepository.findTasksByKeyword(keyword);
}

/**
* 사용자가 명시적으로 새 Task를 제안할 때 사용
* 이미 존재하는 경우 예외를 던져 사용자에게 알림
*/
@Transactional
public TaskAlias createPendingAlias(String taskName){
// Task나 TaskAlias에 이미 존재하는 이름인지 검증
Expand All @@ -54,6 +59,57 @@ public TaskAlias createPendingAlias(String taskName){
return taskAliasRepository.save(pendingAlias);
}

/**
* 로드맵 생성 시 자동으로 pending alias를 등록할 때 사용
* 이미 존재하는 경우 무시 (다른 사용자가 이미 제안했을 수 있음)
*/
@Transactional
public void createPendingAliasIfNotExists(String taskName) {
// TaskAlias에 이미 존재하는지 확인
Optional<TaskAlias> existingAlias = taskAliasRepository.findByNameIgnoreCase(taskName);
if (existingAlias.isPresent()) {
return; // 이미 존재하면 무시
}

// Task에 이미 존재하는지 확인
Optional<Task> existingTask = taskRepository.findByNameIgnoreCase(taskName);
if (existingTask.isPresent()) {
return; // 이미 존재하면 무시
}

// 존재하지 않으면 새로운 pending alias 생성
TaskAlias pendingAlias = new TaskAlias(taskName);
taskAliasRepository.save(pendingAlias);
}

/**
* Task ID 목록을 검증하고 Map으로 반환 (로드맵 생성 시 사용)
* 존재하지 않는 Task ID가 있으면 예외 발생
*/
@Transactional(readOnly = true)
public Map<Long, Task> validateAndGetTasks(List<Long> taskIds) {
if (taskIds == null || taskIds.isEmpty()) {
return Map.of(); // 빈 맵 반환
}

// 일괄 조회로 존재하는 Task들 확인
List<Task> existingTasks = taskRepository.findAllById(taskIds);
Map<Long, Task> existingTaskMap = existingTasks.stream()
.collect(Collectors.toMap(Task::getId, task -> task));

// 존재하지 않는 TaskId 확인
List<Long> missingTaskIds = taskIds.stream()
.filter(taskId -> !existingTaskMap.containsKey(taskId))
.toList();

if (!missingTaskIds.isEmpty()) {
throw new ServiceException("404",
String.format("존재하지 않는 Task ID: %s", missingTaskIds));
}

return existingTaskMap;
}

// === 관리자용 기능들 ===
// Pending alias 목록 조회 (페이징)
@Transactional(readOnly = true)
Expand Down