Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

import com.whylog.server.domain.decision.dto.ApplicationResponse;
import com.whylog.server.domain.decision.dto.DecisionRequest;
import com.whylog.server.domain.decision.exception.DecisionErrorCode;
import com.whylog.server.domain.decision.service.ApplicationQueryService;
import com.whylog.server.global.apiPayload.ApiResponse;
import com.whylog.server.global.apiPayload.annotation.ApiErrorCodeExample;
import com.whylog.server.global.apiPayload.annotation.ApiErrorCodeExamples;
import com.whylog.server.global.apiPayload.code.status.ErrorStatus;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
Expand All @@ -22,12 +27,18 @@
@Tag(name = "Application", description = "적용사항 관련 API")
public class ApplicationController {

private final ApplicationQueryService applicationQueryService;


@GetMapping("/{applicationId}")
@Operation(summary = "적용사항 상세 조회 API", description = "특정 적용사항의 상세 정보를 조회하는 API입니다.")
@Operation(summary = "적용사항 상세 조회 API", description = "특정 적용사항의 상세 정보를 조회하는 API입니다. 적용사항 상세 조회 화면에서 적용사항 제목, 타임라인, 결정원문 맥락, 결정근거를 조회합니다.")
@ApiErrorCodeExamples({
@ApiErrorCodeExample(value = ErrorStatus.class, name = "_BAD_REQUEST"),
@ApiErrorCodeExample(value = DecisionErrorCode.class, name = "APPLICATION_NOT_FOUND")
})
public ApiResponse<ApplicationResponse.ApplicationDetailDTO> getApplication(
@PathVariable Long applicationId) {
return ApiResponse.onSuccess(null);
return ApiResponse.onSuccess(applicationQueryService.getApplicationDetail(applicationId));
}

@GetMapping("/{applicationId}/recommended-commits")
Expand Down Expand Up @@ -60,4 +71,5 @@ public ApiResponse<ApplicationResponse.RecommendedCommitDTO> saveRecommendation(
return ApiResponse.onSuccess(null);
}

// TODO: 적용현황 조회 api 추가
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@ public static class ApplicationDetailDTO {
@Schema(description = "결정 타임라인 목록")
private List<DecisionTimelineItemDTO> decisionTimelines;

@Schema(description = "결정근거 목록")
private List<DecisionReasonItemDTO> decisionReasons;
@Schema(description = "결정 원문 맥락 목록")
private List<DecisionContextItemDTO> decisionContexts;

@Schema(description = "적용현황 (커밋 정보) 목록")
private List<ApplicationBaseItemDTO> applicationBases;
@Schema(description = "결정근거 개수", example = "3")
private Integer decisionReasonCount;

@Schema(description = "결정근거 목록")
private List<DecisionReasonItemDTO> decisionReasons;
}

@Getter
Expand All @@ -60,12 +62,35 @@ public static class DecisionTimelineItemDTO {
@Schema(description = "타임라인 시간", example = "2026-03-24T12:28:00")
private String time;

@Schema(description = "타임라인 단계", example = "이슈제기")
private String step;

@Schema(description = "타임라인 내용", example = "장애 이슈 제기")
private String content;

}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "결정 원문 맥락 항목")
public static class DecisionContextItemDTO {

@Schema(description = "타임라인 시간", example = "2026-03-24T12:28:00")
private String time;

@Schema(description = "발화자 ID", example = "1", nullable = true)
private Long memberId;

@Schema(description = "발화자 이름", example = "김주뇽", nullable = true)
private String memberName;

@Schema(description = "발화자 프로필 사진", example = "https://example.com/profile.jpg", nullable = true)
private String profileImage;

@Schema(description = "대화 내용", example = "아니 우리 이거 버그난다니까?!?@??@")
private String dialogueContent;

}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,6 @@ public static class ReliabilityDTO {

@Schema(description = "신뢰도 점수", example = "85")
private Integer score;

@Schema(description = "근거발언 개수", example = "42")
private Integer reasonSpeechCount;

@Schema(description = "참여자 합의도", example = "HIGH")
private String participantConsensus;

@Schema(description = "결정 구현 일치율", example = "92")
private Integer matchRatio;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,12 @@ public class ApplicationBase extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "decision_base_pk", nullable = false)
private DecisionBase decisionBase;

