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
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ project(":layer-admin") {

dependencies {
implementation project(path: ':layer-event')
implementation project(path: ':layer-domain')

compileOnly 'org.springframework.boot:spring-boot-starter-data-jpa'
compileOnly 'org.springframework.boot:spring-boot-starter-web'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
import org.layer.admin.retrospect.repository.dto.RetrospectAnswerCompletionDto;
import org.layer.admin.retrospect.repository.dto.SpaceRetrospectCountDto;
import org.layer.admin.space.repository.AdminSpaceRepository;
import org.layer.domain.retrospect.entity.Retrospect;
import org.layer.domain.retrospect.entity.RetrospectStatus;
import org.layer.domain.retrospect.repository.RetrospectRepository;
import org.layer.domain.space.entity.MemberSpaceRelation;
import org.layer.domain.space.entity.Team;
import org.layer.domain.space.repository.MemberSpaceRelationRepository;
import org.layer.event.retrospect.ClickRetrospectEvent;
import org.layer.event.retrospect.CreateRetrospectEvent;
import org.layer.event.retrospect.AnswerRetrospectEndEvent;
Expand All @@ -58,6 +64,8 @@ public class AdminRetrospectService {
private final AdminRetrospectClickRepository adminRetrospectClickRepository;
private final AdminMemberRepository adminMemberRepository;
private final AdminSpaceRepository adminSpaceRepository;
private final RetrospectRepository retrospectRepository;
private final MemberSpaceRelationRepository memberSpaceRelationRepository;

public MeaningfulRetrospectMemberResponse getAllMeaningfulRetrospect(
LocalDateTime startTime, LocalDateTime endTime, int retrospectLength, int retrospectCount) {
Expand Down Expand Up @@ -203,13 +211,60 @@ public RetrospectCompletionRateResponse getRetrospectCompletionRate(LocalDateTim
List<RetrospectAnswerCompletionDto> answerHistories = adminRetrospectAnswerRepository.findRetrospectAnswerCompletionStatsBetween(
startTime, endTime);

// 회고별 완수율 계산 (단위: %)
if (answerHistories.isEmpty()) {
return new RetrospectCompletionRateResponse(0.0);
}

// 필요한 회고/스페이스/팀 정보를 미리 한 번에 로딩해서 N+1 방지
List<Long> retrospectIds = answerHistories.stream()
.map(RetrospectAnswerCompletionDto::retrospectId)
.distinct()
.toList();

List<Retrospect> retrospects = retrospectRepository.findAllById(retrospectIds);
Map<Long, Retrospect> retrospectMap = retrospects.stream()
.collect(Collectors.toMap(Retrospect::getId, r -> r));

List<Long> spaceIds = retrospects.stream()
.map(Retrospect::getSpaceId)
.distinct()
.toList();

List<MemberSpaceRelation> allRelations = memberSpaceRelationRepository.findAllBySpaceIdIn(spaceIds);
Map<Long, Team> teamBySpaceId = allRelations.stream()
.collect(Collectors.groupingBy(
relation -> relation.getSpace().getId(),
Collectors.collectingAndThen(Collectors.toList(), Team::new)
));

// 회고별 분모를 도메인 로직(Team, RetrospectStatus, deadline) 기반으로 계산
List<Double> completionRates = answerHistories.stream()
.filter(dto -> dto.targetAnswerCount() > 0) // division by zero 방지
.map(dto -> (double) dto.actualAnswerCount() / dto.targetAnswerCount() * 100.0)
.map(dto -> {
Retrospect retrospect = retrospectMap.get(dto.retrospectId());
if (retrospect == null) {
return null;
}

Team team = teamBySpaceId.get(retrospect.getSpaceId());
if (team == null) {
return null;
}

long totalCount = team.getTeamMemberCount();
if (retrospect.getRetrospectStatus().equals(RetrospectStatus.DONE)) {
// 회고가 종료된 경우, deadline 시점의 팀원 수를 분모로 사용
totalCount = team.getTeamMemberCountBefore(retrospect.getDeadline());
}

if (totalCount == 0) {
return null; // division by zero 방지
}

return (double) dto.actualAnswerCount() / totalCount * 100.0;
})
.filter(Objects::nonNull)
.toList();

// 평균 완수율 계산
double averageCompletionRate = completionRates.isEmpty()
? 0.0
: completionRates.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,16 @@ public RetrospectListGetResponse getRetrospects(Long spaceId, Long memberId) {

List<RetrospectGetResponse> retrospectDtos = retrospects.stream()
.map(r -> {
long writeCount = team.getTeamMemberCount();
long totalCount = team.getTeamMemberCount();
if (r.getRetrospectStatus().equals(RetrospectStatus.DONE)) {
writeCount = answers.getWriteCount(r.getId());
// 회고가 종료된 경우, 해당 회고의 deadline 시점의 팀원 수를 totalCount로 설정한다.
// RetrospectStatus 가 DONE 으로 변경되면, deadline이 null 값이 될 수 없기 때문이다.
totalCount = team.getTeamMemberCountBefore(r.getDeadline());
}

return RetrospectGetResponse.of(r.getSpaceId(), r.getId(), r.getTitle(), r.getIntroduction(),
answers.getWriteStatus(memberId, r.getId()), r.getRetrospectStatus(), r.getAnalysisStatus(),
answers.getWriteCount(r.getId()), writeCount, r.getCreatedAt(), r.getDeadline());
answers.getWriteCount(r.getId()), totalCount, r.getCreatedAt(), r.getDeadline());
})
.toList();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.layer.global.exception.MemberSpaceRelationExceptionType.*;

import java.time.LocalDateTime;
import java.util.List;

import org.layer.domain.space.exception.MemberSpaceRelationException;
Expand Down Expand Up @@ -30,4 +31,10 @@ public List<Long> getMemberIds(){
.map(MemberSpaceRelation::getMemberId)
.toList();
}

public long getTeamMemberCountBefore(LocalDateTime end) {
return memberSpaceRelations.stream()
.filter(memberSpaceRelation -> memberSpaceRelation.getCreatedAt().isBefore(end))
.count();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public interface MemberSpaceRelationRepository extends JpaRepository<MemberSpace

List<MemberSpaceRelation> findAllBySpaceId(Long spaceId);

@Query("SELECT m FROM MemberSpaceRelation m WHERE m.space.id IN :spaceIds")
List<MemberSpaceRelation> findAllBySpaceIdIn(@Param("spaceIds") List<Long> spaceIds);

@Query("SELECT new org.layer.domain.retrospect.dto.SpaceMemberCount(m.space.id, COUNT(m)) " +
"FROM MemberSpaceRelation m " +
"WHERE m.space.id IN :spaceIds " +
Expand Down
Loading