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
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,7 @@ tasks.withType(JavaCompile).configureEach {
tasks.named('test') {
useJUnitPlatform()
}

tasks.withType(JavaCompile).configureEach {
options.compilerArgs += ['-Xlint:deprecation', '-Xlint:unchecked']
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.teamEWSN.gitdeun.codereference.controller;

import com.teamEWSN.gitdeun.codereference.dto.CodeReferenceResponseDtos.*;
import com.teamEWSN.gitdeun.codereference.dto.*;
import com.teamEWSN.gitdeun.codereference.dto.CodeReferenceRequestDtos.*;
import com.teamEWSN.gitdeun.codereference.service.CodeReferenceService;
import com.teamEWSN.gitdeun.common.jwt.CustomUserDetails;
import jakarta.validation.Valid;
Expand All @@ -21,39 +21,40 @@ public class CodeReferenceController {
private final CodeReferenceService codeReferenceService;

// νŠΉμ • λ…Έλ“œμ— μ½”λ“œ μ°Έμ‘° 생성
@PostMapping("/nodes/{nodeId}/code-references")
@PostMapping("/nodes/{nodeKey}/code-references")
public ResponseEntity<ReferenceResponse> createCodeReference(
@PathVariable Long mapId,
@PathVariable String nodeId,
@Valid @RequestBody CodeReferenceRequestDtos.CreateRequest request,
@PathVariable String nodeKey,
@Valid @RequestBody CreateRequest request,
@AuthenticationPrincipal CustomUserDetails userDetails) {
return ResponseEntity.status(HttpStatus.CREATED).body(codeReferenceService.createReference(mapId, nodeId, userDetails.getId(), request));
return ResponseEntity.status(HttpStatus.CREATED).body(codeReferenceService.createReference(mapId, nodeKey, userDetails.getId(), request));
}

// νŠΉμ • μ½”λ“œ μ°Έμ‘° 상세 쑰회
@GetMapping("/code-references/{refId}")
public ResponseEntity<ReferenceResponse> getCodeReference(
@GetMapping("/code-references/{refId}/detail")
public ResponseEntity<ReferenceDetailResponse> getCodeReferenceDetail(
@PathVariable Long mapId,
@PathVariable Long refId,
@AuthenticationPrincipal CustomUserDetails userDetails) {
return ResponseEntity.ok(codeReferenceService.getReference(mapId, refId, userDetails.getId()));
@AuthenticationPrincipal CustomUserDetails userDetails,
@RequestHeader("Authorization") String authorizationHeader) {
return ResponseEntity.ok(codeReferenceService.getReferenceDetail(mapId, refId, userDetails.getId(), authorizationHeader));
}

// νŠΉμ • λ…Έλ“œμ— μ—°κ²°λœ λͺ¨λ“  μ½”λ“œ μ°Έμ‘° λͺ©λ‘ 쑰회
@GetMapping("/nodes/{nodeId}/code-references")
@GetMapping("/nodes/{nodeKey}/code-references")
public ResponseEntity<List<ReferenceResponse>> getNodeCodeReferences(
@PathVariable Long mapId,
@PathVariable String nodeId,
@PathVariable String nodeKey,
@AuthenticationPrincipal CustomUserDetails userDetails) {
return ResponseEntity.ok(codeReferenceService.getReferencesForNode(mapId, nodeId, userDetails.getId()));
return ResponseEntity.ok(codeReferenceService.getReferencesForNode(mapId, nodeKey, userDetails.getId()));
}

// μ½”λ“œ μ°Έμ‘° 정보 μˆ˜μ •
@PatchMapping("/code-references/{refId}")
public ResponseEntity<ReferenceResponse> updateCodeReference(
@PathVariable Long mapId,
@PathVariable Long refId,
@Valid @RequestBody CodeReferenceRequestDtos.CreateRequest request,
@Valid @RequestBody CreateRequest request,
@AuthenticationPrincipal CustomUserDetails userDetails) {
return ResponseEntity.ok(codeReferenceService.updateReference(mapId, refId, userDetails.getId(), request));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,20 @@ public class CodeReferenceResponseDtos {
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public static class ReferenceResponse {
private Long referenceId;
private String nodeId;
private String nodeKey;
private String filePath;
private Integer startLine;
private Integer endLine;
}

@Getter
@Builder
public static class ReferenceDetailResponse {
private Long referenceId;
private String nodeKey;
private String filePath;
private Integer startLine;
private Integer endLine;
private String codeContent; // μ‹€μ œ μ½”λ“œ λ‚΄μš©μ„ 담을 ν•„λ“œ
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public class CodeReference extends AuditedEntity {
@JoinColumn(name = "mindmap_id", nullable = false)
private Mindmap mindmap;

@Column(name = "node_id", nullable = false)
private String nodeId;
@Column(name = "node_key", nullable = false)
private String nodeKey;

@Column(name = "file_path", columnDefinition = "TEXT", nullable = false)
private String filePath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,32 @@
import com.teamEWSN.gitdeun.codereference.entity.CodeReference;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.ReportingPolicy;

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface CodeReferenceMapper {

/**
* κΈ°λ³Έ CodeReference 응닡 λ§€ν•‘
*/
@Mapping(target = "referenceId", source = "id")
ReferenceResponse toReferenceResponse(CodeReference codeReference);

@Mapping(target = "referenceId", source = "codeReference.id")
@Mapping(target = "nodeKey", source = "codeReference.nodeKey")
@Mapping(target = "filePath", source = "codeReference.filePath")
@Mapping(target = "startLine", source = "codeReference.startLine")
@Mapping(target = "endLine", source = "codeReference.endLine")
@Mapping(target = "codeContent", expression = "java(codeContent)")
ReferenceDetailResponse toReferenceDetailResponse(CodeReference codeReference, String codeContent);

/**
* 파일λͺ… μΆ”μΆœ 헬퍼 λ©”μ„œλ“œ
*/
@Named("extractFileName")
default String extractFileName(String filePath) {
if (filePath == null) return null;
return filePath.substring(filePath.lastIndexOf('/') + 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
@Repository
public interface CodeReferenceRepository extends JpaRepository<CodeReference, Long> {

List<CodeReference> findByMindmapIdAndNodeId(Long mindmapId, String nodeId);
List<CodeReference> findByMindmapIdAndNodeKey(Long mindmapId, String nodeKey);

Optional<CodeReference> findByMindmapIdAndId(Long mindmapId, Long id);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.teamEWSN.gitdeun.codereference.repository.CodeReferenceRepository;
import com.teamEWSN.gitdeun.common.exception.ErrorCode;
import com.teamEWSN.gitdeun.common.exception.GlobalException;
import com.teamEWSN.gitdeun.common.fastapi.FastApiClient;
import com.teamEWSN.gitdeun.mindmap.entity.Mindmap;
import com.teamEWSN.gitdeun.mindmap.repository.MindmapRepository;
import com.teamEWSN.gitdeun.mindmapmember.service.MindmapAuthService;
Expand All @@ -26,9 +27,10 @@ public class CodeReferenceService {
private final MindmapRepository mindmapRepository;
private final MindmapAuthService mindmapAuthService;
private final CodeReferenceMapper codeReferenceMapper;
private final FastApiClient fastApiClient;

@Transactional
public ReferenceResponse createReference(Long mapId, String nodeId, Long userId, CreateRequest request) {
public ReferenceResponse createReference(Long mapId, String nodeKey, Long userId, CreateRequest request) {
// 1. κΆŒν•œ 확인
if (!mindmapAuthService.hasEdit(mapId, userId)) {
throw new GlobalException(ErrorCode.FORBIDDEN_ACCESS);
Expand All @@ -41,7 +43,7 @@ public ReferenceResponse createReference(Long mapId, String nodeId, Long userId,
// 3. CodeReference μ—”ν‹°ν‹° 생성 및 μ €μž₯
CodeReference codeReference = CodeReference.builder()
.mindmap(mindmap)
.nodeId(nodeId)
.nodeKey(nodeKey)
.filePath(request.getFilePath())
.startLine(request.getStartLine())
.endLine(request.getEndLine())
Expand All @@ -54,29 +56,34 @@ public ReferenceResponse createReference(Long mapId, String nodeId, Long userId,
}

@Transactional(readOnly = true)
public ReferenceResponse getReference(Long mapId, Long refId, Long userId) {
// 1. κΆŒν•œ 확인
public ReferenceDetailResponse getReferenceDetail(Long mapId, Long refId, Long userId, String authorizationHeader) {
if (!mindmapAuthService.hasView(mapId, userId)) {
throw new GlobalException(ErrorCode.FORBIDDEN_ACCESS);
}

// 2. ν•΄λ‹Ή λ§ˆμΈλ“œλ§΅μ— μ†ν•œ μ½”λ“œ 참쑰인지 확인
CodeReference codeReference = codeReferenceRepository.findByMindmapIdAndId(mapId, refId)
.orElseThrow(() -> new GlobalException(ErrorCode.CODE_REFERENCE_NOT_FOUND));

// 3. DTO둜 λ³€ν™˜ν•˜μ—¬ λ°˜ν™˜
return codeReferenceMapper.toReferenceResponse(codeReference);
Mindmap mindmap = codeReference.getMindmap();

// FastAPIλ₯Ό 톡해 전체 파일 λ‚΄μš© κ°€μ Έμ˜€κΈ°
String fullContent = fastApiClient.getFileRaw(mindmap.getRepo().getGithubRepoUrl(), codeReference.getFilePath(), authorizationHeader);

// νŠΉμ • 라인만 μΆ”μΆœ (snippet)
String snippet = extractLines(fullContent, codeReference.getStartLine(), codeReference.getEndLine());

return codeReferenceMapper.toReferenceDetailResponse(codeReference, snippet);
}

@Transactional(readOnly = true)
public List<ReferenceResponse> getReferencesForNode(Long mapId, String nodeId, Long userId) {
public List<ReferenceResponse> getReferencesForNode(Long mapId, String nodeKey, Long userId) {
// 1. κΆŒν•œ 확인 (읽기)
if (!mindmapAuthService.hasView(mapId, userId)) {
throw new GlobalException(ErrorCode.FORBIDDEN_ACCESS);
}

// 2. νŠΉμ • λ…Έλ“œμ— μ†ν•œ λͺ¨λ“  μ½”λ“œ μ°Έμ‘° 쑰회
List<CodeReference> references = codeReferenceRepository.findByMindmapIdAndNodeId(mapId, nodeId);
List<CodeReference> references = codeReferenceRepository.findByMindmapIdAndNodeKey(mapId, nodeKey);

// 3. DTO 리슀트둜 λ³€ν™˜ν•˜μ—¬ λ°˜ν™˜
return references.stream()
Expand All @@ -86,7 +93,7 @@ public List<ReferenceResponse> getReferencesForNode(Long mapId, String nodeId, L

@Transactional
public ReferenceResponse updateReference(Long mapId, Long refId, Long userId, CreateRequest request) {
// 1. κΆŒν•œ 확인 (μ“°κΈ°)
// 1. κΆŒν•œ 확인
if (!mindmapAuthService.hasEdit(mapId, userId)) {
throw new GlobalException(ErrorCode.FORBIDDEN_ACCESS);
}
Expand Down Expand Up @@ -117,4 +124,16 @@ public void deleteReference(Long mapId, Long refId, Long userId) {
// 3. μ½”λ“œ μ°Έμ‘° μ‚­μ œ
codeReferenceRepository.deleteById(refId);
}

private String extractLines(String fullContent, Integer startLine, Integer endLine) {
if (fullContent == null) return "";
if (startLine == null || endLine == null || startLine <= 0 || endLine < startLine) return fullContent;

String[] lines = fullContent.split("\\r?\\n");
StringBuilder sb = new StringBuilder();
for (int i = startLine - 1; i < endLine && i < lines.length; i++) {
sb.append(lines[i]).append("\n");
}
return sb.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ public class CodeReviewController {
private final CodeReviewService codeReviewService;

// λ…Έλ“œμ— λŒ€ν•œ μ½”λ“œ 리뷰 생성
@PostMapping(value = "/mindmaps/{mapId}/nodes/{nodeId}/code-reviews", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@PostMapping(value = "/mindmaps/{mapId}/nodes/{nodeKey}/code-reviews", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<ReviewResponse> createNodeCodeReview(
@PathVariable Long mapId,
@PathVariable String nodeId,
@PathVariable String nodeKey,
@Valid @RequestPart("request") CreateRequest request,
@RequestPart(value = "files", required = false) List<MultipartFile> files,
@AuthenticationPrincipal CustomUserDetails userDetails) {
return ResponseEntity.status(HttpStatus.CREATED).body(codeReviewService.createNodeReview(mapId, nodeId, userDetails.getId(), request, files));
return ResponseEntity.status(HttpStatus.CREATED).body(codeReviewService.createNodeReview(mapId, nodeKey, userDetails.getId(), request, files));
}

// μ½”λ“œ 참쑰에 λŒ€ν•œ μ½”λ“œ 리뷰 생성
Expand Down Expand Up @@ -66,12 +66,12 @@ public ResponseEntity<Void> updateCodeReviewStatus(
}

// νŠΉμ • λ…Έλ“œμ— 달린 μ½”λ“œ 리뷰 λͺ©λ‘ 쑰회
@GetMapping("/mindmaps/{mapId}/nodes/{nodeId}/code-reviews")
@GetMapping("/mindmaps/{mapId}/nodes/{nodeKey}/code-reviews")
public ResponseEntity<Page<ReviewListResponse>> getNodeCodeReviews(
@PathVariable Long mapId,
@PathVariable String nodeId,
@PathVariable String nodeKey,
@AuthenticationPrincipal CustomUserDetails userDetails,
@PageableDefault(size = 10) Pageable pageable) {
return ResponseEntity.ok(codeReviewService.getReviewsForNode(mapId, nodeId, userDetails.getId(), pageable));
return ResponseEntity.ok(codeReviewService.getReviewsForNode(mapId, nodeKey, userDetails.getId(), pageable));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public class CodeReview extends AuditedEntity {
private Mindmap mindmap;

// 리뷰 λŒ€μƒ: λ…Έλ“œ ID λ˜λŠ” μ½”λ“œ μ°Έμ‘°
@Column(name = "node_id")
private String nodeId; // λ§ˆμΈλ“œλ§΅ κ·Έλž˜ν”„μ˜ λ…Έλ“œ ID
@Column(name = "node_key")
private String nodeKey; // λ§ˆμΈλ“œλ§΅ κ·Έλž˜ν”„μ˜ λ…Έλ“œ key

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "code_reference_id")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@

@Repository
public interface CodeReviewRepository extends JpaRepository<CodeReview, Long> {
Page<CodeReview> findByMindmapIdAndNodeId(Long mindmapId, String nodeId, Pageable pageable);
Page<CodeReview> findByMindmapIdAndNodeKey(Long mindmapId, String nodeKey, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class CodeReviewService {
private final CodeReviewMapper codeReviewMapper;

@Transactional
public ReviewResponse createNodeReview(Long mapId, String nodeId, Long userId, CreateRequest request, List<MultipartFile> files) {
public ReviewResponse createNodeReview(Long mapId, String nodeKey, Long userId, CreateRequest request, List<MultipartFile> files) {
// 리뷰 μž‘μ„± κΆŒν•œ 확인
if (!mindmapAuthService.hasEdit(mapId, userId)) {
throw new GlobalException(ErrorCode.FORBIDDEN_ACCESS);
Expand All @@ -53,7 +53,7 @@ public ReviewResponse createNodeReview(Long mapId, String nodeId, Long userId, C
CodeReview codeReview = CodeReview.builder()
.mindmap(mindmap)
.author(author)
.nodeId(nodeId)
.nodeKey(nodeKey)
.status(CodeReviewStatus.PENDING)
.build();
CodeReview savedReview = codeReviewRepository.save(codeReview);
Expand Down Expand Up @@ -125,12 +125,12 @@ public void updateReviewStatus(Long reviewId, Long userId, CodeReviewStatus stat
}

@Transactional(readOnly = true)
public Page<ReviewListResponse> getReviewsForNode(Long mapId, String nodeId, Long userId, Pageable pageable) {
public Page<ReviewListResponse> getReviewsForNode(Long mapId, String nodeKey, Long userId, Pageable pageable) {
if (!mindmapAuthService.hasView(mapId, userId)) {
throw new GlobalException(ErrorCode.FORBIDDEN_ACCESS);
}

Page<CodeReview> reviews = codeReviewRepository.findByMindmapIdAndNodeId(mapId, nodeId, pageable);
Page<CodeReview> reviews = codeReviewRepository.findByMindmapIdAndNodeKey(mapId, nodeKey, pageable);
return reviews.map(codeReviewMapper::toReviewListResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;
import org.hibernate.annotations.Where;

import java.time.LocalDateTime;
Expand All @@ -16,7 +17,7 @@
@Getter
@Builder
@SQLDelete(sql = "UPDATE comment SET deleted_at = CURRENT_TIMESTAMP WHERE id = ?")
@Where(clause = "deleted_at IS NULL")
@SQLRestriction("deleted_at IS NULL")
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "comment")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public enum ErrorCode {
// λ§ˆμΈλ“œλ§΅ κ΄€λ ¨
MINDMAP_NOT_FOUND(HttpStatus.NOT_FOUND, "MINDMAP-001", "μš”μ²­ν•œ λ§ˆμΈλ“œλ§΅μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€."),
MINDMAP_CREATION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "MINDMAP-002", "λ§ˆμΈλ“œλ§΅ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€."),
NODE_NOT_FOUND(HttpStatus.NOT_FOUND, "MINDMAP-003", "μš”μ²­ν•œ λ§ˆμΈλ“œλ§΅ λ…Έλ“œ 정보λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€."),

// λ§ˆμΈλ“œλ§΅ μš”μ²­ 검증 κ΄€λ ¨
INVALID_USER_ID(HttpStatus.BAD_REQUEST, "VALIDATE-001", "μœ νš¨ν•˜μ§€ μ•Šμ€ μ‚¬μš©μž IDμž…λ‹ˆλ‹€."),
Expand Down
Loading