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 @@ -6,14 +6,18 @@
*/
package com.back.domain.node.controller;

import com.back.domain.node.dto.*;
import com.back.domain.node.dto.PivotListDto;
import com.back.domain.node.dto.TreeDto;
import com.back.domain.node.dto.base.BaseLineBulkCreateRequest;
import com.back.domain.node.dto.base.BaseLineBulkCreateResponse;
import com.back.domain.node.dto.base.BaseLineDto;
import com.back.domain.node.dto.base.BaseNodeDto;
import com.back.domain.node.service.NodeService;
import com.back.global.security.CustomUserDetails;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.List;
Expand Down Expand Up @@ -55,4 +59,13 @@ public ResponseEntity<TreeDto> getTreeForBaseLine(@PathVariable Long baseLineId)
TreeDto tree = nodeService.getTreeForBaseLine(baseLineId);
return ResponseEntity.ok(tree);
}

// 내가 만든 베이스라인 목록 조회
@GetMapping("/mine")
public ResponseEntity<List<BaseLineDto>> getMyBaseLines(
@AuthenticationPrincipal CustomUserDetails me
) {
List<BaseLineDto> list = nodeService.getMyBaseLines(me.getId());
return ResponseEntity.ok(list);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.back.domain.node.dto.base;

/**
* BaseLine 요약 DTO
* - 프론트 첫 리스트용
*/

public record BaseLineDto(Long id, String title) {}
17 changes: 14 additions & 3 deletions back/src/main/java/com/back/domain/node/mapper/NodeMappers.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
package com.back.domain.node.mapper;

import com.back.domain.node.dto.base.BaseLineBulkCreateRequest;
import com.back.domain.node.dto.base.BaseLineDto;
import com.back.domain.node.dto.base.BaseNodeCreateRequestDto;
import com.back.domain.node.dto.base.BaseNodeDto;
import com.back.domain.node.dto.decision.DecNodeDto;
import com.back.domain.node.dto.decision.DecisionNodeCreateRequestDto;
import com.back.global.mapper.Mapper;
import com.back.global.mapper.TwoWayMapper;
import com.back.global.mapper.MappingException;
import com.back.domain.node.entity.*;
import com.back.domain.user.entity.User;
import com.back.global.mapper.Mapper;
import com.back.global.mapper.MappingException;
import com.back.global.mapper.TwoWayMapper;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -24,6 +25,16 @@ public final class NodeMappers {

private NodeMappers() {}

// BaseLine -> BaseLineDto
public static final Mapper<BaseLine, BaseLineDto> BASELINE_READ = e -> {
if (e == null) throw new MappingException("BaseLine is null");
return new BaseLineDto(
e.getId(),
e.getTitle()
);
};


// BaseNode -> BaseLineDto
public static final Mapper<BaseNode, BaseNodeDto> BASE_READ = e -> {
if (e == null) throw new MappingException("BaseNode is null");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

/**
Expand All @@ -17,5 +18,8 @@ public interface BaseLineRepository extends JpaRepository<BaseLine, Long> {
// Guest 베이스라인 1개 제한 확인용
boolean existsByUser_id(Long userId);


List<BaseLine> findByUser_IdOrderByIdDesc(Long userId);

boolean existsByUserAndTitle(User user, String title);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
*/
package com.back.domain.node.service;

import com.back.domain.node.dto.*;
import com.back.domain.node.dto.TreeDto;
import com.back.domain.node.dto.base.BaseLineDto;
import com.back.domain.node.dto.base.BaseNodeDto;
import com.back.domain.node.dto.decision.DecNodeDto;
import com.back.domain.node.dto.decision.DecisionLineDetailDto;
Expand All @@ -18,6 +19,7 @@
import com.back.domain.node.entity.DecisionLine;
import com.back.domain.node.entity.DecisionNode;
import com.back.domain.node.mapper.NodeMappers;
import com.back.domain.node.repository.BaseLineRepository;
import com.back.domain.node.repository.BaseNodeRepository;
import com.back.domain.node.repository.DecisionLineRepository;
import com.back.domain.node.repository.DecisionNodeRepository;
Expand All @@ -42,6 +44,7 @@ public class NodeQueryService {
private final BaseNodeRepository baseNodeRepository;
private final DecisionNodeRepository decisionNodeRepository;
private final DecisionLineRepository decisionLineRepository;
private final BaseLineRepository baseLineRepository;
private final NodeDomainSupport support;

// 가장 중요한: 특정 BaseLine 전체 트리 조회
Expand Down Expand Up @@ -127,4 +130,11 @@ public DecisionLineDetailDto getDecisionLineDetail(Long decisionLineId) {
nodeDtos
);
}

public List<BaseLineDto> getMyBaseLines(Long userId) {
return baseLineRepository.findByUser_IdOrderByIdDesc(userId)
.stream()
.map(NodeMappers.BASELINE_READ::map)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.back.domain.node.dto.*;
import com.back.domain.node.dto.base.BaseLineBulkCreateRequest;
import com.back.domain.node.dto.base.BaseLineBulkCreateResponse;
import com.back.domain.node.dto.base.BaseLineDto;
import com.back.domain.node.dto.base.BaseNodeDto;
import com.back.domain.node.dto.decision.*;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -74,4 +75,8 @@ public BaseNodeDto getBaseNode(Long baseNodeId) {
public DecNodeDto forkFromDecision(ForkFromDecisionRequest request) {
return decisionFlowService.forkFromDecision(request);
}

public List<BaseLineDto> getMyBaseLines(Long id) {
return nodeQueryService.getMyBaseLines(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@
import com.back.domain.node.repository.BaseNodeRepository;
import com.back.domain.user.entity.*;
import com.back.domain.user.repository.UserRepository;
import com.back.global.security.CustomUserDetails;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MockMvc;
Expand Down Expand Up @@ -481,6 +485,70 @@ private Long saveAndGetBaseLineId() throws Exception {
}
}

@Nested
@DisplayName("내 베이스라인 목록(/mine)")
class BaseLine_Mine {

@AfterEach
void clearCtx() { SecurityContextHolder.clearContext(); }

@Test
@DisplayName("성공 : /base-lines/mine — 최소 1개 라인 생성 후 목록에 id/title이 포함되어 반환된다")
void success_mine_returnsList() throws Exception {
var created = mockMvc.perform(post("/api/v1/base-lines/bulk")
.contentType(MediaType.APPLICATION_JSON)
.content(sampleLineJson(userId)))
.andExpect(status().isCreated())
.andReturn();

// ★ 인증 세팅
var me = userRepository.findById(userId).orElseThrow();
setAuth(new CustomUserDetails(me));

// when/then
mockMvc.perform(get("/api/v1/base-lines/mine"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].id").exists())
.andExpect(jsonPath("$[0].title").exists());
}

@Test
@DisplayName("성공 : /base-lines/mine — 라인이 없는 사용자에겐 빈 배열([])을 반환한다")
void success_mine_emptyForUserWithoutLines() throws Exception {
// given: 라인 없는 사용자
String uid = UUID.randomUUID().toString().substring(0, 8);
User emptyUser = User.builder()
.email("nouser_" + uid + "@test.local")
.role(Role.USER)
.birthdayAt(LocalDateTime.now().minusYears(20))
.gender(Gender.F)
.mbti(Mbti.INFP)
.beliefs("NONE")
.authProvider(AuthProvider.LOCAL)
.nickname("nouser-" + uid)
.username("nouser-" + uid)
.build();
userRepository.save(emptyUser);

// ★ 인증 세팅
setAuth(new CustomUserDetails(emptyUser));

// when/then
mockMvc.perform(get("/api/v1/base-lines/mine"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value(0));
}
}


// 가장 중요한 함수 한줄 요약: SecurityContextHolder에 인증 토큰 세팅
private void setAuth(CustomUserDetails cud) {
SecurityContext ctx = SecurityContextHolder.createEmptyContext();
ctx.setAuthentication(new UsernamePasswordAuthenticationToken(cud, null, cud.getAuthorities()));
SecurityContextHolder.setContext(ctx);
}



// (자주 쓰는) 정상 입력 샘플 JSON 생성
private String sampleLineJson(Long uid) {
Expand All @@ -496,4 +564,7 @@ private String sampleLineJson(Long uid) {
""".formatted(uid,
NodeCategory.EDUCATION, NodeCategory.CAREER, NodeCategory.CAREER, NodeCategory.ETC);
}



}