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 @@ -119,17 +119,4 @@ public ApiResponse<ApplicationResponse.CommitConnectionResponseDTO> disconnectCo
return ApiResponse.onSuccess(applicationCommandService.disconnectCommit(applicationId, request));
}

@PostMapping("/{decisionId}/recommendations")
@Operation(summary = "추천 결과 저장 API", description = "적용사항의 추천 결과를 저장하는 API입니다.")
@ApiErrorCodeExamples({
@ApiErrorCodeExample(value = ErrorStatus.class, name = "_BAD_REQUEST"),
@ApiErrorCodeExample(value = DecisionErrorCode.class, name = "DECISION_NOT_FOUND"),
@ApiErrorCodeExample(value = GitErrorCode.class, name = "COMMIT_NOT_FOUND")
})
public ApiResponse<ApplicationResponse.RecommendedCommitDTO> saveRecommendation(
@PathVariable Long decisionId,
@Valid @RequestBody DecisionRequest.RecommendationDTO request) {
return ApiResponse.onSuccess(null);
}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.whylog.server.domain.decision.controller;

import com.fasterxml.jackson.databind.JsonNode;
import com.whylog.server.domain.decision.dto.DecisionResponse;
import com.whylog.server.domain.decision.exception.DecisionErrorCode;
import com.whylog.server.domain.decision.service.DecisionCommitMatchService;
import com.whylog.server.domain.decision.service.DecisionQueryService;
import com.whylog.server.domain.git.exception.GitErrorCode;
import com.whylog.server.global.apiPayload.ApiResponse;
import com.whylog.server.global.apiPayload.annotation.ApiErrorCodeExample;
import com.whylog.server.global.apiPayload.annotation.ApiErrorCodeExamples;
Expand All @@ -12,6 +15,8 @@
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -22,6 +27,7 @@
public class DecisionController {

private final DecisionQueryService decisionQueryService;
private final DecisionCommitMatchService decisionCommitMatchService;

@GetMapping("/{decisionId}/reliability")
@Operation(summary = "신뢰도 조회 API", description = "특정 결정사항의 신뢰도 정보를 조회하는 API입니다.")
Expand All @@ -33,4 +39,46 @@ public ApiResponse<DecisionResponse.ReliabilityDTO> getReliability(
@PathVariable Long decisionId) {
return ApiResponse.onSuccess(decisionQueryService.getReliability(decisionId));
}

@PostMapping("/{decisionId}/commit/match")
@Operation(
summary = "결정사항 적용사항-커밋 추천 매칭",
description = """
결정사항에 속한 적용사항별 커밋 추천 후보를 FastAPI에 요청하고 결과를 저장합니다.
회의 분석이 끝난 후 자동으로 1회 실행되며,
결정사항 페이지 추천 커밋 목록에 있는 새로고침시 해당 API를 호출하면 실행됩니다.

결정사항이 포함된 회의의 팀 레포지토리 ID 목록을 생성해 전달합니다.
추천 개수는 적용사항별 최대 5개로 고정합니다.
이미 Spring 서버에서 적용사항에 연결된 커밋은 추천 응답과 저장 대상에서 제외합니다.
""")
@ApiErrorCodeExamples({
@ApiErrorCodeExample(value = ErrorStatus.class, name = "_BAD_REQUEST"),
@ApiErrorCodeExample(value = DecisionErrorCode.class, name = "DECISION_NOT_FOUND"),
@ApiErrorCodeExample(value = GitErrorCode.class, name = "REPOSITORY_NOT_FOUND")
})
public ApiResponse<JsonNode> matchApplicationCommits(
@PathVariable Long decisionId) {
return ApiResponse.onSuccess(decisionCommitMatchService.matchApplicationCommits(decisionId));
}

@PostMapping("/{decisionId}/commit/match/test")
@Operation(
summary = "결정사항 적용사항-커밋 추천 매칭 저장(테스트용)",
description = """
FastAPI 호출 없이 전달받은 FastAPI 응답 JSON의 result를 추천 결과로 저장합니다.

테스트용 API입니다. `result`를 포함한 전체 응답을 보내도 되고, result 객체만 보내도 됩니다.
이미 Spring 서버에서 적용사항에 연결된 커밋은 저장 대상에서 제외합니다.
""")
@ApiErrorCodeExamples({
@ApiErrorCodeExample(value = ErrorStatus.class, name = "_BAD_REQUEST"),
@ApiErrorCodeExample(value = DecisionErrorCode.class, name = "DECISION_NOT_FOUND"),
@ApiErrorCodeExample(value = GitErrorCode.class, name = "REPOSITORY_NOT_FOUND")
})
public ApiResponse<JsonNode> saveTestApplicationCommitMatches(
@PathVariable Long decisionId,
@RequestBody JsonNode fastApiResponse) {
return ApiResponse.onSuccess(decisionCommitMatchService.saveTestApplicationCommitMatches(decisionId, fastApiResponse));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ public static class RecommendedCommitDTO {

@Schema(description = "추천 사유", example = "이 커밋은 관련된 이슈를 해결하는 커밋입니다.")
private String reason;

@Schema(description = "추천 신뢰도", example = "94")
private Integer confidence;
}

@Getter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,4 @@ public static class CommitDisconnectionDTO {
private Long commitId;
}

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@Schema(description = "추천 결과 저장 요청")
public static class RecommendationDTO {

@Schema(description = "추천 커밋 ID", example = "1")
@NotNull
private Long commitId;

@Schema(description = "추천 이유", example = "이 커밋은 관련된 이슈를 해결하는 커밋입니다.")
private String reason;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,23 @@ public class ApplicationCommits extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "decision_commits_pk", nullable = false)
private DecisionCommits decisionCommits;

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

@Column(name = "confidence")
private Integer confidence;

public static ApplicationCommits create(Application application,
DecisionCommits decisionCommits,
String reason,
Integer confidence) {
ApplicationCommits applicationCommits = new ApplicationCommits();
applicationCommits.id = new ApplicationCommitsId(application.getId(), decisionCommits.getId());
applicationCommits.application = application;
applicationCommits.decisionCommits = decisionCommits;
applicationCommits.reason = reason;
applicationCommits.confidence = confidence;
return applicationCommits;
}
}
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 ApplicationCommitsId implements Serializable {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,10 @@ public class DecisionCommits extends BaseEntity {
@Column(name = "commit_id", nullable = false)
private Long commitId; // 매핑 X,

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

public static DecisionCommits create(Decision decision, Long commitId, String reason) {
public static DecisionCommits create(Decision decision, Long commitId) {
DecisionCommits decisionCommits = new DecisionCommits();
decisionCommits.decision = decision;
decisionCommits.commitId = commitId;
decisionCommits.reason = reason;
return decisionCommits;
}

// 저장시
public void updateReason(String reason) {
this.reason = reason;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ public interface ApplicationCommitsRepository extends JpaRepository<ApplicationC
""")
List<ApplicationCommits> findByApplicationId(@Param("applicationId") Long applicationId);

@Query("""
SELECT AVG(ac.confidence)
FROM ApplicationCommits ac
WHERE ac.application.decision.id = :decisionId
AND ac.confidence IS NOT NULL
""")
Double findAverageConfidenceByDecisionId(@Param("decisionId") Long decisionId);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
DELETE FROM ApplicationCommits ac
WHERE ac.application.decision.id = :decisionId
""")
void deleteByDecisionId(@Param("decisionId") Long decisionId);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
DELETE FROM ApplicationCommits ac
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.whylog.server.domain.decision.repository;

import com.whylog.server.domain.decision.entity.Application;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface ApplicationRepository extends JpaRepository<Application, Long> {

List<Application> findByDecisionId(Long decisionId);

@Modifying
@Query("DELETE FROM Application a WHERE a.decision.meeting.team.id = :teamId")
void deleteByTeamId(@Param("teamId") Long teamId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ public interface DecisionCommitsRepository extends JpaRepository<DecisionCommits
// 추천 결과 저장시 중복 방지용
Optional<DecisionCommits> findByDecisionIdAndCommitId(Long decisionId, Long commitId);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
DELETE FROM DecisionCommits dc
WHERE dc.decision.id = :decisionId
""")
void deleteByDecisionId(@Param("decisionId") Long decisionId);

@Query("""
SELECT dc.id
FROM DecisionCommits dc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ public interface DecisionRepository extends JpaRepository<Decision, Long> {

Optional<Decision> findByMeetingId(Long meetingId);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
UPDATE Decision d
SET d.reliabilityScore = :reliabilityScore
WHERE d.id = :decisionId
""")
void updateReliabilityScore(@Param("decisionId") Long decisionId,
@Param("reliabilityScore") Integer reliabilityScore);

@Modifying
@Query("DELETE FROM Application a WHERE a.decision.meeting.team.id = :teamId")
void deleteApplicationsByTeamId(@Param("teamId") Long teamId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ private ApplicationResponse.RecommendedCommitDTO toRecommendedCommitDTO(Applicat
.commitId(String.valueOf(commit.getId()))
.commitHash(commit.getHash())
.message(commit.getMessage())
.reason(applicationCommit.getDecisionCommits().getReason())
.reason(applicationCommit.getReason())
.confidence(applicationCommit.getConfidence())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.whylog.server.domain.decision.service;

final class DecisionCommitMatchCandidate {

private final Long applicationId;
private final String applicationTitle;
private final Long commitId;
private final Long repositoryId;
private final String commitHash;
private final String reason;
private final Integer confidence;
private Long resolvedApplicationId;
private Long resolvedCommitId;

DecisionCommitMatchCandidate(Long applicationId,
String applicationTitle,
Long commitId,
Long repositoryId,
String commitHash,
String reason,
Integer confidence) {
this.applicationId = applicationId;
this.applicationTitle = applicationTitle;
this.commitId = commitId;
this.repositoryId = repositoryId;
this.commitHash = commitHash;
this.reason = reason;
this.confidence = confidence;
}

Long applicationId() {
return applicationId;
}

String applicationTitle() {
return applicationTitle;
}

Long commitId() {
return commitId;
}

Long repositoryId() {
return repositoryId;
}

String commitHash() {
return commitHash;
}

String reason() {
return reason;
}

Integer confidence() {
return confidence;
}

void resolveApplicationId(Long resolvedApplicationId) {
this.resolvedApplicationId = resolvedApplicationId;
}

Long resolvedApplicationId() {
return resolvedApplicationId;
}

void resolveCommitId(Long resolvedCommitId) {
this.resolvedCommitId = resolvedCommitId;
}

Long resolvedCommitId() {
return resolvedCommitId;
}

String commitUniqueKey() {
if (commitId != null) {
return "id:" + commitId;
}
return "repo:" + repositoryId + ":hash:" + commitHash;
}

String applicationUniqueKey() {
if (applicationId != null) {
return "id:" + applicationId;
}
return "title:" + applicationTitle;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.whylog.server.domain.decision.service;

//중복제거용
record DecisionCommitMatchKey(String applicationKey, String commitKey) {
}
Loading
Loading