public static ApplicationBase create(Application application, DecisionBase decisionBase) {
ApplicationBase applicationBase = new ApplicationBase();
applicationBase.id = new ApplicationBaseId(application.getId(), decisionBase.getId());
applicationBase.application = application;
applicationBase.decisionBase = decisionBase;
return applicationBase;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
Expand All @@ -11,6 +12,7 @@
@Getter
@Embeddable
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ApplicationBaseId implements Serializable {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,12 @@ public class ApplicationTimeline extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "decision_timeline_pk", nullable = false)
private DecisionTimeline decisionTimeline;

public static ApplicationTimeline create(Application application, DecisionTimeline decisionTimeline) {
ApplicationTimeline applicationTimeline = new ApplicationTimeline();
applicationTimeline.id = new ApplicationTimelineId(application.getId(), decisionTimeline.getId());
applicationTimeline.application = application;
applicationTimeline.decisionTimeline = decisionTimeline;
return applicationTimeline;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
Expand All @@ -11,6 +12,7 @@
@Getter
@Embeddable
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ApplicationTimelineId implements Serializable {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import lombok.Getter;
import lombok.NoArgsConstructor;

// 적용현황
// 결정근거
@Entity
@Getter
@Table(name = "Decision_Base")
Expand All @@ -29,4 +29,14 @@ public class DecisionBase extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "decision_id", nullable = false)
private Decision decision;

@Column(name = "content", columnDefinition = "TEXT")
private String content;

public static DecisionBase create(Decision decision, String content) {
DecisionBase decisionBase = new DecisionBase();
decisionBase.decision = decision;
decisionBase.content = content;
return decisionBase;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,39 @@ public class DecisionTimeline extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "decision_id", nullable = false)
private Decision decision;

@Column(name = "timestamp", length = 30)
private String timestamp;

// 타임라인 단계
@Column(name = "step", length = 10)
private String step;

// 타임라인 내용 요약
@Column(name = "content", columnDefinition = "TEXT")
private String content;

// 발화자
@Column(name = "member_id")
private Long memberId;

// 발화 원문
@Column(name = "utterance", columnDefinition = "TEXT")
private String utterance;

public static DecisionTimeline create(Decision decision,
String timestamp,
String step,
String content,
Long memberId,
String utterance) {
DecisionTimeline decisionTimeline = new DecisionTimeline();
decisionTimeline.decision = decision;
decisionTimeline.timestamp = timestamp;
decisionTimeline.step = step;
decisionTimeline.content = content;
decisionTimeline.memberId = memberId;
decisionTimeline.utterance = utterance;
return decisionTimeline;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.whylog.server.domain.decision.exception;

import com.whylog.server.global.apiPayload.exception.GeneralException;

public class ApplicationNotFoundException extends GeneralException {

public ApplicationNotFoundException() {
super(DecisionErrorCode.APPLICATION_NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.whylog.server.domain.decision.exception;

import com.whylog.server.global.apiPayload.code.BaseErrorCode;
import com.whylog.server.global.apiPayload.code.ErrorReasonDTO;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor
public enum DecisionErrorCode implements BaseErrorCode {

DECISION_NOT_FOUND(HttpStatus.NOT_FOUND, "DECISION_404", "존재하지 않는 결정사항입니다."),
APPLICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "APPLICATION_404", "존재하지 않는 적용사항입니다."),
;

private final HttpStatus httpStatus;
private final String code;
private final String message;

@Override
public ErrorReasonDTO getReason() {
return ErrorReasonDTO.builder()
.isSuccess(false)
.code(code)
.message(message)
.build();
}

@Override
public ErrorReasonDTO getReasonHttpStatus() {
return ErrorReasonDTO.builder()
.httpStatus(httpStatus)
.isSuccess(false)
.code(code)
.message(message)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.whylog.server.domain.decision.exception;

import com.whylog.server.global.apiPayload.exception.GeneralException;

public class DecisionNotFoundException extends GeneralException {

public DecisionNotFoundException() {
super(DecisionErrorCode.DECISION_NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.whylog.server.domain.decision.repository;

import com.whylog.server.domain.decision.entity.ApplicationBase;
import com.whylog.server.domain.decision.entity.ApplicationBaseId;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface ApplicationBaseRepository extends JpaRepository<ApplicationBase, ApplicationBaseId> {

// 적용사항에 연결된 결정근거 목록 조회
@Query("""
SELECT ab
FROM ApplicationBase ab
JOIN FETCH ab.decisionBase db
WHERE ab.application.id = :applicationId
ORDER BY db.id ASC
""")
List<ApplicationBase> findByApplicationId(@Param("applicationId") Long applicationId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.whylog.server.domain.decision.repository;

import com.whylog.server.domain.decision.entity.ApplicationTimeline;
import com.whylog.server.domain.decision.entity.ApplicationTimelineId;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface ApplicationTimelineRepository extends JpaRepository<ApplicationTimeline, ApplicationTimelineId> {

// 적용사항에 연결된 결정 타임라인 목록을 조회한다.
@Query("""
SELECT at
FROM ApplicationTimeline at
JOIN FETCH at.decisionTimeline dt
WHERE at.application.id = :applicationId
ORDER BY dt.timestamp ASC, dt.id ASC
""")
List<ApplicationTimeline> findByApplicationId(@Param("applicationId") Long applicationId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.whylog.server.domain.decision.repository;

import com.whylog.server.domain.decision.entity.DecisionBase;
import org.springframework.data.jpa.repository.JpaRepository;

public interface DecisionBaseRepository extends JpaRepository<DecisionBase, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.whylog.server.domain.decision.repository;

import com.whylog.server.domain.decision.entity.DecisionTimeline;
import org.springframework.data.jpa.repository.JpaRepository;

public interface DecisionTimelineRepository extends JpaRepository<DecisionTimeline, Long> {
}
Loading
Loading