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
2 changes: 1 addition & 1 deletion .github/workflows/aws-cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
echo "${APPLICATION_SECRET_PROPERTIES}" > ./layer-api/src/main/resources/application-secret.properties

- name: Build layer-api module
run: ./gradlew build
run: ./gradlew build -x test

- name: Upload Test Report # 실패시 원인 파악을 하기 위한 단계
uses: actions/upload-artifact@v4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.layer.admin.retrospect.controller.dto.RetrospectCompletionRateResponse;
import org.layer.admin.retrospect.controller.dto.RetrospectRetentionResponse;
import org.layer.admin.retrospect.controller.dto.RetrospectStayTimeResponse;
import org.layer.admin.retrospect.entity.AdminRetrospect;
import org.layer.admin.retrospect.entity.AdminRetrospectAnswerHistory;
import org.layer.admin.retrospect.entity.AdminRetrospectHistory;
import org.layer.admin.retrospect.entity.AdminRetrospectClick;
Expand All @@ -32,10 +33,13 @@
import org.layer.admin.retrospect.repository.AdminRetrospectClickRepository;
import org.layer.admin.retrospect.repository.AdminRetrospectImpressionRepository;
import org.layer.admin.retrospect.repository.AdminRetrospectHistoryRepository;
import org.layer.admin.retrospect.repository.AdminRetrospectRepository;
import org.layer.admin.retrospect.repository.dto.ProceedingRetrospectClickDto;
import org.layer.admin.retrospect.repository.dto.ProceedingRetrospectImpressionDto;
import org.layer.admin.retrospect.repository.dto.RetrospectAnswerCompletionDto;
import org.layer.admin.retrospect.repository.dto.SpaceRetrospectCountDto;
import org.layer.admin.space.entity.AdminMemberSpaceRelation;
import org.layer.admin.space.repository.AdminMemberSpaceRelationRepository;
import org.layer.admin.space.repository.AdminSpaceRepository;
import org.layer.event.retrospect.ClickRetrospectEvent;
import org.layer.event.retrospect.CreateRetrospectEvent;
Expand All @@ -58,6 +62,8 @@ public class AdminRetrospectService {
private final AdminRetrospectClickRepository adminRetrospectClickRepository;
private final AdminMemberRepository adminMemberRepository;
private final AdminSpaceRepository adminSpaceRepository;
private final AdminRetrospectRepository retrospectRepository;
private final AdminMemberSpaceRelationRepository memberSpaceRelationRepository;

public MeaningfulRetrospectMemberResponse getAllMeaningfulRetrospect(
LocalDateTime startTime, LocalDateTime endTime, int retrospectLength, int retrospectCount) {
Expand Down Expand Up @@ -203,13 +209,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<AdminRetrospect> retrospects = retrospectRepository.findAllById(retrospectIds);
Map<Long, AdminRetrospect> retrospectMap = retrospects.stream()
.collect(Collectors.toMap(AdminRetrospect::getId, r -> r));

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

List<AdminMemberSpaceRelation> allRelations = memberSpaceRelationRepository.findAllBySpaceIdIn(spaceIds);
Map<Long, List<AdminMemberSpaceRelation>> relationsBySpaceId =
allRelations.stream()
.collect(Collectors.groupingBy(
AdminMemberSpaceRelation::getSpaceId
));

// 회고별 분모를 도메인 로직(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 -> {
AdminRetrospect retrospect = retrospectMap.get(dto.retrospectId());
if (retrospect == null) {
return null;
}

List<AdminMemberSpaceRelation> relationList = relationsBySpaceId.get(retrospect.getSpaceId());
if (relationList == null) {
return null;
}

long totalCount = relationList.size();
if (retrospect.getRetrospectStatus().equals(AdminRetrospectStatus.DONE)) {
// 회고가 종료된 경우, deadline 시점의 팀원 수를 분모로 사용
totalCount = getTeamMemberCountBefore(relationList, 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 All @@ -220,6 +273,12 @@ public RetrospectCompletionRateResponse getRetrospectCompletionRate(LocalDateTim
return new RetrospectCompletionRateResponse(averageCompletionRate);
}

private long getTeamMemberCountBefore(List<AdminMemberSpaceRelation> relationList, LocalDateTime end) {
return relationList.stream()
.filter(memberSpaceRelation -> memberSpaceRelation.getCreatedAt().isBefore(end))
.count();
}

public ProceedingRetrospectCTRAverageResponse getProceedingRetrospectCTR(LocalDateTime startDate, LocalDateTime endDate) {
List<ProceedingRetrospectImpressionDto> impressions = adminRetrospectImpressionRepository.findProceedingRetrospectImpressionGroupByMember(
startDate, endDate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ List<ProceedingSpaceDto> findProceedingSpacesWithMemberCount(
@Param("startDate") LocalDateTime startDate,
@Param("endDate") LocalDateTime endDate
);

@Query("SELECT m FROM AdminMemberSpaceRelation m WHERE m.spaceId IN :spaceIds")
List<AdminMemberSpaceRelation> findAllBySpaceIdIn(@Param("spaceIds") List<Long> spaceIds);
}
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();
}
}
Loading